docs: upload code files and config

This commit is contained in:
Developer
2026-07-13 18:41:33 +08:00
parent 6a64aece24
commit cafe67db6d
38 changed files with 7448 additions and 126 deletions
@@ -0,0 +1,212 @@
# Step 07 — Phase 2.6: JSP 适配器
**依赖**: Step 03, 04, 06(需要 PMD / ESLint / Stylelint 适配器)
**参考设计**: §3.5
## 目标
实现 JSP 组合适配器。将 JSP 文件拆分后分发检查,合并结果。
## 新建文件
| # | 文件 | 说明 |
|---|------|------|
| 1 | `src/jsp/jsp-extractor.ts` | JSP 内嵌代码块提取器 |
| 2 | `src/adapters/jsp.ts` | `JspAdapter`(组合适配器) |
---
## 1. `src/jsp/jsp-extractor.ts`
```typescript
export interface JspSection {
language: 'javascript' | 'css' | 'java';
code: string;
lineOffset: number;
sourceStart: number;
sourceEnd: number;
}
export function extractJspSections(content: string): JspSection[] {
const sections: JspSection[] = [];
const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script\s*>/gi;
let match: RegExpExecArray | null;
while ((match = scriptRegex.exec(content)) !== null) {
const code = match[1];
const beforeMatch = content.substring(0, match.index);
const lineOffset = beforeMatch.split('\n').length - 1;
sections.push({
language: 'javascript',
code,
lineOffset,
sourceStart: match.index,
sourceEnd: match.index + match[0].length,
});
}
const styleRegex = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi;
while ((match = styleRegex.exec(content)) !== null) {
const code = match[1];
const beforeMatch = content.substring(0, match.index);
const lineOffset = beforeMatch.split('\n').length - 1;
sections.push({
language: 'css',
code,
lineOffset,
sourceStart: match.index,
sourceEnd: match.index + match[0].length,
});
}
const scriptletRegex = /<%=?([\s\S]*?)%>/g;
while ((match = scriptletRegex.exec(content)) !== null) {
const code = match[1];
const beforeMatch = content.substring(0, match.index);
const lineOffset = beforeMatch.split('\n').length - 1;
sections.push({
language: 'java',
code,
lineOffset,
sourceStart: match.index,
sourceEnd: match.index + match[0].length,
});
}
return sections;
}
```
---
## 2. `src/adapters/jsp.ts`
```typescript
import * as vscode from 'vscode';
import { LinterAdapter, LinterDiagnostic, AdapterResult } from '../types';
import { PmdAdapter } from './pmd';
import { ESLintAdapter } from './eslint';
import { StylelintAdapter } from './stylelint';
import { extractJspSections, JspSection } from '../jsp/jsp-extractor';
import { getLinterConfig } from '../config';
export class JspAdapter implements LinterAdapter {
id = 'jsp';
supportedLanguages = ['jsp'];
private pmdAdapter = new PmdAdapter();
private eslintAdapter = new ESLintAdapter();
private stylelintAdapter = new StylelintAdapter();
async check(document: vscode.TextDocument, workingDir: string): Promise<AdapterResult> {
const allDiagnostics: LinterDiagnostic[] = [];
const errors: string[] = [];
const config = getLinterConfig();
const jsEnabled = config.languageMap.javascript !== '';
const cssEnabled = config.languageMap.css !== '';
const javaEnabled = config.languageMap.java !== '';
const pmdResult = await this.pmdAdapter.check(document, workingDir);
allDiagnostics.push(...pmdResult.diagnostics);
if (pmdResult.status !== 'ok') {
errors.push(`PMD: ${pmdResult.errorMessage ?? pmdResult.status}`);
}
const sections = extractJspSections(document.getText());
for (const section of sections) {
const isEnabled = (section.language === 'javascript' && jsEnabled)
|| (section.language === 'css' && cssEnabled)
|| (section.language === 'java' && javaEnabled);
if (!isEnabled) { continue; }
const adapter = this.getAdapter(section.language);
if (!adapter) { continue; }
try {
const virtualDoc = await vscode.workspace.openTextDocument({
content: section.code,
language: section.language,
});
const result = await adapter.check(virtualDoc, workingDir);
for (const diag of result.diagnostics) {
const adjustedRange = new vscode.Range(
diag.range.start.line + section.lineOffset,
diag.range.start.character,
diag.range.end.line + section.lineOffset,
diag.range.end.character,
);
allDiagnostics.push({ ...diag, range: adjustedRange });
}
if (result.status !== 'ok') {
errors.push(`${section.language}: ${result.errorMessage ?? result.status}`);
}
} catch (err) {
errors.push(`${section.language}: ${err instanceof Error ? err.message : String(err)}`);
}
}
const hasErrors = errors.length > 0;
const hasUnavailable = errors.some(e => e.includes('未安装') || e.includes('tool-unavailable'));
return {
diagnostics: allDiagnostics,
status: hasErrors ? (hasUnavailable ? 'tool-unavailable' : 'execution-failed') : 'ok',
errorMessage: errors.join('; '),
};
}
private getAdapter(language: string): LinterAdapter | null {
switch (language) {
case 'javascript': return this.eslintAdapter;
case 'css': return this.stylelintAdapter;
case 'java': return this.pmdAdapter;
default: return null;
}
}
isAvailable(): boolean {
return true;
}
}
```
---
## 关键逻辑
**JspAdapter.check() 三步流程**:
```
1. 调用 PmdAdapter.check(document) → JSP 规范检查
2. extractJspSections(document.getText()) → 提取内嵌代码块
3. 对每个 section:
a. 按 language 选择对应适配器(ESLint/Stylelint/PMD
b. 创建虚拟文档 (vscode.workspace.openTextDocument)
c. 调用 adapter.check(virtualDoc)
d. 修正行号偏移 (section.lineOffset)
4. 合并所有结果
```
**提取器正则规则**:
| 代码块类型 | 正则 | 目标适配器 |
|-----------|------|-----------|
| `<script>` | `/<script\b[^>]*>([\s\S]*?)<\/script\s*>/gi` | ESLint |
| `<style>` | `/<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi` | Stylelint |
| `<%=? %>` | `/<%=?([\s\S]*?)%>/g` | PMD |
- 行号偏移修正:`lineOffset` 从提取位置之前的换行符数计算
- 根据 `linters.<language>` 配置决定是否启用对应子适配器
- 组合状态:任一子适配器不可用则标记 `tool-unavailable`
---
## 验收
- [ ] 2 个文件创建完成
- [ ] `npm run compile` 通过
- [ ] `npm run lint` 通过