Files

120 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
```typescript
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()`: spawn `sqlfluff lint --format json --dialect <dialect> -`stdin 传入代码
- 方言映射:`sql → ansi`, `plsql → postgres`
- ruleId 加前缀 `sql-lint:`
- `isAvailable()`: `sqlfluff --version` 检测
- 通过 stdin 传代码,无需真实文件(支持虚拟文档)
---
## 验收
- [ ] 文件创建完成
- [ ] `npm run compile` 通过
- [ ] `npm run lint` 通过