fix: 审查报告修复与清理,升级至 1.3.0

- 版本号 1.2.0 -> 1.3.0;CHANGELOG 新增 1.3.0 条目(修复 6 项 + 清理 3 项)
- addCustomRule 空壳命令改为打开设置面板(抽取 openSetupPanel 复用)
- deactivate 清理 analysisTimers pending 定时器 + 显式 dispose markers
- PMD execPmd 去掉 stdout.length>0 兜底,非零退出按 stderr 报错
- SQLFluff 项目配置检测全量支持 setup.cfg/tox.ini/pep8.ini/pyproject.toml(内容感知)
- 删除 orchestrator 未使用的 getAdapterMap/getAdaptersByIds,补充单语言单 linter 设计注释
- 删除 pmd.ts 未使用的 jarPathChecked 死字段
- merger 翻译配对改为按 originalRuleId 匹配,新增 2 个测试
- runStaticAndApply 包 try-catch,消除未处理 Promise 拒绝
- AGENTS.md 日志规则新增打包排除项;_AI_USAGE_LOG.md 追加记录
This commit is contained in:
范智鹏
2026-08-09 14:35:43 +08:00
parent 8c3e239acd
commit 6aaccd490c
10 changed files with 89 additions and 46 deletions
+17 -13
View File
@@ -17,6 +17,20 @@ import type { CustomRule } from '../types';
let currentReport: MergedReport | null = null;
async function openSetupPanel(): Promise<void> {
try {
await vscode.commands.executeCommand('workbench.view.extension.code-reviewer');
} catch {
const action = await vscode.window.showErrorMessage(
t('setup.openSetupFail'),
t('setup.openSettingsJson')
);
if (action === t('setup.openSettingsJson')) {
await vscode.commands.executeCommand('workbench.action.openSettingsJson');
}
}
}
export function registerCommands(
context: vscode.ExtensionContext,
orchestrator: Orchestrator,
@@ -224,13 +238,13 @@ export function registerCommands(
);
context.subscriptions.push(
vscode.commands.registerCommand('codeReviewer.addCustomRule', () => {
vscode.commands.registerCommand('codeReviewer.addCustomRule', async () => {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (!workspaceRoot) {
vscode.window.showWarningMessage(t('setup.noWorkspace'));
return;
}
vscode.window.showInformationMessage(t('setup.manageRulesHint'));
await openSetupPanel();
})
);
@@ -248,17 +262,7 @@ export function registerCommands(
context.subscriptions.push(
vscode.commands.registerCommand('codeReviewer.openSetup', async () => {
try {
await vscode.commands.executeCommand('workbench.view.extension.code-reviewer');
} catch {
const action = await vscode.window.showErrorMessage(
t('setup.openSetupFail'),
t('setup.openSettingsJson')
);
if (action === t('setup.openSettingsJson')) {
await vscode.commands.executeCommand('workbench.action.openSettingsJson');
}
}
await openSetupPanel();
})
);
+1 -2
View File
@@ -11,7 +11,6 @@ export class PmdAdapter implements LinterAdapter {
id = 'pmd';
supportedLanguages = ['java'];
private pmdDir: string | null = null;
private jarPathChecked = false;
private auxResolver = new AuxClasspathResolver();
private resolvePmdDir(): string {
@@ -117,7 +116,7 @@ export class PmdAdapter implements LinterAdapter {
proc.stdout.on('data', (data: Buffer) => { stdout += data.toString(); });
proc.stderr.on('data', (data: Buffer) => { stderr += data.toString(); });
proc.on('close', (code) => {
if (code === 0 || code === 4 || stdout.length > 0) {
if (code === 0 || code === 4) {
resolve(stdout);
} else {
reject(new Error(stderr || `PMD exited with code ${code}`));
+12 -4
View File
@@ -52,11 +52,19 @@ export function buildPRSMessage(description: string, dialect: string): string {
}
function hasProjectSqlfluffConfig(workspaceRoot: string): boolean {
const candidates = ['.sqlfluff', '.sqlfluff.ini'];
const candidates: Array<{ file: string; marker: string | null }> = [
{ file: '.sqlfluff', marker: null },
{ file: 'setup.cfg', marker: '[sqlfluff]' },
{ file: 'tox.ini', marker: '[sqlfluff]' },
{ file: 'pep8.ini', marker: '[sqlfluff]' },
{ file: 'pyproject.toml', marker: '[tool.sqlfluff]' },
];
for (const candidate of candidates) {
if (fs.existsSync(path.join(workspaceRoot, candidate))) {
return true;
}
const filePath = path.join(workspaceRoot, candidate.file);
if (!fs.existsSync(filePath)) { continue; }
if (candidate.marker === null) { return true; }
const content = fs.readFileSync(filePath, 'utf-8');
if (content.includes(candidate.marker)) { return true; }
}
return false;
}
+16 -7
View File
@@ -12,13 +12,17 @@ let orchestrator: Orchestrator;
let markers: DiagnosticMarkers;
async function runStaticAndApply(document: vscode.TextDocument): Promise<void> {
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);
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>();
@@ -113,5 +117,10 @@ export function activate(context: vscode.ExtensionContext) {
}
export function deactivate() {
for (const timer of analysisTimers.values()) {
clearTimeout(timer);
}
analysisTimers.clear();
markers?.dispose();
orchestrator = undefined!;
}
+10 -2
View File
@@ -66,9 +66,17 @@ export function mergeResults(input: MergeInput): MergedReport {
d => d.range.start.line
);
const translationsByRule = new Map<string, TranslatedDiagnostic[]>();
for (const td of input.translatedDiagnostics) {
const list = translationsByRule.get(td.originalRuleId) ?? [];
list.push(td);
translationsByRule.set(td.originalRuleId, list);
}
const linterDiagnostics = sortBySeverityAndLine(
input.staticDiagnostics.map((d, i) => {
const td = input.translatedDiagnostics[i];
input.staticDiagnostics.map(d => {
const list = translationsByRule.get(d.ruleId);
const td = list?.shift();
if (td) {
return { ...d, message: td.translatedMessage, suggestion: td.translatedSuggestion || d.suggestion };
}
+2 -14
View File
@@ -18,6 +18,8 @@ export class Orchestrator {
private adapters: LinterAdapter[];
constructor() {
// 单语言单 linter 设计:按 linters.<language> 单选配置分派一个适配器;
// 需多引擎的文件走组合适配器(如 JspAdapter = PMD + ESLint + Stylelint)。
this.adapters = [
new ESLintAdapter(),
new PmdAdapter(),
@@ -27,16 +29,6 @@ export class Orchestrator {
];
}
getAdapterMap(): Map<string, LinterAdapter> {
const map = new Map<string, LinterAdapter>();
for (const adapter of this.adapters) {
for (const lang of adapter.supportedLanguages) {
map.set(lang, adapter);
}
}
return map;
}
async runStaticAnalysis(
document: vscode.TextDocument,
workingDir: string
@@ -81,8 +73,4 @@ export class Orchestrator {
duration: Date.now() - startTime,
};
}
getAdaptersByIds(ids: string[]): LinterAdapter[] {
return ids.map(id => this.adapters.find(a => a.id === id)).filter(Boolean) as LinterAdapter[];
}
}