Alexl-git/Delphi-RAG-Lint

GitHub: Alexl-git/Delphi-RAG-Lint

一款纯 Delphi 运行时的本地 RAG 与 lint 工具,为 Delphi/Pascal 提供符号级检索、语义分析、依赖重构及多 IDE 集成能力。

Stars: 15 | Forks: 1

# drag-lint [![发布](https://img.shields.io/github/v/release/Alexl-git/Delphi-RAG-Lint?include_prereleases)](https://github.com/Alexlgit/Delphi-RAG-Lint/releases) [![许可证](https://img.shields.io/github/license/Alexl-git/Delphi-RAG-Lint)](LICENSE) 一款具备符号感知能力的 Delphi 检索 + lint + 重构 + IDE 集成工具。 运行时采用纯 Object Pascal —— 无需 Python、Node 或 Rust。不依赖云端 AI。 **使用方式:** CLI 工具 · LSP server (Zed / VS Code) · MCP server (Claude / Cursor) · RAD Studio 13 插件。 基于 [`tree-sitter-delphi13`](https://github.com/Alexl-git/tree-sitter-delphi13) (兄弟项目)以及内置的 libtree-sitter Pascal 绑定构建。 **配套项目:** [`Delphi-RAG-Lint-Graph`](https://github.com/Alexl-git/Delphi-RAG-Lint-Graph) — 一款独立的 VCL 查看器 (Win64),可将此索引转化为交互式符号 图表:UML 类图、支持渲染 DocInsight 注释的 **Code Flow View**、 **Where-Used** 调用者列表、带有前进/后退历史记录的搜索功能,以及 **编辑器同步** (图表会跟随 RAD Studio中的活动单元)。支持点击跳转回 IDE。 ## 截图 ### IDE 内的进程外编译器智能分析 实时的诊断信息来自于在**派生**进程中编译您的代码缓冲区 —— 甚至是 未保存的代码 —— 因此 IDE 绝不会卡死。Structure 面板和可停靠的代码 图表就位于编辑器侧边。 ![在 RAD Studio 中带有停靠代码图表的 drag-lint 实时诊断](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/IDE_Out_of_process_compilation.png) ### 在 Help Insight 中展示您的 DocInsight `///` 注释 `` 和 `` 将原生地渲染在 IDE 的 Help Insight 提示框中。 ![在 IDE Help Insight 提示框中显示的 DocInsight 文档注释](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/IDE_DOCInsight.png) ### 代码流程视图 以流程图的形式追踪例程的调用 —— 每个节点都包含其 DocInsight 摘要 (此处为 `TCompileChecker.Run`)。 ![带有每个节点 DocInsight 摘要的 TCompileChecker.Run Code Flow View](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/Graph_Calls_out.png) ### UML 类视图,支持悬停查看文档 搜索一个类型即可查看其成员(可见性图标 + 完整签名);将鼠标悬停在 成员上即可查看其 DocInsight 文档。 ![带有成员文档提示框的 TCompileChecker UML 类图](https://static.pigsec.cn/wp-content/uploads/repos/cas/f1/f18d8f766d7882ffa42fe9f99012f1ca64aeb59d293a7b001eb2a8b39de18d59.png) ### 使用位置 在单元的调用图旁边,提供符号调用者的精确、可点击列表 —— 此处为 `ResolveActiveIndexDbs` 的 7 个调用者。 ![图表查看器中 ResolveActiveIndexDbs 的 Where-Used 调用者列表](https://static.pigsec.cn/wp-content/uploads/repos/cas/00/00210f674eefc1764bc8715f403f91da42489b88b27dd9edf094ccf8a1580bf2.png) ### AST 精确符号查询 (CLI) `drag-lint query --name TCompileChecker --json` 返回每一个匹配项的类别、 限定名、节区、文件以及精确的行号/实现范围 —— 没有任何注释或 字符串字面量的干扰。 ![TCompileChecker 的 drag-lint query --json 输出](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/DRAG-Lint.exe_query_example1.png) ### 查找调用者及其源码上下文 (CLI) `drag-lint query find-callers` 列出每一个调用者(此处为 7 个)及其周围的 源代码行。 ![带有代码上下文的 drag-lint find-callers 输出](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/DRAG-Lint.exe_query_example2_Find_Callers.png) ### 从 CLI 进行语义编译检查 `drag-lint check-unit` 在项目上下文中编译某个单元,并报告 检查结果(此处为:无异常)。 ![drag-lint check-unit 无异常结果](https://raw.githubusercontent.com/Alexl-git/Delphi-RAG-Lint/main/docs/Images/DRAG-Lint.exe_query_example2.png) ## 快速开始 ### 独立 CLI 1. 下载[最新发布版本](https://github.com/Alexl-git/Delphi-RAG-Lint/releases) (`drag-lint.exe` + `tree-sitter*.dll`)。 2. 将它们放在同一个目录中。 3. 索引一个 Delphi 项目: drag-lint index C:\Projects\MyApp --db myapp.sqlite 4. 查询符号: drag-lint query --name TFoo --db myapp.sqlite drag-lint surface --qname Unit.TFoo --db myapp.sqlite drag-lint impact --qname Unit.TFoo.DoBar --db myapp.sqlite ### 索引 Delphi RTL/VCL 库 要为 Delphi 自身所知晓的所有内容(直接从注册表中读取 IDE 的 **Library** 和 **Browsing** 搜索路径,进行去重,并展开 `$(BDS)` / `$(Platform)` 宏)建立一个统一的索引,请使用 `--scan-libraries-*` 参数(无需指定项目或路径): ``` drag-lint index --scan-libraries-win --db Library.sqlite # Win32 + Win64 (default) drag-lint index --scan-libraries-all --db Library.sqlite # every registered platform ``` - **`--scan-libraries-win`** 涵盖 IDE 的原生目标平台(Win32 + Win64)。 由于 RTL / VCL / FMX 的 `.pas` 源码是跨平台共享的,这实际上已经捕获了 基本上所有的库源码。(保留 `--scan-libraries` 作为此选项的向后兼容别名。) - **`--scan-libraries-all`** 会枚举 `...\BDS\37.0\Library` 下的**每一个**平台子键 (Android*、iOS*、Linux64、OSX*、Win64x 等)。在 Win 集合的基础上,它还会引入 特定平台的源码树 —— `source\rtl\posix`、 `source\rtl\ios`、`posix\osx` —— 因此诸如 `Posix.*`、`iOSapi.*`、 `Macapi.*` 和 `Androidapi.*` 的符号也能被正确解析。 这两者都会探测 32 位和 64 位注册表视图下的 HKCU + HKLM,并将 结果合并为一个去重后的文件夹集合。添加 `--dry-run` 可在不进行索引的情况下打印出 解析后的文件夹列表。 ### 梳理单元依赖关系(循环 + uses 清理) 查找循环单元依赖关系以及构成这些循环的确切 `uses` 代码行: ``` drag-lint cycles --db myapp.sqlite --edges ``` 每个循环都会列出其 `A uses B [interface|implementation]` 边,将 **interface** 边标记为可移至 implementation 的候选对象,并标记分层 倒置(例如,COMMON 单元反向依赖 CLIENT 单元)。添加 **`--causes`** 可 精确定位 `A` 的 interface 中迫使依赖 `B` 的*具体符号*(即需要移动或提取的类型/变量/方法) —— 并附带行号,以及 在索引无法解析引用时的如实说明。 或者,生成一份完整的**可跟进的重构指南**,初级开发者(或 小模型)即可执行: ``` drag-lint cycles --db myapp.sqlite --plan > cycle-plan.md ``` 针对每个循环,它会提供涉及的文件、关键承载符号(使用位置**以及**声明位置,附带行号)、自动分类的修复方案(对于分层倒置,提供*提取共享 契约*或*反转依赖*)、带编号的操作步骤, 以及验证命令。在完成每个循环后进行构建,并重新运行 `cycles` 进行确认。然后提出并应用 清理方案,该过程**由编译器验证**,因此绝不会破坏构建: ``` drag-lint uses-audit MyUnit.pas --db myapp.sqlite # propose drag-lint uses-fix --project MyApp.dproj --db myapp.sqlite # dry-run sweep report drag-lint uses-fix MyUnit.pas --project MyApp.dproj --db myapp.sqlite --apply # apply (.bak backup) ``` ### 无需完整构建的语义错误检查 `check-unit` 会在其项目上下文中编译单个单元,因此您可以快速获得真实的 编译器错误(例如 `E2003 Undeclared identifier`) —— 并且可以通过 shadow overlay 在 **未保存**的缓冲区上进行: ``` drag-lint check-unit MyUnit.pas --project MyApp.dproj --platform win64 \ --db myapp.sqlite --resolve-uses ``` `--resolve-uses` 会将未声明的标识符转化为修复方案:“*将单元 X 添加到 uses 子句中。*” ### LSP server (Zed / VS Code) 将您编辑器的 LSP 配置指向 `drag-lint.exe lsp --db .sqlite`。 该 server 通过 stdio 进行 JSON-RPC 通信。 支持的功能:hover、definition、references、completion、signatureHelp、 diagnostics(在 didSave 时触发 publishDiagnostics)、workspaceSymbols。 ### MCP server (Claude / Cursor) 添加到您的 MCP 配置中(例如 `~/.claude/claude_desktop_config.json`): ``` { "drag-lint": { "command": "drag-lint.exe", "args": ["serve", "--db", "C:\\Projects\\MyApp\\.drag-lint.sqlite"] } } ``` 随后 Claude 即可使用 14+ 种工具:`find_symbol`、`find_callers`、 `get_symbol_doc`、`get_context_bundle`、`rename_symbol`、`run_compile_check` 等等(详见下方的 [MCP 工具](#mcp-tools-14))。 ### RAD Studio 13 插件 1. 构建 BPL: msbuild src/delphi-plugin/dclDragLintWizard.dproj /p:Platform=Win64 /p:Config=Debug 或从最新的 GitHub 发布版本中下载 `dclDragLintWizard.bpl`。 2. 在 RAD Studio 中:**Component > Install Packages > Add** —— 浏览并选中该 BPL。 3. 重启 RAD Studio。 4. **Tools > drag-lint** 菜单现在包含 12+ 个条目。 ## 功能 ### CLI(约 25 条命令) | 命令 | 描述 | |---------|-------------| | `index ` | 解析 Delphi 项目并将其索引至 SQLite | | `index --scan-libraries-win` | 索引 IDE 的 Win32+Win64 Library + Browsing 路径(读取自注册表) | | `index --scan-libraries-all` | 同上,但涵盖所有已注册的平台(添加 Posix/iOS/Android/OSX 源码) | | `query --name ` | 按名称查找符号(支持模糊匹配) | | `query --text ""` | 搜索已索引的字符串内容 —— 消息、标题、异常文本(非标识符)。参数:`--any-order`、`--substring`、`--source pas\|dfm\|sql`、`--limit N`、`--json`。默认情况下,仅从 `MS*.sql` 文件中索引 SQL `CREATE EXCEPTION` 消息(使用 `--no-sql-ms` 可索引所有 `.sql` 文件)。 | | `surface --qname ` | 显示符号的完整源码表层结构 | | `slice --qname ` | 提取从某个符号可达的调用切片 | | `impact --qname ` | 显示更改某个符号后可能受到影响的所有内容 | | `wiring --qname ` | Spring4D DI 连线边(实现类 + 生命周期 + 解析位置)及 DFM 事件处理器(`--coverage` 列出未解析的 DI 注册) | | `hover --file --line --col ` | 提供源码指定位置处的悬停信息 | | `rename --qname --new-name ` | 预览或应用符号重命名 | | `generate-docs --qname ` | 生成 XML 文档注释存根 | | `generate-test --qname ` | 生成测试方法存根 | | `find-deadcode` | 列出自身单元之外没有调用者的符号 | | `compile-check ` | 运行 msbuild 并将诊断信息存储在 DB 中 | | `check-unit ` | 在项目上下文中编译单个单元(检查语义错误;`--shadow` 用于未保存的缓冲区,`--resolve-uses` 用于建议缺失的单元) | | `cycles` | 检查循环单元依赖(`--edges` 显示边以及移动/分层候选对象) | | `uses-audit ` | 提议将 interface 移至 implementation,并找出未使用的单元 | | `uses-fix --project ` | 经过编译器验证的 uses 清理(移动/移除;默认为 dry-run,使用 `--apply` 执行写入) | | `find-unit --name --in ` | 将声明了 `X` 的单元添加到 `` 的 `uses` 子句中(使用 `--apply` 进行写入) | | `import-log ` | 将已保存的 msbuild 日志导入 DB | | `format ` | 使用 YADF 格式化程序格式化 .pas 文件 | | `check-ast ` | 在不编译的情况下运行 tree-sitter lint 规则 | | `lint ` | 运行所有内置及外部 .scm 规则 | | `query find-callers --name ` | 列出符号的每一个调用点(包含源码上下文) | | `workspace index` | 索引 workspace 配置中的所有项目 | | `workspace status` | 显示每个项目的文件数量 | | `workspace add ` | 将一个项目添加到 workspace 配置中 | | `context --task "verb qname"` | 为 AI prompt 输出紧凑的上下文捆绑包(例如 `--task "modify Unit.TFoo.Bar"`) 包含文档 + 表层结构 + 目标主体,比源码精简约 10-60 倍 | | `check-unit ... --shadow` | 编译**未保存**的缓冲区(overlay)并报告错误 —— IDE ghost-compile 的 CLI 端实现 | | `ghost-check --overlays ` | 在叠加一个或多个单元的未保存内容(多单元)的情况下编译项目,并逐字节还原所有文件;支持 IDE 的实时 ghost-compile | | `ghost-recover` | 恢复任何因 ghost-check 中途崩溃而遗留的叠加文件(`_D-RAG` journal) | | `bench-context ` | 对上下文捆绑包的吞吐量进行基准测试 | | `forms-csv --project --db ` | 测试辅助 CSV:每个窗体一行,包含从主窗体到按钮/菜单的路径(`Navigation`)、打开该窗体的窗体(`Called From`)、单元 + 行数(`--out `、`--root `) | | `lsp [--db ]` | 启动 LSP server (stdio) | | `serve [--db ]` | 启动 MCP server (stdio) | | `--version` | 打印版本号 | | `--help` | 打印帮助信息 | ### MCP 工具(14+) | 工具 | 描述 | |------|-------------| | `find_symbol` | 按名称搜索索引 | | `find_callers` | 列出某个符号的所有调用点 | | `get_symbol_doc` | 获取符号的文档注释 | | `get_context_bundle` | 为 AI 消费者提供紧凑的上下文捆绑包 | | `rename_symbol` | 预览或应用符号重命名 | | `run_compile_check` | 触发 msbuild 并返回诊断信息 | | `import_log` | 导入已保存的构建日志 | | `run_ast_checks` | 对文件运行 AST lint 规则 | | `format_file` | 使用 YADF 格式化源文件 | | `get_surface` | 符号的完整源码表层结构 | | `get_impact` | 符号的调用影响集合 | | `get_wiring` | 接口或窗体的 Spring4D DI 边 + DFM 事件处理器 | | `get_slice` | 可达的调用切片 | | `workspace_status` | workspace 项目/文件摘要 | | `workspace_index` | 重新索引所有 workspace 项目 | ### Lint 规则包(130+ 条规则) 运行 `drag-lint rules` 以获取权威且实时更新的目录(内置 + 外部 `.scm`,130+ 且持续增加)。下表是内置规则的 一个小示例: | 规则 ID | 严重程度 | 描述 | |---------|----------|-------------| | `writeln-in-source` | info | 直接使用 `WriteLn` —— 应使用 logger | | `goto-statement` | warning | `goto` 被认为是有害的 | | `with-statement` | info | `with` 会导致作用域模糊 | | `nested-with` | warning | 嵌套的 `with` —— 会加剧作用域的模糊性 | | `empty-procedure-body` | info | 空的 `begin..end` 块 | | `large-magic-number` | info | 未命名的数字字面量 | | `case-magic-numbers` | info | 将整数字面量用作 `case` 标签 | | `string-equality-comparison` | info | 对字符串表达式使用 `=` 比较 | | `parser-error` | error | Tree-sitter `ERROR` 节点(格式错误的语法) | | `compiler-magic-comments` | info | 注释中的 TODO/FIXME/HACK/XXX | | `assert-call` | info | `Assert()` —— 需确保有具有描述性的第二个参数 | | `boolean-comparison-true` | info | `X = True` 或 `X = False` —— 冗余 | | `redundant-as-tobject` | info | `(X as TObject)` —— 每一个对象本身就是 TObject | | `inherited-bare` | info | 裸露的 `inherited;` —— 需验证是否调用了正确的祖先 | 将自定义的 `.scm` + `.json` 文件对放入 `rules/` 目录中;有关 schema 请参见 [rules/README.md](rules/README.md)。 ### RAD Studio 插件 **`drag-lint` 菜单**(位于主菜单栏的顶层 —— 回退到 Tools 菜单下 —— 包含约 30 个项目,并被组织成多个子菜单):光标处悬停、显示补全、 显示签名帮助、运行诊断、重命名符号、编译并诊断、导入构建日志、 使用 YADF 格式化、显示结构、运行 AST 检查、查找用法、符号搜索、可停靠的 面板(Structure / Usages / Graph)、生成测试辅助 CSV...、 **Uses & 依赖关系**子菜单(cycles、uses-audit、uses-fix、reconcile、wiring、impact)、 **检查符号**子菜单(surface、slice、type-at-cursor)、 **代码质量**子菜单(死代码、未记录的代码、TODO、编译器提示、热门符号)、 **生成与导出**子菜单(文档、测试、枚举、图表、Obsidian)、 **索引与维护**子菜单,以及诊断和测试工具。设置。 **快捷键绑定**(通过 `IOTAKeyBindingServices` 注册): | 快捷键 | 操作 | |----------|--------| | Ctrl+Alt+H | 光标处悬停 | | Ctrl+Alt+C | 显示补全 | | Ctrl+Alt+S | 显示签名帮助 | | Ctrl+Alt+D | 运行诊断 | | Ctrl+Alt+I | 编辑器内诊断提示弹窗 | | Ctrl+Alt+R | 重命名符号 | | Ctrl+Alt+F | 查找用法 | | Ctrl+Alt+T | 符号搜索 | **编辑器内诊断**:通过 `IOTAEditViewNotifier.BeforeDrawLine` 显示装订线点标记 + 波浪下划线。严重程度的颜色取自 IDE 配色方案注册表。 **Ghost-compile(实时,进程外)**:在您输入时,drag-lint 会在*派生*进程中编译您 **未保存**的缓冲区,并在装订线中显示真实的编译器错误 (例如 `E2003 Undeclared identifier`) —— 无需保存,且 绝不会冻结 IDE。在空闲和切换选项卡时自动触发,支持 多单元 overlay,因此可以捕捉到多个打开单元中的编辑内容。文件会 逐字节还原(通过恢复日志实现崩溃安全)。 **悬停提示框**(v0.35):一个 200 毫秒的定时器,当光标在 带有诊断信息的行上稳定停留 600 毫秒后,会显示带有 诊断信息的 `Application.HintWindow`。基于插入符号位置触发(非像素级精确)。可通过设置进行开关。 **Code lens**(v0.32):方法声明旁边带有暗灰色的 `[N callers]` 文本。 **Structure 窗体**(v0.30):浮动的 `fsStayOnTop` 窗体,显示 活动文件的符号树,并在视图激活时更新。 **查找用法窗体**(v0.33):`Ctrl+Alt+F` 提示输入符号名称;在 TTreeView 中按文件分组显示调用者;双击可跳转编辑器。 **符号搜索窗体**(v0.33):`Ctrl+Alt+T` 对已建立 索引的符号表进行去抖动实时搜索;按下 Enter 键可将编辑器导航至所选位置。 **原生 Tools > Options 页面**(v0.30):所有设置均通过 `INTAAddInOptions` 实现。 ## 架构 ``` drag-lint.exe | +-- CLI dispatch (DRagLint.CLI) | | | +-- Indexer (DRagLint.Core.Indexer) | | +-- tree-sitter-delphi13.dll (Delphi 13 grammar) | | +-- tree-sitter-dfm.dll (DFM grammar) | | +-- tree-sitter.dll (libtree-sitter runtime) | | +-- SQLite storage | | | +-- Query / Surface / Impact / Slice | +-- Lint (rule runner over .scm files) | +-- Refactor (rename, doc stubs, test stubs, YADF format) | +-- Compiler diagnostics (msbuild integration) | +-- Workspace (multi-project shared DB) | +-- LSP server (DRagLint.LSP.Server) -- stdio JSON-RPC | +-- MCP server (DRagLint.MCP.Server) -- stdio JSON-RPC | +-- CLI context bundler (DRagLint.Context.Bundler) dclDragLintWizard.bpl (Delphi IDE plugin) +-- Wizard / menu / keystrokes / EditViewNotifier +-- LSP client -> drag-lint.exe lsp +-- DiagnosticCache -> in-editor markers + hover tooltip +-- CodeLensCache -> inline [N callers] +-- Structure / Refactor / Usages / SymbolSearch forms +-- Options (INTAAddInOptions) ``` 所有三个入口点(CLI、LSP、MCP)都调用相同的索引器、查询、lint 和重构引擎。IDE 插件是 LSP 客户端的轻量级封装, 并针对 LSP 协议未涵盖的功能直接调用 CLI。 ## 从源码构建 前置条件: - RAD Studio 13 Florence (37.0) 及 Win64 目标平台 - `third_party/dll/` 目录下需包含 `tree-sitter-delphi13` DLL 构建 CLI: ``` call "C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat" msbuild drag-lint.dproj /p:Config=Release /p:Platform=Win64 ``` 构建 IDE 插件: ``` msbuild src/delphi-plugin/dclDragLintWizard.dproj /p:Config=Debug /p:Platform=Win64 ``` 运行测试套件(位于 `tests/fixtures/` 中的批处理文件): ``` tests\fixtures\T61_hovertracker.bat tests\fixtures\T62_lint_rules_v035.bat tests\fixtures\T56_lint_rules_v032.bat :: ... etc. ``` `tests/autotest/` 中的 PowerShell 冒烟测试脚本会对构建好的 exe 进行端到端的验证: ``` pwsh -File tests/autotest/run_smoke.ps1 # CLI + LSP server smoke pwsh -File tests/autotest/run_formsmap.ps1 # forms-csv navigation-map smoke (fixture project) ``` ## 版本历史 有关详细历史记录,请参阅 [CHANGELOG.md](CHANGELOG.md)(从 v0.16 到 v0.44-alpha,2026-05-28 → 2026-06-14)。开发工作每天都在继续(目前在 `feat/*` 分支上版本为 **v0.46-alpha**):Win64 图表查看器(UML / Code Flow / Where-Used / editor-sync)、进程外 ghost-compile 以及基于 manifest 的多 DB 解析。 ## 许可证 MIT。详见 [LICENSE](LICENSE)。