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