docs: upload code files and config

This commit is contained in:
Developer
2026-07-13 18:41:33 +08:00
parent 6a64aece24
commit cafe67db6d
38 changed files with 7448 additions and 126 deletions
@@ -0,0 +1,645 @@
# 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/endpoint/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.endpoint` | 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, endpoint)` 工厂函数 |
**Provider 接口**:
```typescript
interface ChatOptions {
model: string;
temperature: number;
timeoutMs: number;
}
abstract class AIProvider {
abstract id: string;
abstract name: string;
constructor(protected apiKey: string, protected endpoint: 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": "CodeGuard 代码审查",
"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.endpoint` |
| 输出语言 | 下拉框 | `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 |