- 方法级审查:CodeLens 触发 + 单次 AI 调用(规则匹配 + 6 维度深度审查),新增 method-extractor / status-cache / codeLensProvider - 模板导入:severity 保留原始值 + 占位 id、去重对照统一 known-rules、重复提示条双语翻译、箭头展开/折叠 UI、520 条静态规则补 zh/ja 翻译 - SQL:sql-lint 重命名 sqlfluff + sqlfluff.dialect 方言可配置 + 默认方言调整 - ESLint:v9 flat config 接线修复(overrideConfigFile)+ legacy 迁移提示 - AI:空响应 EmptyContentError + 重试一次 + max_tokens 截断专用报错 - JSP:整文件检查走 PMD JSP 规则集 + scriptlet 包装解析 + 行号映射 - 诊断按 severity + 行号排序
35 KiB
方法级代码审查功能实施计划书
面向 AI 编码 agent 的技术实施文档。本文档包含完整的架构设计、文件清单、代码模板和验收标准,可直接据此编码。
项目上下文
- 仓库:
sdjndaq/2026-ai-b3(Gitee 私有仓库) - 分支:
vscode-code-reviewer - 项目名:代码审查官 · Code Purifier(
vscode-code-reviewer) - 版本:1.2.0
- 类型:VS Code 扩展插件(TypeScript)
- 技术栈:TypeScript + VS Code Extension API + DeepSeek AI
功能目标
为现有代码审查插件新增方法级触发能力。用户在每个函数声明行上方看到 CodeLens 按钮,点击后对该方法执行自定义规则审查 + AI 深度审查。方法级审查不执行静态分析(IDE 已实时提供 linter 诊断),但加载自定义规则与 AI 协同工作:AI 先按自定义规则做匹配,再以 6 维度增强策略做深度审查,单次调用同时产出两类结果。
核心设计决策
| 决策 | 选择 | 理由 |
|---|---|---|
| 审查流程 | 自定义规则 + AI(不走静态分析) | IDE 已实时提供 linter 诊断,静态分析冗余;自定义规则保留是因为团队规则仍需在方法级生效 |
| AI 调用模式 | 单次调用合并规则匹配 + 深度审查 | 方法代码短,token 预算充足,合并为单次调用减少延迟 |
| 触发方式 | CodeLens 行内按钮 | 最直观的交互,用户无需记忆命令或快捷键 |
| 方法提取 | VS Code Symbol API | 原生支持,覆盖 TS/JS/Java/Python 等主流语言 |
| 调用链 | 同文件粗匹配 | 首版限制在同文件内,控制实现复杂度 |
| AI 审查范围 | 6 维度增强 | AI 同时承担规则匹配和深度审查,维度覆盖比全文件更广 |
现有架构摘要
编码前必须理解以下现有代码结构,所有新代码需与这些模式保持一致。
入口与注册
src/extension.ts 的 activate(context) 函数完成三件事:
- 创建
Orchestrator实例 - 注册
SetupViewProvider(Webview View) - 调用
registerCommands(context, orchestrator)注册所有命令 - 监听
onDidSaveTextDocument执行防抖静态分析 - 监听
onDidChangeConfiguration切换 i18n 语言
命令注册
src/activation/commands.ts 的 registerCommands(context, orchestrator) 注册命令。现有命令通过 vscode.window.withProgress 包裹执行,使用 t() 函数做 i18n。核心命令 codeReviewer.review 的执行流程:
获取 document + workingDir
→ withProgress:
→ orchestrator.runStaticAnalysis(document, workingDir)
→ loadActiveRules(workspaceRoot) + filterAndSummarize
→ runAIReview(context, code, staticDiagnostics, relevantRules)
→ mergeResults(...)
→ ReviewPanel.createOrShow + panel.update(currentReport)
AI 引擎
src/ai/engine.ts 导出 runAIReview(context, code, staticDiagnostics, customRules) 函数。内部流程:
getApiKey(context)获取 API KeycreateProvider(providerId, apiKey, baseUrl, extensionUri)创建 AI Provider- 并行发起两个请求:
requestA:自定义规则匹配(buildCustomRuleSystemPrompt+buildUserPromptCustomRules)requestB:深度审查(buildDeepReviewSystemPrompt+buildUserPromptDeepReview)
parseJsonResponse解析 JSON- 返回
AIEngineResult
现有 buildDeepReviewSystemPrompt() 的定位是"补充静态分析的盲区",任务包含翻译静态诊断和发现额外问题。方法级审查不复用这个 prompt。
类型定义
src/types.ts 定义核心类型:
interface CustomRule {
id: string;
severity: Severity;
description: string;
message: string;
languages?: string[];
excludeLanguages?: string[];
}
interface LinterDiagnostic {
severity: Severity;
ruleId: string;
message: string;
range: vscode.Range;
suggestion?: string;
}
interface LinterAdapter {
id: string;
supportedLanguages: string[];
check(document: vscode.TextDocument, workingDir: string): Promise<AdapterResult>;
isAvailable(): boolean;
}
src/ai/schema.ts 定义 AI 输出类型:
interface AIFinding {
ruleId: string;
severity: 'error' | 'warning' | 'info';
category: 'bug' | 'performance' | 'security' | 'style' | 'design';
title: string;
description: string;
suggestion: string;
codeDiff?: string;
line: number;
}
interface AIEngineResult {
customRuleResults: CustomRuleResult[];
translatedDiagnostics: TranslatedDiagnostic[];
findings: AIFinding[];
degraded: boolean;
error?: string;
}
结果合并与展示
src/merger/merger.ts 的 mergeResults(...) 将静态诊断、AI 发现等聚合成 MergedReport。src/panel/webview.ts 的 ReviewPanel 类负责 Webview 面板的创建和更新。
i18n
src/i18n/messages.ts 导出 t(key, params?) 和 setLanguage(lang) 函数。所有用户可见文案必须通过 t() 获取。
改动总览
新增文件(3 个)
| 文件路径 | 职责 |
|---|---|
src/scope/method-extractor.ts |
从文档中提取方法符号、代码、签名、调用链 |
src/views/codeLensProvider.ts |
CodeLens Provider,在函数声明上方渲染审查按钮 |
src/scope/status-cache.ts |
方法审查状态缓存,驱动 CodeLens 按钮文案刷新 |
修改文件(4 个 + package.json)
| 文件路径 | 改动内容 |
|---|---|
src/activation/commands.ts |
新增 codeReviewer.reviewMethod 命令 |
src/ai/engine.ts |
新增 runMethodReview 函数 + 方法级 prompt builder |
src/ai/schema.ts |
新增 MethodFinding 类型 + 扩展 category |
src/extension.ts |
注册 CodeLensProvider |
package.json |
新增 command、codelens 配置项 |
不修改的文件
src/orchestrator/orchestrator.ts、src/merger/merger.ts、src/panel/webview.ts、src/ai/providers/ 目录、src/i18n/ 目录均不需要修改。方法级审查的执行路径不经过 Orchestrator,直接从命令层调用 engine.ts。自定义规则的加载复用现有 src/rules/rule-loader.ts 的 loadActiveRules 函数,过滤复用 src/rules/rule-filter.ts 的 filterForDocument 函数,均无需修改。
新增文件详细设计
1. src/scope/method-extractor.ts
职责
从 VS Code 文档中提取方法级别的信息,包括方法代码、签名和同文件内的调用链。
导出类型
import * as vscode from 'vscode';
export interface MethodScope {
/** 方法名称 */
name: string;
/** 方法在文档中的完整范围 */
range: vscode.Range;
/** 方法代码文本(带行号) */
code: string;
/** 方法签名(一行摘要,如 "processOrder(order: Order): Promise<Result>") */
signature: string;
/** 调用者方法名列表(同文件内引用了本方法的其他方法) */
callers: string[];
/** 被调用方法名列表(本方法体内调用的其他方法) */
callees: string[];
/** 方法在业务流中的角色描述(由符号层级推断) */
role: string;
}
export interface MethodSymbol {
/** 方法名称 */
name: string;
/** 方法范围 */
range: vscode.Range;
/** 方法所在类/模块名(如果有) */
containerName?: string;
}
导出函数
/**
* 获取文档中所有方法/函数符号
* 使用 VS Code Symbol API,无 Provider 时回退到正则匹配
*/
export async function getMethodSymbols(
document: vscode.TextDocument
): Promise<MethodSymbol[]>;
/**
* 提取指定位置所在方法的完整 MethodScope
* @param document 文档
* @param position 光标位置或 Symbol 范围
*/
export async function extractMethodScope(
document: vscode.TextDocument,
range: vscode.Range
): Promise<MethodScope | null>;
实现要点
Symbol API 获取符号树:
const symbols = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
'vscode.executeDocumentSymbolProvider',
document.uri
);
过滤 SymbolKind.Function、SymbolKind.Method、SymbolKind.Constructor 类型的节点。递归遍历子符号(类内的方法)。
正则回退(无 Symbol Provider 时):
针对 JavaScript/TypeScript 使用正则 /(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(/g 匹配函数声明。针对 Java 使用 /((?:public|private|protected|static)\s+)*\w+(?:<[^>]+>)?\s+(\w+)\s*\(/g。
调用链粗匹配:
获取同文件内所有方法符号后,对每个方法:
- 被调用者:在本方法代码文本中搜索其他方法名是否出现(正则
\b方法名\s*\() - 调用者:遍历其他所有方法的代码,检查本方法名是否出现
签名提取:
取方法代码的第一行(到 { 或 ) 结束),去除注释和多余空白。
role 字段推断:
根据 containerName 和方法名粗略推断,例如:
containerName包含 "Controller" → "HTTP 请求处理入口"containerName包含 "Service" → "业务逻辑处理"- 方法名以 "get/set/is" 开头 → "属性访问器"
- 默认 → "通用方法"
2. src/views/codeLensProvider.ts
职责
实现 vscode.CodeLensProvider,在每个函数声明行上方渲染审查按钮。
类定义
import * as vscode from 'vscode';
import { getMethodSymbols } from '../scope/method-extractor';
import { ReviewStatusCache } from '../scope/status-cache';
import { t } from '../i18n/messages';
export class MethodCodeLensProvider implements vscode.CodeLensProvider {
private _onDidChangeCodeLenses: vscode.EventEmitter<void> =
new vscode.EventEmitter<void>();
readonly onDidChangeCodeLenses: vscode.Event<void> =
this._onDidChangeCodeLenses.event;
constructor(private statusCache: ReviewStatusCache) {}
/** 触发 CodeLens 刷新 */
refresh(): void {
this._onDidChangeCodeLenses.fire();
}
async provideCodeLenses(
document: vscode.TextDocument,
token: vscode.CancellationToken
): Promise<vscode.CodeLens[]> {
// 1. 检查配置是否启用
const config = vscode.workspace.getConfiguration('vscode-code-reviewer');
const enabled = config.get<boolean>('codelens.enabled', true);
if (!enabled) return [];
// 2. 检查语言是否在允许列表内
const languages = config.get<string[]>('codelens.languages', [
'typescript', 'javascript', 'java', 'python'
]);
if (!languages.includes(document.languageId)) return [];
// 3. 获取方法符号
const symbols = await getMethodSymbols(document);
if (symbols.length === 0) return [];
// 4. 方法数量上限保护
if (symbols.length > 50) return [];
// 5. 为每个方法生成 CodeLens
const lenses: vscode.CodeLens[] = [];
for (const symbol of symbols) {
const status = this.statusCache.get(document.uri, symbol.name);
const title = this.buildLensTitle(status);
lenses.push(new vscode.CodeLens(symbol.range.start, {
command: 'codeReviewer.reviewMethod',
title: title,
arguments: [symbol.range],
}));
}
return lenses;
}
private buildLensTitle(status: ReviewStatus | null): string {
if (!status) {
return t('codelens.reviewMethod');
}
if (status.issueCount === 0) {
return t('codelens.reviewedClean');
}
return t('codelens.reviewedWithIssues', { 0: String(status.issueCount) });
}
}
i18n key 约定
需要在 src/i18n/messages.ts 中新增以下 key(中英文):
| key | 中文 | English |
|---|---|---|
codelens.reviewMethod |
🔍 Code Purifier: 审查此方法 |
🔍 Code Purifier: Review This Method |
codelens.reviewedClean |
✓ Code Purifier: 已审查(无问题) |
✓ Code Purifier: Reviewed (No Issues) |
codelens.reviewedWithIssues |
✓ Code Purifier: 已审查({0} 个问题) |
✓ Code Purifier: Reviewed ({0} Issues) |
methodReview.running |
Code Purifier 正在审查方法:{0} |
Code Purifier: Reviewing method: {0} |
methodReview.noMethod |
当前位置未检测到方法 |
No method detected at current position |
methodReview.complete |
方法审查完成,发现 {0} 个问题 |
Method review complete, {0} issues found |
3. src/scope/status-cache.ts
职责
内存缓存,记录每个方法最近一次的审查结果,驱动 CodeLens 按钮文案刷新。
类定义
import * as vscode from 'vscode';
export interface ReviewStatus {
/** 发现的问题数量 */
issueCount: number;
/** 审查时间戳 */
timestamp: number;
}
export class ReviewStatusCache {
/** key 格式:documentUri.toString() + '::' + methodName */
private cache = new Map<string, ReviewStatus>();
get(uri: vscode.Uri, methodName: string): ReviewStatus | null {
const key = this.buildKey(uri, methodName);
return this.cache.get(key) ?? null;
}
set(uri: vscode.Uri, methodName: string, issueCount: number): void {
const key = this.buildKey(uri, methodName);
this.cache.set(key, {
issueCount,
timestamp: Date.now(),
});
}
/** 清除指定文档的所有缓存(文档关闭时调用) */
clearDocument(uri: vscode.Uri): void {
const prefix = uri.toString() + '::';
for (const key of this.cache.keys()) {
if (key.startsWith(prefix)) {
this.cache.delete(key);
}
}
}
private buildKey(uri: vscode.Uri, methodName: string): string {
return uri.toString() + '::' + methodName;
}
}
修改文件详细设计
4. src/ai/schema.ts
在现有类型基础上新增方法级审查的类型定义,不修改现有类型。
新增内容
/** 方法级审查的 category 类型(6 维度) */
export type MethodFindingCategory =
| 'correctness' // 正确性:分支覆盖、边界条件、异常路径
| 'security' // 安全性:输入校验、注入风险、权限检查
| 'design' // 设计:职责单一、参数合理性、调用链适配
| 'convention' // 规范:命名、复杂度、魔法数字、注释
| 'performance' // 性能:时间/空间复杂度、资源泄漏
| 'testability'; // 可测试性:副作用隔离、依赖可 Mock 性
/** 方法级审查的 finding,扩展 AIFinding */
export interface MethodFinding {
ruleId: string;
severity: 'error' | 'warning' | 'info';
category: MethodFindingCategory;
title: string;
description: string;
suggestion: string;
codeDiff?: string;
line: number;
/** 触发路径描述,如 "if(order == null) → NPE on .getId()" */
path?: string;
}
/** 方法级审查的 AI 返回结构 */
export interface MethodReviewResult {
/** 自定义规则匹配结果(AI 按规则做语义判断) */
customRuleResults: CustomRuleResult[];
/** AI 深度审查发现 */
findings: MethodFinding[];
degraded: boolean;
error?: string;
}
5. src/ai/engine.ts
新增方法级审查的入口函数和专用 prompt builder。不修改现有 runAIReview 及其相关函数。
新增函数
/**
* 方法级 AI 审查
* 不依赖静态诊断,但加载自定义规则与 AI 协同工作
* 单次 AI 调用同时完成规则匹配和 6 维度深度审查
*/
export async function runMethodReview(
context: vscode.ExtensionContext,
scope: MethodScope,
customRules: CustomRule[],
): Promise<MethodReviewResult>;
内部实现流程
export async function runMethodReview(
context: vscode.ExtensionContext,
scope: MethodScope,
customRules: CustomRule[],
): Promise<MethodReviewResult> {
// 1. 获取 API Key
const apiKey = await getApiKey(context);
if (!apiKey) {
return {
customRuleResults: [],
findings: [],
degraded: true,
error: t('adapter.noApiKey'),
};
}
// 2. 创建 Provider(复用现有工厂)
const providerId = getAIProvider();
const baseUrl = getAIBaseUrl();
let provider: AIProvider;
try {
provider = createProvider(providerId, apiKey, baseUrl, context.extensionUri);
} catch (err) {
return {
customRuleResults: [],
findings: [],
degraded: true,
error: t('adapter.createProviderFail', { 0: err instanceof Error ? err.message : String(err) }),
};
}
// 3. 构建请求
const options = {
model: getAIModel(),
temperature: getAITemperature(),
maxTokens: getAIMaxTokens(),
timeoutMs: getAITimeout() * 1000,
};
const numberedCode = addLineNumbers(scope.code);
// 4. 单次 AI 调用(规则匹配 + 深度审查合并为一次)
// 有规则时 prompt 包含规则匹配任务,无规则时只做深度审查
const hasRules = customRules.length > 0;
const result = await provider.chat(
buildMethodReviewSystemPrompt(hasRules),
buildMethodUserPrompt(scope, numberedCode, customRules),
options,
);
// 5. 解析结果
const errors: string[] = [];
let customRuleResults: CustomRuleResult[] = [];
let findings: MethodFinding[] = [];
if (result.status === 'fulfilled') {
try {
const parsed = parseJsonResponse(result.value) as {
customRuleResults?: CustomRuleResult[];
findings?: MethodFinding[];
};
customRuleResults = (parsed.customRuleResults ?? []).map(r => ({
...r,
ruleId: r.ruleId.startsWith('custom:') ? r.ruleId : `custom:${r.ruleId}`,
}));
findings = (parsed.findings ?? []).map(f => ({
...f,
ruleId: f.ruleId.startsWith('method:') ? f.ruleId : `method:${f.ruleId}`,
}));
} catch (e) {
errors.push(t('adapter.aiReviewParseFail', { 0: e instanceof Error ? e.message : String(e) }));
}
} else {
errors.push(t('adapter.aiReviewRequestFail', { 0: result.reason }));
}
return {
customRuleResults,
findings,
degraded: errors.length > 0,
error: errors.join('; ') || undefined,
};
}
新增 prompt builder
function buildMethodReviewSystemPrompt(hasRules: boolean): string {
const lang = getLanguage();
if (lang === 'en') {
const ruleSection = hasRules
? `## Task 1: Custom Rule Matching
Evaluate whether the method violates any of the provided custom rules.
Understand semantics, not text matching.
Report violations in "customRuleResults".\n\n`
: '';
const ruleOutput = hasRules
? ` "customRuleResults": [
{
"ruleId": "original rule id",
"line": line_number,
"severity": "error|warning|info",
"message": "violation description"
}
],\n`
: '';
return `You are a senior code review expert reviewing a single method.
There is no static analysis before you — you handle rule matching AND deep review.
${ruleSection}## Review Strategy: Path Enumeration
- Walk through every if/else/switch branch, note coverage and gaps
- Enumerate boundary values for every parameter (null, empty collection, extreme values, wrong types)
- Check every throw/catch path for proper fallback strategy
- Trace the method's role in its call chain
## Required Dimensions (do not skip any)
A. Correctness: branch coverage, boundary conditions, exception path completeness
B. Security: input validation, injection risk, permission check, sensitive data leakage
C. Design: single responsibility, parameter design, return value contract, call chain adaptation
D. Convention: naming, cyclomatic complexity, magic numbers, missing comments
E. Performance: time/space complexity, resource leaks, unnecessary computation
F. Testability: side effect isolation, dependency mockability, deterministic output
## Call Chain Analysis
- Check whether callers' arguments match this method's expectations
- Check whether this method's return value is correctly handled by callers
- Check whether exceptions are caught or declared by callers
Output JSON only. Double quotes in strings must be escaped with \\".
Format:
{
${ruleOutput} "findings": [
{
"ruleId": "method-boundary-null",
"severity": "error|warning|info",
"category": "correctness|security|design|convention|performance|testability",
"title": "issue title",
"description": "detailed description",
"suggestion": "fix suggestion",
"codeDiff": "optional fix diff",
"line": line_number,
"path": "trigger path description, e.g. if(order==null) -> NPE on .getId()"
}
]
}
If no issues found, return empty arrays.
Output language: en`;
}
// 中文 prompt(默认)
const ruleSection = hasRules
? `## 任务一:自定义规则匹配
评估方法是否违反了提供的自定义规则。
理解语义,而非文本匹配。
在 "customRuleResults" 中报告违规。\n\n`
: '';
const ruleOutput = hasRules
? ` "customRuleResults": [
{
"ruleId": "原始规则 ID",
"line": 行号,
"severity": "error|warning|info",
"message": "违规描述"
}
],\n`
: '';
return `你是资深代码审查专家,正在审查单个方法。
没有静态分析的前置过滤——你同时负责规则匹配和深度审查。
${ruleSection}## 审查策略:逐路径枚举
- 遍历每个 if/else/switch 分支,标注覆盖与遗漏
- 枚举每个入参的边界值(null、空集合、极值、错误类型)
- 检查每个 throw/catch 路径的降级策略
- 追踪方法在调用链中的角色
## 必须覆盖的维度(不可跳过)
A. 正确性:分支覆盖、边界条件、异常路径完整性
B. 安全性:输入校验、注入风险、权限检查、敏感信息泄露
C. 设计:职责单一性、参数设计合理性、返回值契约、调用链适配
D. 规范:命名、圈复杂度、魔法数字、注释缺失
E. 性能:时间/空间复杂度、资源泄漏、不必要的计算
F. 可测试性:副作用隔离、依赖可 Mock 性、确定性输出
## 调用链分析
- 检查调用者传入的参数是否符合本方法预期
- 检查本方法的返回值是否被调用者正确处理
- 检查异常是否被调用者捕获或声明
输出 JSON,字符串中的双引号必须用 \\" 转义。
格式:
{
${ruleOutput} "findings": [
{
"ruleId": "method-boundary-null",
"severity": "error|warning|info",
"category": "correctness|security|design|convention|performance|testability",
"title": "问题标题",
"description": "详细描述",
"suggestion": "修复建议",
"codeDiff": "可选的修复 diff",
"line": 行号,
"path": "触发路径描述,如 if(order==null) -> NPE on .getId()"
}
]
}
如果未发现问题,返回空数组。
输出语言:zh-CN`;
}
function buildMethodUserPrompt(
scope: MethodScope,
numberedCode: string,
customRules: CustomRule[],
): string {
const lang = getLanguage();
const codeLabel = lang === 'en' ? 'Method Code (with line numbers)' : '方法代码(带行号)';
const sigLabel = lang === 'en' ? 'Method Signature' : '方法签名';
const chainLabel = lang === 'en' ? 'Call Chain Context' : '调用链上下文';
const roleLabel = lang === 'en' ? 'Role in Business Flow' : '业务流中的角色';
const callersLabel = lang === 'en' ? 'Callers' : '调用者';
const calleesLabel = lang === 'en' ? 'Callees' : '被调用者';
const noneLabel = lang === 'en' ? '(none)' : '(无)';
// 自定义规则块:有规则时注入,无规则时省略
let ruleBlock = '';
if (customRules.length > 0) {
const ruleLabel = lang === 'en' ? 'Custom Rules to Match' : '需匹配的自定义规则';
const ruleLines = customRules
.map((r, i) => `${i + 1}. [${r.id}] (${r.severity}) ${r.description}\n ${r.message}`)
.join('\n');
ruleBlock = `\n## ${ruleLabel}\n${ruleLines}\n`;
}
return `## ${sigLabel}
${scope.signature}
## ${codeLabel}
${numberedCode}
${ruleBlock}
## ${chainLabel}
${roleLabel}: ${scope.role}
${callersLabel}: ${scope.callers.length > 0 ? scope.callers.join(', ') : noneLabel}
${calleesLabel}: ${scope.callees.length > 0 ? scope.callees.join(', ') : noneLabel}`;
}
与现有 runAIReview 的关键差异
| 对比项 | 现有 runAIReview |
新增 runMethodReview |
|---|---|---|
| 输入参数 | code, staticDiagnostics, customRules |
scope: MethodScope, customRules |
| AI 调用次数 | 2 次(requestA + requestB 并行) | 1 次(规则匹配 + 深度审查合并) |
| prompt 定位 | 补充静态分析盲区 | 唯一审查者,全覆盖 |
| 输出结构 | customRuleResults + translatedDiagnostics + findings |
customRuleResults + findings |
| category | 5 类 | 6 类(新增 correctness、testability) |
| path 字段 | 无 | 有(触发路径描述) |
| 规则匹配方式 | 独立 prompt + 独立请求 | 与深度审查合并为单次 prompt |
6. src/activation/commands.ts
新增 codeReviewer.reviewMethod 命令。不修改现有命令。
新增命令注册
在 registerCommands 函数内追加:
context.subscriptions.push(
vscode.commands.registerCommand(
'codeReviewer.reviewMethod',
async (symbolRange: vscode.Range) => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const document = editor.document;
const workspaceRoot = vscode.workspace.getWorkspaceFolder(document.uri)?.uri.fsPath;
// 1. 提取方法
const scope = await extractMethodScope(document, symbolRange);
if (!scope) {
vscode.window.showWarningMessage(t('methodReview.noMethod'));
return;
}
// 2. 加载并过滤自定义规则(复用现有 rule-filter)
let customRules: CustomRule[] = [];
if (workspaceRoot) {
const allRules = await loadActiveRules(workspaceRoot);
customRules = filterForDocument(allRules, document);
}
// 3. 执行 AI 审查(传入自定义规则)
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: t('methodReview.running', { 0: scope.name }),
cancellable: false,
},
async () => {
const result = await runMethodReview(context, scope, customRules);
// 4. 构造 MergedReport(复用现有 merger)
// customRuleResults 和 findings 均来自 AI 单次调用
const totalIssues = result.customRuleResults.length + result.findings.length;
currentReport = mergeResults({
staticDiagnostics: [],
customRuleResults: result.customRuleResults,
translatedDiagnostics: [],
aiFindings: result.findings, // MethodFinding[] 兼容 AIFinding[]
errors: result.error ? [result.error] : [],
degraded: result.degraded,
startTime: Date.now(),
filePath: document.uri.fsPath,
language: document.languageId,
adapterIds: [],
customRuleFilterInfo: undefined,
});
// 5. 更新状态缓存并刷新 CodeLens
statusCache.set(document.uri, scope.name, totalIssues);
codeLensProvider.refresh();
// 6. 面板展示
const panel = ReviewPanel.createOrShow(context.extensionUri);
panel.update(currentReport);
// 7. 提示
vscode.window.showInformationMessage(
t('methodReview.complete', { 0: String(totalIssues) })
);
}
);
}
)
);
函数签名变更
registerCommands 需要新增两个参数:
export function registerCommands(
context: vscode.ExtensionContext,
orchestrator: Orchestrator,
codeLensProvider: MethodCodeLensProvider, // 新增
statusCache: ReviewStatusCache, // 新增
): void {
新增 import
commands.ts 顶部需追加以下 import(现有 import 不变):
import { loadActiveRules } from '../rules/rule-loader';
import { filterForDocument } from '../rules/rule-filter';
import { CustomRule } from '../types';
7. src/extension.ts
注册 CodeLensProvider 和 StatusCache。
修改内容
在 activate 函数中,registerCommands 调用之前新增:
export function activate(context: vscode.ExtensionContext) {
const lang = getAIOutputLanguage() as Language;
setLanguage(lang);
console.log(t('extension.activated'));
orchestrator = new Orchestrator();
const setupProvider = new SetupViewProvider(context);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider('codeReviewer.setupView', setupProvider)
);
// ===== 新增:方法级审查基础设施 =====
const statusCache = new ReviewStatusCache();
const codeLensProvider = new MethodCodeLensProvider(statusCache);
context.subscriptions.push(
vscode.languages.registerCodeLensProvider(
{ scheme: 'file' },
codeLensProvider
)
);
// 文档关闭时清理状态缓存
context.subscriptions.push(
vscode.workspace.onDidCloseTextDocument((document) => {
statusCache.clearDocument(document.uri);
})
);
// ===== 新增结束 =====
registerCommands(context, orchestrator, codeLensProvider, statusCache);
// ... 保留现有的 onDidSaveTextDocument 和 onDidChangeConfiguration 逻辑不变 ...
}
8. package.json
新增 command
在 contributes.commands 数组中追加:
{
"command": "codeReviewer.reviewMethod",
"title": "Code Purifier: 审查此方法"
}
新增 configuration
在 contributes.configuration.properties 中追加:
"vscode-code-reviewer.codelens.enabled": {
"type": "boolean",
"default": true,
"description": "在函数声明上方显示方法级审查按钮"
},
"vscode-code-reviewer.codelens.languages": {
"type": "array",
"default": ["typescript", "javascript", "java", "python"],
"description": "启用方法级 CodeLens 的语言列表"
}
新增 menu(可选)
在 contributes.menus 中追加右键菜单入口(作为 CodeLens 的补充):
"editor/context": [
{
"command": "codeReviewer.reviewMethod",
"when": "editorTextFocus",
"group": "navigation"
}
]
注意:右键菜单触发时 symbolRange 参数为 undefined,命令内需要处理此情况——使用 editor.selection.active 作为 fallback 位置传入 extractMethodScope。
实施顺序
按以下顺序实施,每步完成后可独立验证:
Phase 1:基础设施
| 步骤 | 文件 | 验证方式 |
|---|---|---|
| 1.1 | src/scope/status-cache.ts |
单元测试:set/get/clearDocument 行为正确 |
| 1.2 | src/scope/method-extractor.ts |
手动测试:在 TS 文件中调用 getMethodSymbols 和 extractMethodScope,确认能提取方法名、代码、调用链 |
| 1.3 | src/ai/schema.ts |
TypeScript 编译通过,类型无冲突 |
Phase 2:AI 引擎
| 步骤 | 文件 | 验证方式 |
|---|---|---|
| 2.1 | src/ai/engine.ts 新增 buildMethodReviewSystemPrompt |
确认 prompt 文本包含 6 维度、逐路径枚举策略,且有规则时包含 Task 1 规则匹配任务 |
| 2.2 | src/ai/engine.ts 新增 buildMethodUserPrompt |
确认输出包含签名、代码、调用链,且传入 customRules 时包含规则列表块 |
| 2.3 | src/ai/engine.ts 新增 runMethodReview |
配置 API Key + 自定义规则后手动调用,确认返回 customRuleResults 和 findings 两类结果 |
Phase 3:命令与集成
| 步骤 | 文件 | 验证方式 |
|---|---|---|
| 3.1 | src/activation/commands.ts 新增 reviewMethod 命令 |
通过命令面板触发,确认加载自定义规则并传入 runMethodReview,面板展示审查结果 |
| 3.2 | src/extension.ts 注册 CodeLensProvider |
打开 TS 文件,确认函数上方出现 CodeLens 按钮 |
| 3.3 | src/views/codeLensProvider.ts |
点击 CodeLens 按钮,确认触发方法级审查 |
| 3.4 | package.json 新增配置项 |
在设置中修改 codelens.enabled,确认 CodeLens 消失/出现 |
Phase 4:i18n 与打磨
| 步骤 | 文件 | 验证方式 |
|---|---|---|
| 4.1 | src/i18n/messages.ts 新增 key |
切换输出语言为 en,确认 CodeLens 文案变为英文 |
| 4.2 | 右键菜单 fallback 处理 | 右键触发 reviewMethod,确认能用光标位置定位方法 |
| 4.3 | 状态缓存刷新 | 审查完成后确认 CodeLens 文案变为 ✓ 已审查(N 个问题) |
验收标准
功能验收
- 打开任意
.ts/.js/.java/.py文件,每个函数声明上方出现🔍 Code Purifier: 审查此方法CodeLens 按钮 - 点击按钮后,右下角弹出进度通知
Code Purifier 正在审查方法:xxx - 审查完成后,CodeLens 按钮文案变为
✓ Code Purifier: 已审查(N 个问题)或✓ Code Purifier: 已审查(无问题),其中 N = 自定义规则违规数 + AI 深度审查发现数 - 审查面板自动打开,展示方法级的 AI 审查结果
- 审查结果中,自定义规则违规的
ruleId以custom:前缀标识,AI 深度审查发现的ruleId以method:前缀标识 - 审查结果覆盖 6 个维度(correctness / security / design / convention / performance / testability)
- 审查结果中的
path字段描述了触发路径 - 当工作区配置了自定义规则时,审查结果中包含
customRuleResults,且违规内容与规则语义相关(非文本匹配) - 当工作区无自定义规则时,审查结果中
customRuleResults为空数组,AI 仅做 6 维度深度审查
边界验收
- 在没有 Symbol Provider 的纯文本文件中,CodeLens 不出现(不报错)
- 文件内方法数超过 50 个时,CodeLens 不出现(性能保护)
- 在设置中关闭
codelens.enabled后,CodeLens 消失 - 在设置中将某语言从
codelens.languages中移除后,该语言文件不显示 CodeLens - 未配置 API Key 时,提示需要 API Key(不崩溃)
- AI 返回非 JSON 时,降级提示解析失败(不崩溃)
- 文档关闭后重新打开,CodeLens 恢复为初始状态
🔍 Code Purifier: 审查此方法(缓存已清理)
架构验收
src/orchestrator/orchestrator.ts未被修改src/rules/rule-filter.ts未被修改- 现有
codeReviewer.review命令行为不变(文档级三阶段审查正常工作) - 现有
codeReviewer.reviewSelection命令行为不变 - TypeScript 编译无错误(
npm run compile) - ESLint 检查无错误(
npm run lint)
注意事项
- 所有用户可见文案必须通过
t()函数获取,支持中英文切换 runMethodReview内复用现有的getApiKey、createProvider、parseJsonResponse、addLineNumbers等工具函数,不重复实现MethodFinding的字段与AIFinding高度重叠,mergeResults可直接接收MethodFinding[]作为aiFindings参数(结构兼容)- CodeLens 的
refresh()通过_onDidChangeCodeLenses.fire()触发,VS Code 会重新调用provideCodeLenses package.json中的engines.vscode字段为^1.120.0,CodeLens API 和 DocumentSymbol API 在该版本完全支持- 自定义规则通过
loadActiveRules+filterForDocument加载,与现有codeReviewer.review命令使用同一加载路径,保证规则一致性 - AI 对自定义规则做语义匹配而非文本匹配——prompt 中明确要求"理解语义,而非文本匹配",避免简单关键词命中导致的误报
- 当
customRules为空数组时,system prompt 中的 Task 1 规则匹配段和customRuleResults输出段自动省略,AI 仅执行 6 维度深度审查