# SQLFluff 方言只读展示 — 设计 Spec 日期:2026-08-14 状态:已审批 关联流程:① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 ## 1. 背景 侧边栏"设置"面板(`codeReviewer.setupView`)的 SQLFluff 适配器卡片目前只展示 "创建项目配置 / 修改全局设置"两个按钮,不直接展示 SQL 审查当前生效的方言。 用户希望在卡片内以"内置规则"徽章的同款样式,只读展示当前生效方言。 ## 2. 共识(Stage ② 确认) - 呈现形式:只读展示,不可在侧边栏直接修改;修改仍需去 VS Code 设置(`sqlfluff.dialect`)。 - 位置:SQLFluff 卡片徽章行,紧跟"内置规则/项目配置/全局配置"模式徽章之后。 - 样式:徽章配色随**方言实际来源**变化(显式=蓝/全局配置=黄/项目配置=绿/内置=紫),一眼可辨方言由哪个配置控制。 - 展示内容:**当前实际生效方言**(非占位文案),按运行时优先级完整解析。 - 解析深度(Stage ② 最终确认):含全局配置文件解析。 ## 3. 方案 ### 3.1 架构概览 无需架构调整,改动局限在设置面板三件套 + i18n 文案 + 方言解析逻辑集中在 `src/adapters/sqlfluff.ts`,保证展示值与适配器实际使用完全一致。 数据流(复用现有链路): ``` collectAdapterStatus() ──pushConfig()──> postMessage(initConfig.adapterStatus) └─> setupView.js renderAdapters() 渲染方言徽章 ``` 设置变更实时刷新:`setupView.ts:266-275` 已监听 `vscode-code-reviewer.sqlfluff` 变更并触发 `pushConfig()`,方言徽章自动跟随更新,无需额外订阅。 ### 3.2 生效方言解析链(与 sqlfluff.ts 运行时行为逐一对齐) | 优先级 | 来源 | 生效值 | |--------|------|--------| | 1 | 显式设置 `sqlfluff.dialect`(受支持集合内) | 该值(经 `--dialect` 覆盖一切) | | 2 | 全局 `sqlfluff.configFile` | 解析该文件 `[sqlfluff]`/`[tool.sqlfluff]` 段 `dialect`;无该键则 sqlfluff 默认 `ansi` | | 3 | 项目配置(`.sqlfluff`/`setup.cfg`/`tox.ini`/`pep8.ini`/`pyproject.toml`) | 同上,解析 dialect,无则 `ansi` | | 4 | 无任何配置 → 插件内置配置 | `oracle`(SQL/PLSQL 兜底方言) | ### 3.3 文件变更清单 | 文件 | 变更 | 说明 | |------|------|------| | `src/adapters/sqlfluff.ts` | 修改 | `SUPPORTED_DIALECTS` 改 `export const`;`hasProjectSqlfluffConfig` 重构为 `findProjectSqlFluffConfig()`(返回文件路径);新增 `readDialectFromConfigFile()`;`resolveSqlFluffDialect()` 返回 `{ dialect, source }`(source: explicit/global/project/builtin) | | `src/views/setupView.ts` | 修改 | 引入 `resolveSqlFluffDialect`;`AdapterConfigStatus` 加 `sqlfluffDialect?: string` 与 `sqlfluffDialectSource?`;`collectAdapterStatus()` 填充;`pushConfig()` i18n 加 label;新增 `.adapter-badge-explicit` 蓝色徽章样式 | | `src/views/setupView.js` | 修改 | `renderAdapters()` 对 sqlfluff 在模式徽章后追加方言徽章,按 `sqlfluffDialectSource` 映射徽章 class(explicit→蓝/global→warn/ project→ok/builtin→info) | | `src/i18n/messages.ts` | 修改 | 新增 `setup.adapter.sqlfluffDialectLabel` 三语文案 | 无新建、无删除。 ### 3.3.1 方言徽章配色映射 | source | 含义 | 徽章 class | 颜色 | |--------|------|-----------|------| | `explicit` | 显式设置 `sqlfluff.dialect` | `adapter-badge-explicit` | 蓝 `#388bfd` | | `global` | 全局 `sqlfluff.configFile` | `adapter-badge-warn` | 黄 `#d29922` | | `project` | 项目 `.sqlfluff` 等 | `adapter-badge-ok` | 绿 `#3fb950` | | `builtin` | 插件内置默认 | `adapter-badge-info` | 紫 `#8b5cf6` | ### 3.4 关键实现 **sqlfluff.ts** ```ts export const SUPPORTED_DIALECTS = [ 'ansi', ..., 'vertica' ]; function findProjectSqlFluffConfig(workspaceRoot: string): string | undefined { // 由 hasProjectSqlfluffConfig 重构:返回实际命中的文件路径 } function readDialectFromConfigFile(filePath: string): string | undefined { // 行扫描 [sqlfluff] / [tool.sqlfluff] 段内 dialect = X 或 dialect = "X" } export type SqlFluffDialectSource = 'explicit' | 'global' | 'project' | 'builtin'; export interface SqlFluffDialectInfo { dialect: string; source: SqlFluffDialectSource; } export function resolveSqlFluffDialect(workspaceRoot: string): SqlFluffDialectInfo { const explicit = getSqlFluffDialect(); if (explicit && SUPPORTED_DIALECTS.includes(explicit)) { return { dialect: explicit, source: 'explicit' }; } const globalCfg = getSqlFluffConfigFile(); if (globalCfg && globalCfg.trim() !== '') { return { dialect: readDialectFromConfigFile(globalCfg) ?? 'ansi', source: 'global' }; } const projectFile = findProjectSqlFluffConfig(workspaceRoot); if (projectFile) { return { dialect: readDialectFromConfigFile(projectFile) ?? 'ansi', source: 'project' }; } return { dialect: 'oracle', source: 'builtin' }; } ``` **setupView.ts** ```ts import { resolveSqlFluffDialect, type SqlFluffDialectSource } from '../adapters/sqlfluff'; interface AdapterConfigStatus { // ...原有字段 sqlfluffDialect?: string; // 仅 sqlfluff 生效 sqlfluffDialectSource?: SqlFluffDialectSource; } // collectAdapterStatus() sqlfluff 分支: const dialectInfo = id === 'sqlfluff' ? resolveSqlFluffDialect(vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '') : undefined; status.sqlfluffDialect = dialectInfo?.dialect; status.sqlfluffDialectSource = dialectInfo?.source; ``` **setupView.js `renderAdapters()`** ```js var dialectBadgeClassMap = { explicit: 'adapter-badge-explicit', global: 'adapter-badge-warn', project: 'adapter-badge-ok', builtin: 'adapter-badge-info', }; var dialectBadgeClass = dialectBadgeClassMap[a.sqlfluffDialectSource] || 'adapter-badge-info'; var dialectBadge = a.id === 'sqlfluff' ? '' + escapeHtml(i18n.sqlfluffDialectLabel) + ' ' + escapeHtml(a.sqlfluffDialect || '') + '' : ''; ``` **messages.ts 新增 key** | key | zh-CN | en | ja | |-----|-------|-----|-----| | `setup.adapter.sqlfluffDialectLabel` | `方言` | `Dialect` | `方言` | ### 3.5 影响范围 - 仅设置面板 SQLFluff 卡片的 UI 展示,不影响分析执行逻辑、配置优先级、package.json。 - `sqlfluff.dialect` 设置值本身不变;`src/config/linter.ts` 的 `getSqlFluffDialect`/`getSqlFluffConfigFile` 被复用。 - 新增对 `adapters/sqlfluff` 模块的跨层 import(仅常量与纯函数,无副作用)。 ### 3.6 验证方式 1. `npm test`(lint → compile → test)。 2. 手工四态:显式 postgres / 全局配置文件含方言 / 项目 `.sqlfluff` 含方言 / 无配置 → 侧边栏 SQLFluff 卡片分别显示"方言 postgres(蓝)" / 对应值(黄) / 对应值(绿) / "方言 oracle(紫)"。 ## 4. 风险 - "无配置默认 oracle"与"有配置文件但无 dialect → ansi"的差异语义:与运行时行为一致, 展示的是真实生效值,属预期。 - `SUPPORTED_DIALECTS` 与 package.json enum 清单重复维护,本次仅复用代码内既有常量,不新增重复。