3.6 KiB
3.6 KiB
Step 08 — Phase 3: 编排器 Orchestrator
依赖: Step 02~07(所有适配器)
参考设计: §3.6, §4
目标
实现单文件保存触发、多适配器调度、500ms debounce、结果聚合的编排器。
新建文件
| # | 文件 | 说明 |
|---|---|---|
| 1 | src/orchestrator/orchestrator.ts |
Orchestrator 类 |
src/orchestrator/orchestrator.ts
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<string, LinterAdapter> {
const map = new Map<string, LinterAdapter>();
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<StaticAnalysisResult> {
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:
export function debounce<T extends (...args: unknown[]) => unknown>(fn: T, ms: number): (...args: Parameters<T>) => void {
let timer: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
关键逻辑
getAdapterMap(): 建立 language → adapter 的映射表runStaticAnalysis():- 获取文档 languageId
- 查
linters.<language>配置 → 确定使用的适配器 - 调用
adapter.check(document, workingDir) - 收集结果,区分 status
- 返回聚合
StaticAnalysisResult
- 硬编码注册 5 个适配器(工厂函数方式)
- 禁用某语言的配置项为空字符串时不执行检查
验收
- 文件创建完成
npm run compile通过npm run lint通过