Files
2026Technology-Competition/src/merger/merger.ts
T
范智鹏 3d8119d9c9 feat: 自动修复重构与审查面板交互修复 + 静态分析 AI 翻译配对 + SQLFluff 方言显示
- 自动修复:废弃 AI 修复,改 linter 原生 fix 多轮收敛;CodeAction hover + 面板修复/全部修复 + 快照 diff 撤销;hover 修复不入「已修复」列表、重新审查清空;修复/撤销后自动保存;单条修复只修目标问题(区间重叠收敛,不再连带相邻同规则)
- 审查面板:内联 JS 外部化(reviewPanel.js)修复 CSP 屏蔽导致的修复按钮/行号跳转/tab 失效;面板操作不依赖文件焦点(resolveFixDocument);行号跳转定位已打开编辑器,不在面板列新开副本
- 静态分析:translatedDiagnostics 规则 ID 归一化配对 + 深度审查 prompt 强化,静态分析条目显示中文翻译与逐条 AI 建议
- 波浪线:诊断补 source/code,hover 显示快速修复链接
- SQLFluff:设置面板方言徽章(显式/全局/项目/内置来源配色)
2026-08-18 21:41:51 +08:00

145 lines
4.1 KiB
TypeScript

import * as vscode from 'vscode';
import type { LinterDiagnostic, Severity } from '../types';
import type { TranslatedDiagnostic, CustomRuleResult, AIFinding } from '../ai/schema';
export 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[];
customRuleFilterInfo?: {
totalActive: number;
injected: number;
filteredOut: number;
skippedRequestA: boolean;
};
}
interface MergeInput {
staticDiagnostics: LinterDiagnostic[];
customRuleResults: CustomRuleResult[];
translatedDiagnostics: TranslatedDiagnostic[];
aiFindings: AIFinding[];
errors: string[];
degraded: boolean;
startTime: number;
filePath: string;
language: string;
adapterIds: string[];
customRuleFilterInfo?: {
totalActive: number;
injected: number;
filteredOut: number;
skippedRequestA: boolean;
};
}
const SEVERITY_RANK: Record<string, number> = { error: 0, warning: 1, info: 2 };
const RULE_NAMESPACE_PREFIXES = ['eslint:', 'stylelint:', 'sqlfluff:', 'pmd:', 'custom:', 'method:'];
function normalizeRuleId(id: string): string {
let result = id.trim();
for (const p of RULE_NAMESPACE_PREFIXES) {
if (result.startsWith(p)) {
result = result.slice(p.length);
break;
}
}
const segments = result.split(/[:/]/);
return segments[segments.length - 1];
}
function findTranslation(pool: TranslatedDiagnostic[], ruleId: string): TranslatedDiagnostic | undefined {
for (let i = 0; i < pool.length; i++) {
if (pool[i].originalRuleId === ruleId) {
return pool.splice(i, 1)[0];
}
}
const norm = normalizeRuleId(ruleId);
for (let i = 0; i < pool.length; i++) {
if (normalizeRuleId(pool[i].originalRuleId) === norm) {
return pool.splice(i, 1)[0];
}
}
return undefined;
}
function sortBySeverityAndLine<T extends { severity: string }>(items: T[], lineOf: (item: T) => number): T[] {
return [...items].sort((a, b) => {
const rankDiff = (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3);
if (rankDiff !== 0) { return rankDiff; }
return lineOf(a) - lineOf(b);
});
}
export function mergeResults(input: MergeInput): MergedReport {
const customRuleDiagnostics: LinterDiagnostic[] = sortBySeverityAndLine(
input.customRuleResults.map(r => ({
severity: r.severity as Severity,
ruleId: r.ruleId,
message: r.message,
range: new vscode.Range(Math.max(0, r.line - 1), 0, Math.max(0, r.line - 1), 1),
})),
d => d.range.start.line
);
const translationPool = [...input.translatedDiagnostics];
const linterDiagnostics = sortBySeverityAndLine(
input.staticDiagnostics.map(d => {
const td = findTranslation(translationPool, d.ruleId);
if (td) {
return { ...d, message: td.translatedMessage, suggestion: td.translatedSuggestion || d.suggestion };
}
return d;
}),
d => d.range.start.line
);
const aiFindings = sortBySeverityAndLine(
input.aiFindings.map(f => ({ ...f, line: Math.max(0, f.line - 1) })),
f => f.line
);
const linterCount = linterDiagnostics.length;
const customRuleCount = customRuleDiagnostics.length;
const aiCount = aiFindings.length;
const fixableLinterIndices = linterDiagnostics
.map((d, i) => (d.fix ? i : -1))
.filter(i => i !== -1);
const fixableCustomIndices: number[] = [];
return {
linterDiagnostics,
customRuleDiagnostics,
translatedDiagnostics: input.translatedDiagnostics,
aiFindings,
linterCount,
customRuleCount,
aiCount,
errors: input.errors,
degraded: input.degraded,
duration: Date.now() - input.startTime,
filePath: input.filePath,
language: input.language,
adapterNames: input.adapterIds,
fixableLinterIndices,
fixableCustomIndices,
customRuleFilterInfo: input.customRuleFilterInfo,
};
}