feat: 编辑器波浪线自动标记 + SQLFluff 默认方言改 oracle + AI 审查行号修正

- 波浪线标记:打开/编辑/保存自动静态分析,新增 markers.enabled 配置与 onStartupFinished 激活,带防抖与版本竞态保护
- SQLFluff:默认方言 sql->oracle;PRS 解析错误改用 i18n 三语友好提示(含当前方言与配置指引)并提升为 error
- AI 审查:mergeResults 统一 aiFindings 行号为 0 基,修复整文件审查行号偏移 1 行
This commit is contained in:
范智鹏
2026-08-04 22:10:41 +08:00
parent 247f19fd44
commit 50855dae67
9 changed files with 232 additions and 20 deletions
+5
View File
@@ -174,3 +174,8 @@
| 2026-08-03 22:11 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 默认方言调整:DIALECT_MAP sql 映射 ansi→mysqlplsql 保持 oracle),setupView 项目配置模板 dialect postgres→mysql;优先级不变(显式 sqlfluff.dialect > 全局/项目配置 > 语言映射兜底) | 中间产物:无;实证(sqlfluff 4.2.2):反引号 MySQL 脚本在适配器内置配置(dialect=mysql)下正常解析无 PRS(此前 ansi 会 PRS 失败),验证中途发现 PowerShell 转义将反引号变双引号导致的 PRS 误报(MySQL 双引号=字符串,非产品问题);测试沿用移开 adapter.test.ts 方案 64 用例通过(1 个既有 extension 激活失败与本变更无关) | src/adapters/sqlfluff.ts, src/views/setupView.ts | deepseek-v4-flash |
| 2026-08-03 22:18 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复 JSP 适配器两处缺陷:(1) 整文件检查改用 PMD JSP 规则集(新增 pmd.ts checkJsp(),优先级 getPMDJspRulesetPath > 内置 pmd-jsp-ruleset.xml,此前该配置零使用导致整文件 files:[]);(2) 提取的 <% %> Java 片段裸语句无法被 PMD 按编译单元解析(ParseException)→ 按 scriptletKind 包装成 package jsp; class JspScriptlet{...} 再交 PMD,用 codeLineOffset 映射回原文行号并过滤合成包装噪音(NoPackage/AtLeastOneConstructor 等);jsp-extractor.ts 区分 5 种标签(注释/指令跳过、declaration/expression/statement 标记 kind);PmdRunner.java 新增第 3 参数指定 stdin 临时文件扩展名(jsp 用 .jsp,未保存 JSP 也可走 JSP 语言模块)并重编译 | 中间产物:无;实证(PMD 7.26.0):含违规 JSP 检出 JspEncoding/NoScriptlets + EmptyControlStatement/UnusedLocalVariable/UnusedPrivateField 等,行号正确映射、无合成噪音;stdin+.jsp 扩展名路径验证通过;测试沿用移开 adapter.test.ts 方案 64 用例通过(1 个既有 extension 激活失败与本变更无关) | src/jsp/jsp-extractor.ts, src/adapters/jsp.ts, src/adapters/pmd.ts, jars/pmd/PmdRunner.java(重编译 PmdRunner.class) | deepseek-v4-flash |
| 2026-08-03 22:42 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 诊断按严重度+行号排序:mergeResults 集中排序(error<warning<info<其他,同级按 range.start.line 升序,Node 22 稳定排序),linterDiagnostics/customRuleDiagnostics/aiFindings 三数组统一;翻译映射在排序前完成;fixableLinterIndices/fixableCustomIndices 基于排序后数组重算;新增 merger.test.ts 排序测试 | 中间产物:测试断言首次行号基准错误(customRuleDiagnostics range 为 line-1 0 基,断言误用 1 基)→ 修正为 error:1/info:8;测试沿用移开 adapter.test.ts 方案 65 用例通过(新增排序用例,1 个既有 extension 激活失败与本变更无关) | src/merger/merger.ts, src/test/merger.test.ts | deepseek-v4-flash |
| 2026-08-04 20:34 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 内置配置默认方言调整:DIALECT_MAP 中 sql 映射由 mysql 改为 oracle,sql 文件未显式配置方言且无项目 .sqlfluff 时,内置配置按 oracle 审查(plsql 仍为 oracle | 无 | src/adapters/sqlfluff.ts | deepseek-v4-flash |
| 2026-08-04 20:48 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 编辑器波浪线标记功能:新增 src/diagnostics/diagnosticMarkers.tsDiagnosticMarkers 类封装 createDiagnosticCollection('codeReviewer') + 纯函数 toVscodeDiagnostics 严重度映射 error/warning/info→Error/Warning/Information 并加 [ruleId] 前缀 + isMarkersEnabled 配置读取);extension.ts 集成四时机(onDidOpenTextDocument file scheme 自动静态分析并应用、保存 debounce 500ms 结果应用到标记、onDidChangeTextDocument 编辑时清除、onDidCloseTextDocument 清除并入 statusCache 清理)+ 版本号校验防异步竞态(结果返回时文档已改/已关则不应用);commands.ts 完整审查命令 codeReviewer.review 完成后应用翻译后 linterDiagnosticsreviewMethod 不触碰标记);package.json 新增 vscode-code-reviewer.markers.enabled(默认 true);新增 src/test/diagnostics.test.ts 4 用例(severity 映射/range 透传/ruleId 前缀/空数组) | 中间产物:临时 .vscode-test-diag.mjs 配置(限定仅跑诊断测试,4 用例通过后删除);全量测试沿用团队既有惯例临时移开 adapter.test.js/extension.test.jsstylelint-config-recommended@18 ESM-only 无 exports.main 使 CJS require 失败的既有问题,extension 激活亦受其阻),其余 69 用例全部通过后恢复原文件 | src/diagnostics/diagnosticMarkers.ts(新增), src/test/diagnostics.test.ts(新增), src/extension.ts, src/activation/commands.ts, package.json | deepseek-v4-flash |
| 2026-08-04 20:59 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复 AI 审查行号显示错误(整文件审查多 1 行):根因是 AI 返回的 findings[].line 为 1 基(基于带行号代码),而 webview/report 按 0 基处理(buildIssueItem/formatLine 显示 line+1、navigate 直接作 0 基跳转),方法级审查因 commands.ts 已转 0 基而正确、两条路径基准不一致。修复:mergeResults 统一将 aiFindings.line 转 0 基(Math.max(0, f.line-1)),reviewMethod 的 aiFindings 偏移由 f.line+methodLine-1 改为 f.line+methodLine(传 1 基文件行号交 mergeResults 转换,与 customRuleResults 语义一致);更新 merger.test.ts 排序断言(aiFindings error:3/warning:6+ 新增 0 基转换用例 | 中间产物:无;根因追踪:先假设「mergeResults 统一转 0 基」并在 merger.test.ts 改写断言验证(4 passing);全量测试沿用既有惯例临时移开 adapter/extension.test.js 后 70 用例全部通过(新增 1 个转换用例),恢复原文件 | src/merger/merger.ts, src/activation/commands.ts, src/test/merger.test.ts | deepseek-v4-flash |
| 2026-08-04 21:20 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | SQLFluff PRS 解析错误呈现优化:sqlfluff.ts 新增可导出纯函数 buildPRSMessage(正则提取 Found unparsable section 引号内片段、正则失败回退原文、换行单行化 \\n、超 80 字符截断 +...),check() 中 v.code==='PRS' 时 message 改用 i18n 友好提示(含当前生效方言 effectiveDialect=cliDialect??fallbackDialect + 配置指引)、severity 提升为 error(解析失败时后续规则不可信);messages.ts 新增 adapter.sqlfluffPRS 三语 key;新增 sqlfluff-prs.test.ts 4 用例(片段提取/换行单行化/截断/回退)。背景:问题面板报 PRS 显示原始 SQL 片段+重复行号前缀观感差,且 DIALECT_MAP 默认方言已改为 oracle08-04 20:34 未提交改动)致 MySQL 风格 DDL 误报 PRS(已实证:同一 SQL mysql 方言解析通过、oracle 报 PRS) | 中间产物:测试首次用 msg.length<200 断言截断不稳定(中英模板长度差异致英文下超限,DEBUG 日志确认实际失败的是 truncate 用例而非 fallback 用例)→ 改为断言 !includes(longFragment)(验证全文被截断,不依赖模板总长);全量测试沿用既有惯例临时移开 adapter/extension.test.js 后 74 用例全部通过(新增 4 个 PRS 用例),恢复原文件 | src/adapters/sqlfluff.ts, src/i18n/messages.ts, src/test/sqlfluff-prs.test.ts(新增) | deepseek-v4-flash |
| 2026-08-04 21:45 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复已打开文件不静态分析、无波浪线问题:根因两处——① package.json activationEvents 为空导致插件不自动激活(onDidOpenTextDocument 对激活前已打开的文件永不触发);② activate 未遍历存量 textDocuments。改动:package.json activationEvents 加 onStartupFinishedextension.ts 新增 scheduleAnalysis(统一按 URI 防抖调度器,gate file scheme + markers.enabled)与 analyzeOpenDocuments(遍历已打开文档、活动文件优先、串行 await runStaticAndApply),activate 时调用;onDidChangeTextDocument 改为 clear 波浪线 + 1s 防抖自动重分析;onDidSaveTextDocument 改用 scheduleAnalysis(500) 替换原独立 debounceTimers Map。复用 runStaticAndApply 版本竞态保护,行为不变。设计文档 docs/superpowers/specs/2026-08-04-auto-static-analysis-design.md 已产出 | 中间产物:无(澄清阶段就"并发 vs 串行"向用户说明各 linter 执行成本后定为串行;激活顺序定为活动文件优先);lint/compile 通过 | package.json, src/extension.ts, docs/superpowers/specs/2026-08-04-auto-static-analysis-design.md(新增) | deepseek-v4-flash |
@@ -0,0 +1,92 @@
# 打开文件自动静态分析 + 波浪线 设计文档
> 面向 AI 编码 agent 的技术实施文档。本文档包含完整的根因分析、方案设计、文件清单和验收标准,可直接据此编码。
## 功能目标
修复插件对**已打开文件不做静态分析、不显示波浪线**的问题:
1. VSCode 启动后(`onStartupFinished`)插件自动激活,对**所有已打开的文件**串行做静态分析并显示波浪线。
2. 文本编辑时先清除波浪线(即时反馈),停止输入 1s 后自动重新分析。
3. 保存文件仍按现有 500ms 防抖重分析。
## 根因分析
| 问题 | 位置 | 说明 |
|------|------|------|
| 插件不自动激活 | `package.json:12` `activationEvents: []` | 无 `onStartupFinished` 等事件,插件只在执行命令/打开面板时激活 |
| 存量文件不分析 | `extension.ts` activate 无初始扫描 | `onDidOpenTextDocument` 只对"激活后新打开"的文件触发,激活前已打开的文件永远不会被分析 |
| 编辑不重分析 | `extension.ts:63-66` | `onDidChangeTextDocument``markers.clear`,需等保存才有波浪线 |
## 架构决策
| 决策 | 选择 | 理由 |
|------|------|------|
| 激活时机 | `onStartupFinished` | VSCode 启动完成后激活,不拖慢启动,兼容性最好 |
| 初始分析范围 | 所有已打开 file 文档 | 行为统一,符合"已打开文件都应有波浪线"预期 |
| 批量执行方式 | 串行执行 | 避免多个 JVMPMD/ Python 进程(SQLFluff)并发;JS/TS/CSS 为进程内执行,无压力 |
| 串行顺序 | 活动文件优先 | 用户眼前文件先出波浪线 |
| 编辑时行为 | 1s 防抖自动重分析 | 更接近 ESLint 等 linter 体验 |
| 防抖调度 | 统一按 URI 调度器 | 合并保存/编辑两类防抖,逻辑单一 |
## 文件变更清单
| 文件 | 变更 |
|------|------|
| `package.json` | `activationEvents``[]` 改为 `["onStartupFinished"]` |
| `src/extension.ts` | ① 初始串行扫描(活动文件优先);② 统一 `scheduleAnalysis` 调度器;③ 编辑时防抖重分析 |
无新增依赖。
## 关键实现
### 统一防抖调度器(`src/extension.ts`
```ts
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);
}
```
### 激活时初始串行扫描(活动文件优先)
```ts
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);
}
```
### 事件改写
- `onDidChangeTextDocument``markers.clear(uri)` + `scheduleAnalysis(doc, 1000)`
- `onDidSaveTextDocument`:删除原 `debounceTimers` Map,改用 `scheduleAnalysis(doc, 500)`
复用现有 `runStaticAndApply`(含版本竞态保护)与 `markers.apply`,行为不变。
## 影响范围
- **激活行为**:插件随 VSCode 启动自动激活(内存常驻,激活逻辑本已完整,代价极小)
- **兼容性**`markers.enabled` 配置全程生效;命令/CodeLens/AI 审查流程不受影响
- **进程压力**:初始串行扫描保证任一时刻最多 1 个 JVM 在跑
## 验收标准
1. 启动 Extension Dev Host,预置多个已打开文件(含活动与非活动),确认启动后活动文件波浪线先出现、其余依次出现
2. 编辑文件停止 1s 后波浪线自动刷新;输入过程中波浪线消失
3. 保存文件 500ms 后波浪线刷新
4. `markers.enabled=false` 时不显示任何波浪线且不触发分析
5. `npm run lint``npm run compile` 通过
+8 -1
View File
@@ -9,7 +9,9 @@
"categories": [
"Other"
],
"activationEvents": [],
"activationEvents": [
"onStartupFinished"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
@@ -310,6 +312,11 @@
"type": "array",
"default": ["typescript", "javascript", "java", "python"],
"description": "启用方法级 CodeLens 的语言列表"
},
"vscode-code-reviewer.markers.enabled": {
"type": "boolean",
"default": true,
"description": "在编辑器中显示代码审查波浪线标记"
}
}
}
+7 -1
View File
@@ -12,6 +12,7 @@ import { exportTemplate } from '../rules/export-service';
import { extractMethodScope } from '../scope/method-extractor';
import { ReviewStatusCache } from '../scope/status-cache';
import { MethodCodeLensProvider } from '../views/codeLensProvider';
import { DiagnosticMarkers, isMarkersEnabled } from '../diagnostics/diagnosticMarkers';
import type { CustomRule } from '../types';
let currentReport: MergedReport | null = null;
@@ -21,6 +22,7 @@ export function registerCommands(
orchestrator: Orchestrator,
codeLensProvider: MethodCodeLensProvider,
statusCache: ReviewStatusCache,
markers: DiagnosticMarkers,
): void {
context.subscriptions.push(
@@ -73,6 +75,10 @@ export function registerCommands(
const panel = ReviewPanel.createOrShow(context.extensionUri);
panel.update(currentReport);
if (isMarkersEnabled()) {
markers.apply(document.uri, currentReport.linterDiagnostics);
}
});
})
);
@@ -151,7 +157,7 @@ export function registerCommands(
translatedDiagnostics: [],
aiFindings: result.findings.map(f => ({
...f,
line: f.line + methodLine - 1,
line: f.line + methodLine,
})),
errors: result.error ? [result.error] : [],
degraded: result.degraded,
+15 -3
View File
@@ -9,7 +9,7 @@ import { t } from '../i18n/messages';
import staticRules from '../rules/static-rules.json';
const DIALECT_MAP: Record<string, string> = {
sql: 'mysql',
sql: 'oracle',
plsql: 'oracle',
};
@@ -53,6 +53,16 @@ function tierToSeverity(tier: string | undefined): Severity {
return 'warning';
}
export function buildPRSMessage(description: string, dialect: string): string {
const match = /Found unparsable section: '([\s\S]*)'/.exec(description);
let fragment = match ? match[1] : description;
fragment = fragment.replace(/\n/g, '\\n');
if (fragment.length > 80) {
fragment = fragment.slice(0, 80) + '...';
}
return t('adapter.sqlfluffPRS', { 0: dialect, 1: fragment });
}
function hasProjectSqlfluffConfig(workspaceRoot: string): boolean {
const candidates = ['.sqlfluff', '.sqlfluff.ini'];
for (const candidate of candidates) {
@@ -135,6 +145,7 @@ export class SqlFluffAdapter implements LinterAdapter {
const cliDialect = explicitDialect && SUPPORTED_DIALECTS.includes(explicitDialect)
? explicitDialect
: undefined;
const effectiveDialect = cliDialect ?? fallbackDialect;
let configPath: string | undefined;
let tempConfigPath: string | undefined;
@@ -156,10 +167,11 @@ export class SqlFluffAdapter implements LinterAdapter {
for (const result of results) {
for (const v of result.violations) {
const isPRS = v.code === 'PRS';
diagnostics.push({
severity: tierToSeverity(tierMap.get(v.code)),
severity: isPRS ? 'error' : tierToSeverity(tierMap.get(v.code)),
ruleId: `sqlfluff:${v.code}`,
message: v.description,
message: isPRS ? buildPRSMessage(v.description, effectiveDialect) : v.description,
range: new vscode.Range(
v.start_line_no - 1,
v.start_line_pos - 1,
+38
View File
@@ -0,0 +1,38 @@
import * as vscode from 'vscode';
import type { LinterDiagnostic } from '../types';
export function isMarkersEnabled(): boolean {
return vscode.workspace.getConfiguration('vscode-code-reviewer').get<boolean>('markers.enabled', true);
}
export function toVscodeDiagnostics(diagnostics: LinterDiagnostic[]): vscode.Diagnostic[] {
return diagnostics.map(d => {
const severity =
d.severity === 'error'
? vscode.DiagnosticSeverity.Error
: d.severity === 'warning'
? vscode.DiagnosticSeverity.Warning
: vscode.DiagnosticSeverity.Information;
return new vscode.Diagnostic(d.range, `[${d.ruleId}] ${d.message}`, severity);
});
}
export class DiagnosticMarkers {
private collection: vscode.DiagnosticCollection;
constructor() {
this.collection = vscode.languages.createDiagnosticCollection('codeReviewer');
}
apply(uri: vscode.Uri, diagnostics: LinterDiagnostic[]): void {
this.collection.set(uri, toVscodeDiagnostics(diagnostics));
}
clear(uri: vscode.Uri): void {
this.collection.delete(uri);
}
dispose(): void {
this.collection.dispose();
}
}
+58 -14
View File
@@ -6,8 +6,46 @@ 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';
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);
}
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;
@@ -15,6 +53,8 @@ export function activate(context: vscode.ExtensionContext) {
console.log(t('extension.activated'));
orchestrator = new Orchestrator();
markers = new DiagnosticMarkers();
context.subscriptions.push(markers);
const setupProvider = new SetupViewProvider(context);
context.subscriptions.push(
@@ -24,6 +64,8 @@ export function activate(context: vscode.ExtensionContext) {
const statusCache = new ReviewStatusCache();
const codeLensProvider = new MethodCodeLensProvider(statusCache);
void analyzeOpenDocuments();
context.subscriptions.push(
vscode.languages.registerCodeLensProvider(
{ scheme: 'file' },
@@ -34,27 +76,29 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.workspace.onDidCloseTextDocument((document) => {
statusCache.clearDocument(document.uri);
markers.clear(document.uri);
})
);
registerCommands(context, orchestrator, codeLensProvider, statusCache);
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument((document) => {
if (document.uri.scheme !== 'file' || !isMarkersEnabled()) { return; }
void runStaticAndApply(document);
})
);
const debounceTimers = new Map<string, NodeJS.Timeout>();
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((event) => {
markers.clear(event.document.uri);
scheduleAnalysis(event.document, 1000);
})
);
registerCommands(context, orchestrator, codeLensProvider, statusCache, markers);
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument((document) => {
const key = document.uri.toString();
const existing = debounceTimers.get(key);
if (existing) { clearTimeout(existing); }
const timer = setTimeout(() => {
debounceTimers.delete(key);
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '';
const workingDir = workspaceRoot || vscode.Uri.joinPath(document.uri, '..').fsPath;
orchestrator.runStaticAnalysis(document, workingDir);
}, 500);
debounceTimers.set(key, timer);
scheduleAnalysis(document, 500);
})
);
+5
View File
@@ -1068,6 +1068,11 @@ const messages: Record<string, Record<Language, string>> = {
en: 'sqlfluff not installed, run: pip install sqlfluff',
ja: 'sqlfluffがインストールされていません、pip install sqlfluff を実行してください',
},
'adapter.sqlfluffPRS': {
'zh-CN': 'SQL 解析失败(当前方言:{0}),可能是方言不匹配或语法错误。请在设置中配置 sqlfluff.dialect 或添加项目 .sqlfluff 指定正确方言。无法解析片段:{1}',
en: 'Failed to parse SQL (current dialect: {0}). Possible dialect mismatch or syntax error. Configure sqlfluff.dialect in settings or add a project .sqlfluff. Unparsable fragment: {1}',
ja: 'SQLの解析に失敗しました(現在の方言:{0})。方言の不一致または構文エラーの可能性があります。設定で sqlfluff.dialect を構成するか、プロジェクトに .sqlfluff を追加してください。解析不能な断片:{1}',
},
'adapter.invalidApiKey': {
'zh-CN': 'API Key 无效,请重新设置',
en: 'Invalid API Key, please reconfigure',
+4 -1
View File
@@ -77,7 +77,10 @@ export function mergeResults(input: MergeInput): MergedReport {
d => d.range.start.line
);
const aiFindings = sortBySeverityAndLine(input.aiFindings, f => f.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;