Files
2026Technology-Competition/src/extension.ts
T
范智鹏 d16d680f0f feat: AI 修复链路扩展 + 修复预览两步确认 + 自定义规则/AI 审查条目支持 AI 修复
- AI 修复:无原生 fix 的 linter 条目走 AI 修复(aiFixEngine/fixPrompt,AI 重检收敛),自定义规则与 AI 审查条目新增 AI 修复按钮(customFixEngine),自定义规则条目展开显示 AI 建议(suggestion 字段)

- 修复预览:面板触发修复先用内置 diff 预览,面板内「应用/取消」两步确认后才写入(fixPreview/fixPending,单条与分 tab 批量均支持)

- 修复面板交互:linter/custom/ai 分 tab「全部修复」、已修复+撤销、pending 按钮状态同步

- bug 修复:修复按钮失败后卡  不恢复;custom/ai 修复不稳定(空修复重试、AI 重检收敛判定放宽、降级接受最后一次有效修复)

- 移除 AI codeDiff 展示块
2026-08-20 22:26:30 +08:00

145 lines
4.8 KiB
TypeScript

import * as vscode from 'vscode';
import { Orchestrator } from './orchestrator/orchestrator';
import { registerCommands } from './activation/commands';
import { SetupViewProvider } from './views/setupView';
import { setLanguage, t, type Language } from './i18n/messages';
import { getAIOutputLanguage } from './config';
import { ReviewStatusCache } from './scope/status-cache';
import { MethodCodeLensProvider } from './views/codeLensProvider';
import { DiagnosticMarkers, isMarkersEnabled } from './diagnostics/diagnosticMarkers';
import { FixCodeActionProvider } from './fix/codeActionProvider';
import { FixSessionManager } from './fix/fixSession';
import { FixPendingStore } from './fix/fixPending';
import { registerFixPreviewProvider } from './fix/fixPreview';
let orchestrator: Orchestrator;
let markers: DiagnosticMarkers;
async function runStaticAndApply(document: vscode.TextDocument): Promise<void> {
try {
const version = document.version;
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '';
const workingDir = workspaceRoot || vscode.Uri.joinPath(document.uri, '..').fsPath;
const result = await orchestrator.runStaticAnalysis(document, workingDir);
const current = vscode.workspace.textDocuments.find(d => d.uri.toString() === document.uri.toString());
if (!current || current.version !== version) { return; }
markers.apply(document.uri, result.diagnostics);
} catch (err) {
console.error('[code-reviewer] static analysis failed:', err);
}
}
const analysisTimers = new Map<string, NodeJS.Timeout>();
function scheduleAnalysis(document: vscode.TextDocument, delay: number): void {
if (document.uri.scheme !== 'file' || !isMarkersEnabled()) { return; }
const key = document.uri.toString();
const existing = analysisTimers.get(key);
if (existing) { clearTimeout(existing); }
const timer = setTimeout(async () => {
analysisTimers.delete(key);
await runStaticAndApply(document);
}, delay);
analysisTimers.set(key, timer);
}
async function analyzeOpenDocuments(): Promise<void> {
const docs = vscode.workspace.textDocuments;
const active = vscode.window.activeTextEditor?.document;
const ordered = [...docs].sort((a, b) => a === active ? -1 : b === active ? 1 : 0);
for (const doc of ordered) {
if (doc.uri.scheme !== 'file' || !isMarkersEnabled()) { continue; }
await runStaticAndApply(doc);
}
}
export function activate(context: vscode.ExtensionContext) {
const lang = getAIOutputLanguage() as Language;
setLanguage(lang);
console.log(t('extension.activated'));
orchestrator = new Orchestrator();
markers = new DiagnosticMarkers();
context.subscriptions.push(markers);
const setupProvider = new SetupViewProvider(context);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider('codeReviewer.setupView', setupProvider)
);
const statusCache = new ReviewStatusCache();
const codeLensProvider = new MethodCodeLensProvider(statusCache);
const fixSession = new FixSessionManager();
const pendingStore = new FixPendingStore();
registerFixPreviewProvider(context);
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
{ scheme: 'file' },
new FixCodeActionProvider(orchestrator),
{ providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] }
)
);
void analyzeOpenDocuments();
context.subscriptions.push(
vscode.languages.registerCodeLensProvider(
{ scheme: 'file' },
codeLensProvider
)
);
context.subscriptions.push(
vscode.workspace.onDidCloseTextDocument((document) => {
statusCache.clearDocument(document.uri);
markers.clear(document.uri);
fixSession.clear(document.uri);
pendingStore.clear(document.fileName);
})
);
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument((document) => {
if (document.uri.scheme !== 'file' || !isMarkersEnabled()) { return; }
void runStaticAndApply(document);
})
);
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((event) => {
markers.clear(event.document.uri);
scheduleAnalysis(event.document, 1000);
})
);
registerCommands(context, orchestrator, codeLensProvider, statusCache, markers, fixSession, pendingStore);
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument((document) => {
scheduleAnalysis(document, 500);
})
);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('vscode-code-reviewer.ai.outputLanguage')) {
const newLang = getAIOutputLanguage() as Language;
setLanguage(newLang);
}
})
);
}
export function deactivate() {
for (const timer of analysisTimers.values()) {
clearTimeout(timer);
}
analysisTimers.clear();
markers?.dispose();
orchestrator = undefined!;
}