Files
2026Technology-Competition/docs/superpowers/specs/2026-07-31-method-level-review-plan.md
T
范智鹏 247f19fd44 feat: 方法级代码审查 + 模板导入/预览增强 + SQLFluff 方言 + AI 空响应报错修复
- 方法级审查: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 + 行号排序
2026-08-03 22:53:20 +08:00

35 KiB
Raw Blame History

方法级代码审查功能实施计划书

面向 AI 编码 agent 的技术实施文档。本文档包含完整的架构设计、文件清单、代码模板和验收标准,可直接据此编码。

项目上下文

  • 仓库:sdjndaq/2026-ai-b3Gitee 私有仓库)
  • 分支:vscode-code-reviewer
  • 项目名:代码审查官 · Code Purifiervscode-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.tsactivate(context) 函数完成三件事:

  1. 创建 Orchestrator 实例
  2. 注册 SetupViewProviderWebview View
  3. 调用 registerCommands(context, orchestrator) 注册所有命令
  4. 监听 onDidSaveTextDocument 执行防抖静态分析
  5. 监听 onDidChangeConfiguration 切换 i18n 语言

命令注册

src/activation/commands.tsregisterCommands(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) 函数。内部流程:

  1. getApiKey(context) 获取 API Key
  2. createProvider(providerId, apiKey, baseUrl, extensionUri) 创建 AI Provider
  3. 并行发起两个请求:
    • requestA:自定义规则匹配(buildCustomRuleSystemPrompt + buildUserPromptCustomRules
    • requestB:深度审查(buildDeepReviewSystemPrompt + buildUserPromptDeepReview
  4. parseJsonResponse 解析 JSON
  5. 返回 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.tsmergeResults(...) 将静态诊断、AI 发现等聚合成 MergedReportsrc/panel/webview.tsReviewPanel 类负责 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.tssrc/merger/merger.tssrc/panel/webview.tssrc/ai/providers/ 目录、src/i18n/ 目录均不需要修改。方法级审查的执行路径不经过 Orchestrator,直接从命令层调用 engine.ts。自定义规则的加载复用现有 src/rules/rule-loader.tsloadActiveRules 函数,过滤复用 src/rules/rule-filter.tsfilterForDocument 函数,均无需修改。


新增文件详细设计

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.FunctionSymbolKind.MethodSymbolKind.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 文件中调用 getMethodSymbolsextractMethodScope,确认能提取方法名、代码、调用链
1.3 src/ai/schema.ts TypeScript 编译通过,类型无冲突

Phase 2AI 引擎

步骤 文件 验证方式
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 + 自定义规则后手动调用,确认返回 customRuleResultsfindings 两类结果

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 4i18n 与打磨

步骤 文件 验证方式
4.1 src/i18n/messages.ts 新增 key 切换输出语言为 en,确认 CodeLens 文案变为英文
4.2 右键菜单 fallback 处理 右键触发 reviewMethod,确认能用光标位置定位方法
4.3 状态缓存刷新 审查完成后确认 CodeLens 文案变为 ✓ 已审查(N 个问题)

验收标准

功能验收

  1. 打开任意 .ts / .js / .java / .py 文件,每个函数声明上方出现 🔍 Code Purifier: 审查此方法 CodeLens 按钮
  2. 点击按钮后,右下角弹出进度通知 Code Purifier 正在审查方法:xxx
  3. 审查完成后,CodeLens 按钮文案变为 ✓ Code Purifier: 已审查(N 个问题)✓ Code Purifier: 已审查(无问题),其中 N = 自定义规则违规数 + AI 深度审查发现数
  4. 审查面板自动打开,展示方法级的 AI 审查结果
  5. 审查结果中,自定义规则违规的 ruleIdcustom: 前缀标识,AI 深度审查发现的 ruleIdmethod: 前缀标识
  6. 审查结果覆盖 6 个维度(correctness / security / design / convention / performance / testability
  7. 审查结果中的 path 字段描述了触发路径
  8. 当工作区配置了自定义规则时,审查结果中包含 customRuleResults,且违规内容与规则语义相关(非文本匹配)
  9. 当工作区无自定义规则时,审查结果中 customRuleResults 为空数组,AI 仅做 6 维度深度审查

边界验收

  1. 在没有 Symbol Provider 的纯文本文件中,CodeLens 不出现(不报错)
  2. 文件内方法数超过 50 个时,CodeLens 不出现(性能保护)
  3. 在设置中关闭 codelens.enabled 后,CodeLens 消失
  4. 在设置中将某语言从 codelens.languages 中移除后,该语言文件不显示 CodeLens
  5. 未配置 API Key 时,提示需要 API Key(不崩溃)
  6. AI 返回非 JSON 时,降级提示解析失败(不崩溃)
  7. 文档关闭后重新打开,CodeLens 恢复为初始状态 🔍 Code Purifier: 审查此方法(缓存已清理)

架构验收

  1. src/orchestrator/orchestrator.ts 未被修改
  2. src/rules/rule-filter.ts 未被修改
  3. 现有 codeReviewer.review 命令行为不变(文档级三阶段审查正常工作)
  4. 现有 codeReviewer.reviewSelection 命令行为不变
  5. TypeScript 编译无错误(npm run compile
  6. ESLint 检查无错误(npm run lint

注意事项

  • 所有用户可见文案必须通过 t() 函数获取,支持中英文切换
  • runMethodReview 内复用现有的 getApiKeycreateProviderparseJsonResponseaddLineNumbers 等工具函数,不重复实现
  • MethodFinding 的字段与 AIFinding 高度重叠,mergeResults 可直接接收 MethodFinding[] 作为 aiFindings 参数(结构兼容)
  • CodeLens 的 refresh() 通过 _onDidChangeCodeLenses.fire() 触发,VS Code 会重新调用 provideCodeLenses
  • package.json 中的 engines.vscode 字段为 ^1.120.0CodeLens API 和 DocumentSymbol API 在该版本完全支持
  • 自定义规则通过 loadActiveRules + filterForDocument 加载,与现有 codeReviewer.review 命令使用同一加载路径,保证规则一致性
  • AI 对自定义规则做语义匹配而非文本匹配——prompt 中明确要求"理解语义,而非文本匹配",避免简单关键词命中导致的误报
  • customRules 为空数组时,system prompt 中的 Task 1 规则匹配段和 customRuleResults 输出段自动省略,AI 仅执行 6 维度深度审查