# Step 08 — Phase 3: 编排器 Orchestrator **依赖**: Step 02~07(所有适配器) **参考设计**: §3.6, §4 ## 目标 实现单文件保存触发、多适配器调度、500ms debounce、结果聚合的编排器。 ## 新建文件 | # | 文件 | 说明 | |---|------|------| | 1 | `src/orchestrator/orchestrator.ts` | `Orchestrator` 类 | --- ## `src/orchestrator/orchestrator.ts` ```typescript import * as vscode from 'vscode'; import { LinterAdapter, LinterDiagnostic, AdapterResult } from '../types'; import { getLinterConfig } from '../config'; import { ESLintAdapter } from '../adapters/eslint'; import { PmdAdapter } from '../adapters/pmd'; import { StylelintAdapter } from '../adapters/stylelint'; import { SqlLintAdapter } from '../adapters/sql-lint'; import { JspAdapter } from '../adapters/jsp'; export interface StaticAnalysisResult { diagnostics: LinterDiagnostic[]; errors: string[]; adapterIds: string[]; duration: number; } export class Orchestrator { private adapters: LinterAdapter[]; constructor() { this.adapters = [ new ESLintAdapter(), new PmdAdapter(), new StylelintAdapter(), new SqlLintAdapter(), new JspAdapter(), ]; } getAdapterMap(): Map { const map = new Map(); for (const adapter of this.adapters) { for (const lang of adapter.supportedLanguages) { map.set(lang, adapter); } } return map; } async runStaticAnalysis( document: vscode.TextDocument, workingDir: string ): Promise { const startTime = Date.now(); const config = getLinterConfig(); const languageId = document.languageId; const selectedLinter = config.languageMap[languageId]; if (!selectedLinter || selectedLinter === '') { return { diagnostics: [], errors: [], adapterIds: [], duration: 0 }; } const adapter = this.adapters.find(a => a.id === selectedLinter); if (!adapter) { return { diagnostics: [], errors: [`未找到适配器: ${selectedLinter}`], adapterIds: [], duration: Date.now() - startTime, }; } const result = await adapter.check(document, workingDir); const errors: string[] = []; if (result.status !== 'ok') { errors.push(`[${adapter.id}] ${result.errorMessage ?? result.status}`); } return { diagnostics: result.diagnostics, errors, adapterIds: [adapter.id], duration: Date.now() - startTime, }; } getAdaptersByIds(ids: string[]): LinterAdapter[] { return ids.map(id => this.adapters.find(a => a.id === id)).filter(Boolean) as LinterAdapter[]; } } ``` --- ## debounce 工具函数 在 `src/orchestrator/orchestrator.ts` 中或另外创建 `src/utils/debounce.ts`: ```typescript export function debounce unknown>(fn: T, ms: number): (...args: Parameters) => void { let timer: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); }; } ``` --- ## 关键逻辑 - `getAdapterMap()`: 建立 language → adapter 的映射表 - `runStaticAnalysis()`: 1. 获取文档 languageId 2. 查 `linters.` 配置 → 确定使用的适配器 3. 调用 `adapter.check(document, workingDir)` 4. 收集结果,区分 status 5. 返回聚合 `StaticAnalysisResult` - 硬编码注册 5 个适配器(工厂函数方式) - 禁用某语言的配置项为空字符串时不执行检查 --- ## 验收 - [ ] 文件创建完成 - [ ] `npm run compile` 通过 - [ ] `npm run lint` 通过