Files
2026Technology-Competition/docs/superpowers/specs/2026-07-10-implementation-plan.md
T
范智鹏 a734cdf009 refactor: 品牌重命名 + maxTokens 支持 + 设置面板简化
- CodeGuard → Code Purifier / 净码特工(displayName、命令、配置标题)
- 新增 ai.maxTokens 配置项,所有 Provider 及 fixer 传入 maxTokens
- AI 引擎增强:repairJsonEscapes + JSON 解析 fallback + 详细错误信息
- 设置面板规则管理改为文件级(list/delete .yaml),addRule 改为 AI 从 Markdown 生成 YAML
- yaml-parser 简化:移除 config.yaml 的 enable/disable 过滤逻辑
- 审查报告面板:errorBanner 优先显示具体错误、lint 诊断显示 suggestion、移除 translatedDiagnostics 独立渲染
- merger 中 translatedDiagnostics 覆盖原始 lint 诊断 message/suggestion
- HTML linter 配置项、测试用例重写、typescript-eslint 移入 dependencies
2026-07-20 20:24:40 +08:00

646 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# vscode-code-reviewer 实施拆分方案
## 决策汇总
| 决策项 | 选择 |
|--------|------|
| 拆分策略 | 按层级自底向上 |
| 适配器优先级 | 按复杂度递增:ESLint → Stylelint → sql-lint → PMD → JSP |
| 核心层B顺序 | 按依赖链:Provider → AI引擎 → 规则解析 → 结果合并 → 自动修复 → 报告导出 |
| UI层顺序 | 按依赖顺序:命令注册 → 设置面板 → 审查面板 → Code Action |
| 构建与测试 | 集中在最后 Phase |
## 整体 Phase 划分
```
Phase 1: 基础层 Types + Config 管理
Phase 2: 适配层 5 个 Linter 适配器
Phase 3: 核心层 A 编排器 Orchestrator
Phase 4: 核心层 B AI引擎 + 规则 + 合并 + 修复 + 报告
Phase 5: UI 层 命令 + 设置面板 + 审查面板
Phase 6: 构建与测试 esbuild + 测试
```
## 依赖关系图
```
Phase 1 ────→ Phase 2 ────→ Phase 3 ────→ Phase 4 ────→ Phase 5 ────→ Phase 6
↑ ↑
│ │
(2.1 接口) (4.1 Provider)
(2.2~2.6 适配器) (4.2~4.6 其余)
```
Phase 2 内部必须按 2.1 → 2.2 → 2.3 → 2.4 → 2.5 → 2.6 顺序,JSP 适配器依赖 PMD/ESLint/Stylelint。
Phase 4 内部必须按 4.1 → 4.2 → 4.3 → 4.4 → 4.5 → 4.6 顺序,Fixer 依赖 Provider 和 Merger。
---
## Phase 1: 基础层
**依赖**: 无
**目标**: 建立公共类型定义和配置管理,为所有上层模块提供基础能力
**参考设计**: §3.2, §6.4, §13.2
### 文件清单
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/types.ts` | 新建 | `LinterDiagnostic``AdapterResult``LinterAdapter` 等公共类型 |
| `src/config/index.ts` | 新建 | 统一导出 |
| `src/config/ai.ts` | 新建 | AI 配置 getterprovider/model/baseUrl/temperature/timeout/outputLanguage |
| `src/config/linter.ts` | 新建 | Linter 配置 getterlinters.xxx 语言-linter 映射、PMD 路径) |
| `src/config/fixer.ts` | 新建 | 修复器配置(contextLines |
| `src/config/secret.ts` | 新建 | API Key SecretStorageget/set/delete/isConfigured |
### 关键类型
```typescript
// src/types.ts
export type Severity = 'error' | 'warning' | 'info';
export type AdapterStatus = 'ok' | 'tool-unavailable' | 'execution-failed';
export interface LinterDiagnostic {
severity: Severity;
ruleId: string;
message: string;
range: vscode.Range;
suggestion?: string;
}
export interface AdapterResult {
diagnostics: LinterDiagnostic[];
status: AdapterStatus;
errorMessage?: string;
}
export interface LinterAdapter {
id: string;
supportedLanguages: string[];
check(document: vscode.TextDocument, workingDir: string): Promise<AdapterResult>;
isAvailable(): boolean;
}
```
### 配置 Key 映射
所有配置以 `vscode-code-reviewer.` 为前缀:
| 模块 | 配置项 | 类型 | 默认值 |
|------|--------|------|--------|
| ai | `ai.provider` | enum | `deepseek` |
| ai | `ai.model` | string | `deepseek-chat` |
| ai | `ai.baseUrl` | string | `https://api.deepseek.com/v1` |
| ai | `ai.temperature` | number | 0.2 |
| ai | `ai.timeout` | number | 300 |
| ai | `ai.outputLanguage` | string | `zh-CN` |
| linter | `linters.javascript` | enum | `eslint` |
| linter | `linters.typescript` | enum | `eslint` |
| linter | `linters.java` | enum | `pmd` |
| linter | `linters.jsp` | enum | `jsp` |
| linter | `linters.css` | enum | `stylelint` |
| linter | `linters.sql` | enum | `sql-lint` |
| linter | `linters.plsql` | enum | `sql-lint` |
| linter | `pmd.jarPath` | string | `""` |
| linter | `pmd.rulesetPath` | string | `""` |
| linter | `pmd.jspRulesetPath` | string | `""` |
| linter | `sql-lint.configFile` | string | `""` |
| fixer | `fixer.contextLines` | number | 5 |
| secret | API Key | SecretStorage | `vscode-code-reviewer.apiKey` |
---
## Phase 2: 适配层
**依赖**: Phase 1
**目标**: 实现 5 个语言适配器,统一 `LinterAdapter` 接口
**参考设计**: §3
### Phase 2.1: 适配器接口
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/adapters/adapter.ts` | 新建 | 重新导出 `LinterAdapter` 接口(或在此处定义,取决于代码组织) |
### Phase 2.2: ESLint 适配器
**参考设计**: §3.3
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/adapters/eslint.ts` | 新建 | `ESLintAdapter` implements `LinterAdapter` |
| | | `supportedLanguages`: `['javascript', 'typescript']` |
| | | `check()`: 使用 eslint npm 包 `lintText()` |
| | | `isAvailable()`: 检测 eslint 是否已安装 |
**npm 依赖**: `eslint` ^9.39.3
### Phase 2.3: Stylelint 适配器
**参考设计**: §3.3
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/adapters/stylelint.ts` | 新建 | `StylelintAdapter` implements `LinterAdapter` |
| | | `supportedLanguages`: `['css']` |
| | | `check()`: 使用 stylelint npm 包 `lint({ code })` |
| | | `isAvailable()`: 检测 stylelint 是否已安装 |
**npm 依赖**: `stylelint` ^17.14.0
### Phase 2.4: sql-lint 适配器
**参考设计**: §3.3
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/adapters/sql-lint.ts` | 新建 | `SqlLintAdapter` implements `LinterAdapter` |
| | | `supportedLanguages`: `['sql', 'plsql']` |
| | | `check()`: CLI 子进程调用 sqlfluff |
| | | `isAvailable()`: 检测 sqlfluff CLI 是否可用 |
| | | 方言映射:sql → ansi, plsql → postgres |
### Phase 2.5: PMD 适配器
**参考设计**: §3.3, §3.4
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/adapters/pmd.ts` | 新建 | `PmdAdapter` implements `LinterAdapter` |
| | | `supportedLanguages`: `['java']` |
| | | `check()`: Java 子进程调用 PmdRunner |
| | | 虚拟文档 (untitled) 通过 stdin 传入代码 |
| | | 真实文件传文件路径 |
| | | `isAvailable()`: 检测 Java 11+ 和 PMD JAR |
| `jars/pmd/PmdRunner.java` | 新建 | PMD 包装器:stdin 支持 + JSON 渲染器 |
| `jars/pmd/pmd-java-ruleset.xml` | 新建 | Java 规则集 |
| `jars/pmd/pmd-jsp-ruleset.xml` | 新建 | JSP 规则集 |
**PmdRunner.java 核心逻辑**:
```
参数: filePath (传 "-" 表示从 stdin 读取), ruleset
→ 构建 PMDConfiguration
→ 配置 JSON 渲染器
→ 若 filePath 为 "-",从 stdin 读代码 → 写入临时文件
→ 执行 PMD 分析
→ 输出 JSON 到 stdout
→ 清理临时文件
```
**PMD JAR 目录结构**:
```
jars/pmd/
├── lib/ # PMD 依赖 JAR(需下载)
├── PmdRunner.java # 包装器(编译为 .class)
├── pmd-java-ruleset.xml
└── pmd-jsp-ruleset.xml
```
### Phase 2.6: JSP 适配器
**依赖**: Phase 2.5, 2.2, 2.3(需要 PMD/ESLint/Stylelint 适配器)
**参考设计**: §3.5
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/jsp/jsp-extractor.ts` | 新建 | JSP 内嵌代码块提取器 |
| `src/adapters/jsp.ts` | 新建 | `JspAdapter` implements `LinterAdapter`(组合适配器) |
| | | `supportedLanguages`: `['jsp']` |
| | | `check()`: 三步流程 |
**JspAdapter.check() 流程**:
```
1. 调用 PmdAdapter.check(document) → JSP 规范检查
2. extractJspSections(document.getText()) → 提取内嵌代码块
3. 对每个 section:
a. 按 language 选择对应适配器
b. 创建虚拟文档 (vscode.workspace.openTextDocument)
c. 调用 adapter.check(virtualDoc)
d. 修正行号偏移 (section.lineOffset)
4. 合并所有结果
```
**提取器正则规则**:
| 代码块类型 | 正则匹配 | 目标适配器 |
|-----------|---------|-----------|
| `<script>` 标签 | `/<script\b[^>]*>([\s\S]*?)<\/script\s*>/gi` | ESLint |
| `<style>` 标签 | `/<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi` | Stylelint |
| `<% %>` scriptlet | `/<%=?([\s\S]*?)%>/g` | PMD |
---
## Phase 3: 核心层 A — 编排器
**依赖**: Phase 2
**参考设计**: §3.6, §4
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/orchestrator/orchestrator.ts` | 新建 | `Orchestrator` 类 |
**关键函数**:
- `getAdapters()` — 硬编码返回 5 个适配器实例
- `runStaticAnalysis(document, workingDir)` — 按语言选择适配器 → 调用 `check()` → 聚合
- 保存监听 + debounce 500ms
**调度逻辑**:
```
1. 获取当前文档语言 ID
2. 查询 linters.<language> 配置 → 确定使用的适配器
3. 调用适配器 check(document, workingDir)
4. 收集结果,区分 status
5. 返回聚合后的 diagnostics 列表
```
---
## Phase 4: 核心层 B
**依赖**: Phase 3
**参考设计**: §5, §8, §12, §14
### Phase 4.1: AI Provider 基础设施
**参考设计**: §5.2
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/ai/providers/base.ts` | 新建 | `AIProvider` 抽象基类 + `ChatOptions` 接口 |
| `src/ai/providers/deepseek.ts` | 新建 | `DeepSeekProvider extends AIProvider` |
| `src/ai/providers/openai.ts` | 新建 | `OpenAIProvider extends AIProvider` |
| `src/ai/factory.ts` | 新建 | `createProvider(providerId, apiKey, baseUrl)` 工厂函数 |
**Provider 接口**:
```typescript
interface ChatOptions {
model: string;
temperature: number;
timeoutMs: number;
}
abstract class AIProvider {
abstract id: string;
abstract name: string;
constructor(protected apiKey: string, protected baseUrl: string) {}
abstract chat(systemPrompt: string, userPrompt: string, options: ChatOptions): Promise<string>;
}
```
### Phase 4.2: AI 引擎 + Schema
**参考设计**: §5.3, §5.4, §5.5, §5.6
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/ai/schema.ts` | 新建 | `AIResponse`, `TranslatedDiagnostic`, `CustomRuleResult`, `AIFinding` 接口 |
| `src/ai/engine.ts` | 新建 | `runAIReview()` 主函数 |
**引擎核心逻辑**:
```typescript
async function runAIReview(code, staticDiagnostics, customRules) {
const [resultA, resultB] = await Promise.allSettled([
callCustomRuleReview(code, customRules), // 请求 A
callTranslateAndDeepReview(code, staticDiagnostics), // 请求 B
]);
return {
customRuleResults: ...,
translatedDiagnostics: ...,
findings: ...,
degraded: resultA.status === 'rejected' || resultB.status === 'rejected',
error: ...,
};
}
```
**两并行请求**:
- 请求 A: 自定义规则评估(system prompt 注入规则 description
- 请求 B: 静态分析翻译 + AI 深度审查
**降级策略**:
1. 单请求失败不影响另一个请求(`Promise.allSettled`
2. 所有 AI 功能都失败时,纯静态分析结果仍然展示
3. 失败信息在面板顶部以黄色/红色提示条展示
### Phase 4.3: 自定义规则系统
**参考设计**: §12
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/rules/yaml-parser.ts` | 新建 | `loadActiveRules(workspaceRoot)` |
**加载逻辑**:
```
1. 扫描 .code-review/rules/*.yaml → 加载所有规则定义
2. 读取 .code-review/config.yaml
3. 按文件级 enabled 列表过滤
4. 按规则级 rules.<id>.enabled 覆盖
5. 返回激活的规则列表
```
**CustomRule 类型**:
```typescript
interface CustomRule {
id: string; // 不含 custom: 前缀,运行时自动拼接
severity: Severity;
description: string; // AI 评估依据
message: string; // 触发时显示
languages?: string[];
}
```
### Phase 4.4: 结果合并
**参考设计**: §14.1
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/merger/merger.ts` | 新建 | `mergeResults()``MergedReport` |
```typescript
interface MergedReport {
linterDiagnostics: LinterDiagnostic[];
customRuleDiagnostics: LinterDiagnostic[];
translatedDiagnostics: TranslatedDiagnostic[];
aiFindings: AIFinding[];
linterCount: number;
customRuleCount: number;
aiCount: number;
errors: string[];
degraded: boolean;
duration: number;
filePath: string;
language: string;
adapterNames: string[];
fixableLinterIndices: number[];
fixableCustomIndices: number[];
}
```
### Phase 4.5: 自动修复
**参考设计**: §8
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/fixer/fixer.ts` | 新建 | `generateFix()`, `applyFix()`, `applyBatchFixes()`, `undoLastFix()` |
**核心流程**:
```
用户触发修复(单条 / 批量)
prepareContext() 获取代码上下文(动态行数)
generateFix() 调用 AI 生成修复方案(复用 Provider)
matchAndValidate() 匹配验证(行号 → 代码搜索)
applyFix() 应用修复到编辑器(单条 / 批量倒序)
保存快照,更新撤销按钮状态
```
**动态上下文策略**:
| 问题类型 | 上下文范围 |
|---------|-----------|
| 命名(naming | 问题行 ± 2 行 |
| 代码风格(style) | 问题行 ± 5 行 |
| 逻辑/安全/性能(bug/security/performance | 整个函数/方法 |
**两阶段匹配**:
- 阶段 1: 按行号匹配原文
- 阶段 2: 全文搜索 originalText
### Phase 4.6: 报告导出
**参考设计**: §14.2
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/utils/report.ts` | 新建 | `reportToMarkdown(report: MergedReport): string` |
**Markdown 格式**: 文件信息 → 统计摘要 → 分来源列出问题(linter/自定义/AI)
---
## Phase 5: UI 层
**依赖**: Phase 4
**参考设计**: §6, §7, §13
### Phase 5.1: 命令注册 + extension.ts 更新
**参考设计**: §6.1
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/activation/commands.ts` | 新建 | 注册所有命令处理函数 |
| `package.json` | 修改 | 替换 helloWorld 为正式命令、添加 viewsContainers/views/menus/configuration |
| `src/extension.ts` | 修改 | activate 中注册命令、视图、监听保存事件 |
**8 个命令**:
| 命令 ID | 功能 | 快捷键 |
|---------|------|--------|
| `codeReviewer.review` | 运行完整审查(静态分析 + AI | Ctrl+Shift+R |
| `codeReviewer.reviewSelection` | 审查选中代码 | — |
| `codeReviewer.openPanel` | 显示审查报告面板 | — |
| `codeReviewer.exportReport` | 导出 Markdown 报告 | — |
| `codeReviewer.addCustomRule` | 添加自定义规则 | — |
| `codeReviewer.fixIssue` | 修复单条问题 | — |
| `codeReviewer.fixAll` | 批量修复 | — |
| `codeReviewer.openSetup` | 打开设置面板(侧边栏) | — |
**package.json 需添加的贡献点**:
```jsonc
{
"viewsContainers": {
"activitybar": [{
"id": "code-reviewer",
"title": "净码特工",
"icon": "images/icon.png"
}]
},
"views": {
"code-reviewer": [{
"type": "tree",
"id": "codeReviewer.setupView",
"name": "设置"
}]
},
"menus": {
"editor/context": [
{ "command": "codeReviewer.review", "group": "navigation" },
{ "command": "codeReviewer.reviewSelection", "when": "editorHasSelection" }
]
},
"configuration": {
// 见 Phase 1 配置 Key 映射
}
}
```
### Phase 5.2: 设置面板(侧边栏 TreeView
**参考设计**: §13
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/views/setupView.ts` | 新建 | `SetupViewProvider implements vscode.TreeDataProvider` |
**面板区域**:
| 区域 | 元素 | 数据源 |
|------|------|--------|
| 快速开始 | 三步引导(①②③ 圆形序号,完成变紫色) | 实时状态 |
| 审核引擎 | 三个模块标签(紫/琥珀/绿圆点) | 静态 |
| AI 模型配置 | 提供商下拉框 + 模型下拉框 + 状态标签 | `ai.provider` / `ai.model` |
| API Key | 密码输入框 + Base URL + 状态标签 | SecretStorage / `ai.baseUrl` |
| 输出语言 | 下拉框 | `ai.outputLanguage` |
| 自定义规则 | 开关列表 + 添加行 | `.code-review/config.yaml` |
**快速引导**:
| 步骤 | 触发条件 | 效果 |
|------|----------|------|
| ① 配置 AI 模型及 API Key | API Key 有值 | 序号变紫色 |
| ② 启用自定义规则 | 任意规则开关打开 | 序号变紫色 |
| ③ 保存并测试连接 | 前两步完成 + 测试成功 | 序号变紫色 |
### Phase 5.3: 审查面板(Webview
**参考设计**: §7
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/panel/webview.ts` | 新建 | `ReviewPanel` 类(Webview 管理) |
**面板布局**:
```
┌─────────────────────────────────────┐
│ 📋 代码审查报告 │
│ xxx.java · Java · 3.2s │
├─────────────────────────────────────┤
│ [总计:10] [错误:3] [警告:5] [建议:2] │
├─────────────────────────────────────┤
│ 🔧 静态分析 | 📋 自定义规则 | 🤖 AI │
├─────────────────────────────────────┤
│ 问题列表(可跳转、修复、忽略) │
├─────────────────────────────────────┤
│ [🔄 重新审查] [📄 导出] [⚙️ 设置] │
│ [↩ 撤销上次修复] │
└─────────────────────────────────────┘
```
**消息协议**:
```typescript
// Webview → Extension
interface PanelMessage {
type: 'navigate' | 'rerun' | 'export' | 'settings' | 'fix' | 'fixAll';
line?: number;
ruleId?: string;
source?: 'linter' | 'custom' | 'ai';
}
// Extension → Webview
interface PanelUpdate {
report: MergedReport;
degraded: boolean;
errors: string[];
hasSnapshot: boolean;
}
```
---
## Phase 6: 构建与测试
**依赖**: Phase 5
**参考设计**: §15, §16, §17
### Phase 6.1: 构建脚本
| 文件 | 操作 | 内容 |
|------|------|------|
| `scripts/build.mjs` | 新建 | esbuild 打包(bundle + minify + external vscode |
| `package.json` | 修改 | 更新 vscode:prepublish / 添加 build 脚本 |
**esbuild 配置**:
```javascript
entryPoints: ['src/extension.ts'],
bundle: true,
outfile: 'out/extension.js',
external: ['vscode'],
format: 'cjs',
platform: 'node',
target: 'node22',
```
### Phase 6.2: 工具脚本
| 文件 | 操作 | 内容 |
|------|------|------|
| `scripts/download-pmd.mjs` | 新建 | 自动下载 PMD 7.26.0 JAR 依赖 |
| `scripts/package-prod.mjs` | 新建 | 生产打包:esbuild + copy assets + vsce |
### Phase 6.3: 测试
| 文件 | 操作 | 内容 |
|------|------|------|
| `src/test/fixtures/` | 新建 | 测试用代码样本目录 |
| `src/test/adapter.test.ts` | 新建 | ESLint/Stylelint/sql-lint 输出解析测试 |
| `src/test/config.test.ts` | 新建 | 配置读取正确性测试 |
| `src/test/merger.test.ts` | 新建 | 多源结果合并 + 统计计算测试 |
| `src/test/pipeline.test.ts` | 新建 | 全链路集成测试 |
| `src/test/extension.test.ts` | 修改 | 替换占位测试 |
---
## 文件变更总数
| Phase | 新建 | 修改 | 合计 |
|-------|------|------|------|
| Phase 1 | 6 | 0 | 6 |
| Phase 2 | 10 | 0 | 10 |
| Phase 3 | 1 | 0 | 1 |
| Phase 4 | 9 | 0 | 9 |
| Phase 5 | 3 | 2 | 5 |
| Phase 6 | 4 | 2 | 6 |
| **总计** | **33** | **4** | **37** |
---
## npm 依赖(需在 Phase 2/4 时添加)
### 运行时依赖
```json
{
"eslint": "^9.39.3",
"stylelint": "^17.14.0",
"node-sql-parser": "^5.4.0"
}
```
### 开发依赖(新增)
```json
{
"esbuild": "^0.28.1",
"@vscode/vsce": "^3.9.2"
}
```
### 外部工具
| 工具 | 版本 | 用途 | 获取方式 |
|------|------|------|----------|
| PMD | 7.26.0 | Java/JSP 静态分析 | 自带 `jars/pmd/lib/` |
| Java | 11+ | PMD 运行环境 | 用户环境 |
| sqlfluff | — | SQL 静态分析 | 用户环境(pip install sqlfluff |