3.6 KiB
3.6 KiB
Step 05 — Phase 2.4: sql-lint 适配器
依赖: Step 02
参考设计: §3.3
目标
实现 SQL / PL/SQL 的适配器,通过 CLI 子进程调用 sqlfluff。
新建文件
| # | 文件 | 说明 |
|---|---|---|
| 1 | src/adapters/sql-lint.ts |
SqlLintAdapter |
src/adapters/sql-lint.ts
import * as vscode from 'vscode';
import * as child_process from 'child_process';
import { LinterAdapter, LinterDiagnostic, AdapterResult } from '../types';
import { getLinterConfig } from '../config';
const DIALECT_MAP: Record<string, string> = {
sql: 'ansi',
plsql: 'postgres',
};
export class SqlLintAdapter implements LinterAdapter {
id = 'sql-lint';
supportedLanguages = ['sql', 'plsql'];
async check(document: vscode.TextDocument, workingDir: string): Promise<AdapterResult> {
try {
const code = document.getText();
const languageId = document.languageId;
const dialect = DIALECT_MAP[languageId] ?? 'ansi';
const config = getLinterConfig();
const args = ['lint', '--format', 'json', '--dialect', dialect, '-'];
if (config.sqlLintConfigFile) {
args.push('--config', config.sqlLintConfigFile);
}
const result = await this.execSqlfluff(code, args, workingDir);
const output = JSON.parse(result);
const diagnostics: LinterDiagnostic[] = [];
for (const violation of output) {
const line = Math.max(0, (violation.line_no ?? violation.line_pos ?? 1) - 1);
const col = Math.max(0, (violation.line_pos ?? 1) - 1);
const range = new vscode.Range(line, col, line, col + 1);
diagnostics.push({
severity: 'warning',
ruleId: `sql-lint:${violation.code ?? violation.rule ?? 'unknown'}`,
message: violation.description ?? violation.message ?? '',
range,
});
}
return { diagnostics, status: 'ok' };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes('ENOENT') || message.includes('not found')) {
return { diagnostics: [], status: 'tool-unavailable', errorMessage: 'sqlfluff 未安装,请执行 pip install sqlfluff' };
}
return { diagnostics: [], status: 'execution-failed', errorMessage: message };
}
}
private execSqlfluff(code: string, args: string[], cwd: string): Promise<string> {
return new Promise((resolve, reject) => {
const proc = child_process.spawn('sqlfluff', args, { cwd });
let stdout = '';
let stderr = '';
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 || stdout.length > 0) {
resolve(stdout);
} else {
reject(new Error(stderr || `sqlfluff exited with code ${code}`));
}
});
proc.on('error', reject);
proc.stdin.write(code);
proc.stdin.end();
});
}
isAvailable(): boolean {
try {
child_process.execSync('sqlfluff --version', { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
}
关键逻辑
check(): spawnsqlfluff lint --format json --dialect <dialect> -,stdin 传入代码- 方言映射:
sql → ansi,plsql → postgres - ruleId 加前缀
sql-lint: isAvailable():sqlfluff --version检测- 通过 stdin 传代码,无需真实文件(支持虚拟文档)
验收
- 文件创建完成
npm run compile通过npm run lint通过