docs: upload code files and config
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# 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` 通过
|
||||
Reference in New Issue
Block a user