如何为VSCode中JSDoc代码块启用IntelliSense功能?
为JSDoc代码块实现IntelliSense功能的可行方案
1. 核心实现思路
借助VS Code的vscode.languages系列API,结合TypeScript语言服务能力,解析JSDoc内的代码块:
- 先通过文本分析定位JSDoc中的代码块(识别
```jsx和```标记),提取完整代码片段。 - 将提取的代码包装成临时TypeScript/JSX文件,用TypeScript语言服务生成补全建议、语法错误和类型诊断信息。
- 把诊断信息映射回原文档的对应位置,实现错误高亮;补全请求触发时,返回临时文档生成的补全项。
2. 复用内置TypeScript语言服务的核心代码
直接调用VS Code内置的TypeScript语言服务实例,创建虚拟文件处理JSDoc代码块:
import * as vscode from 'vscode'; import * as ts from 'typescript'; export function activate(context: vscode.ExtensionContext) { // 注册补全提供者 context.subscriptions.push( vscode.languages.registerCompletionItemProvider('typescript', { async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { const offset = document.offsetAt(position); const docText = document.getText(); // 判断当前位置是否在JSDoc代码块内 const prevCodeBlockStart = docText.lastIndexOf('```jsx', offset); const nextCodeBlockEnd = docText.indexOf('```', offset); const isInJsdocCodeBlock = prevCodeBlockStart !== -1 && nextCodeBlockEnd !== -1 && prevCodeBlockStart < offset && offset < nextCodeBlockEnd; if (!isInJsdocCodeBlock) return []; // 提取JSDoc代码块内容 const blockStart = prevCodeBlockStart + 6; // 跳过"```jsx" const jsdocCode = docText.slice(blockStart, nextCodeBlockEnd).trim(); // 创建虚拟文件 const virtualFile = { fileName: 'virtual-jsdoc.tsx', text: jsdocCode }; // 配置TypeScript语言服务宿主 const serviceHost: ts.LanguageServiceHost = { getScriptFileNames: () => [virtualFile.fileName], getScriptVersion: () => '1', getScriptSnapshot: (fileName) => { return fileName === virtualFile.fileName ? ts.ScriptSnapshot.fromString(virtualFile.text) : undefined; }, getCurrentDirectory: () => vscode.workspace.rootPath || '', getCompilationSettings: () => ({ target: ts.ScriptTarget.Latest, jsx: ts.JsxEmit.React }), getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options) }; const languageService = ts.createLanguageService(serviceHost); // 计算虚拟文件中的光标偏移 const virtualOffset = offset - blockStart; // 获取补全建议 const completions = languageService.getCompletionsAtPosition( virtualFile.fileName, virtualOffset ); // 转换为VS Code兼容的补全项 return completions?.entries.map(entry => { const item = new vscode.CompletionItem(entry.name); item.kind = mapTsCompletionKindToVsCode(entry.kind); item.detail = entry.documentation; return item; }) || []; } }) ); // 注册诊断提供者实现语法/类型校验 context.subscriptions.push( vscode.languages.registerDiagnosticProvider('typescript', { provideDiagnostics(document: vscode.TextDocument) { const diagnostics: vscode.Diagnostic[] = []; const docText = document.getText(); // 提取所有JSDoc代码块 const codeBlockRegex = /```jsx([\s\S]*?)```/g; let match; while ((match = codeBlockRegex.exec(docText)) !== null) { const blockStartOffset = match.index + 6; const blockCode = match[1].trim(); // 创建虚拟文件 const virtualFile = { fileName: 'virtual-jsdoc.tsx', text: blockCode }; const serviceHost = createServiceHost(virtualFile); const languageService = ts.createLanguageService(serviceHost); const tsDiagnostics = languageService.getSemanticDiagnostics(virtualFile.fileName); // 将TypeScript诊断映射到原文档位置 tsDiagnostics.forEach(tsDiag => { const start = document.positionAt(blockStartOffset + tsDiag.start); const end = document.positionAt(blockStartOffset + tsDiag.start + tsDiag.length); const range = new vscode.Range(start, end); const diagnostic = new vscode.Diagnostic( range, tsDiag.messageText.toString(), mapTsDiagnosticSeverityToVsCode(tsDiag.category) ); diagnostics.push(diagnostic); }); } return diagnostics; } }) ); } // 辅助函数:映射TypeScript补全类型到VS Code function mapTsCompletionKindToVsCode(kind: ts.ScriptElementKind): vscode.CompletionItemKind { switch(kind) { case ts.ScriptElementKind.functionElement: return vscode.CompletionItemKind.Function; case ts.ScriptElementKind.variableElement: return vscode.CompletionItemKind.Variable; case ts.ScriptElementKind.classElement: return vscode.CompletionItemKind.Class; default: return vscode.CompletionItemKind.Text; } } // 辅助函数:映射TypeScript诊断级别到VS Code function mapTsDiagnosticSeverityToVsCode(category: ts.DiagnosticCategory): vscode.DiagnosticSeverity { switch(category) { case ts.DiagnosticCategory.Error: return vscode.DiagnosticSeverity.Error; case ts.DiagnosticCategory.Warning: return vscode.DiagnosticSeverity.Warning; default: return vscode.DiagnosticSeverity.Information; } } // 辅助函数:创建语言服务宿主 function createServiceHost(virtualFile: { fileName: string; text: string }): ts.LanguageServiceHost { return { getScriptFileNames: () => [virtualFile.fileName], getScriptVersion: () => '1', getScriptSnapshot: (fileName) => { return fileName === virtualFile.fileName ? ts.ScriptSnapshot.fromString(virtualFile.text) : undefined; }, getCurrentDirectory: () => vscode.workspace.rootPath || '', getCompilationSettings: () => ({ target: ts.ScriptTarget.Latest, jsx: ts.JsxEmit.React }), getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options) }; }
3. 关键注意事项
- 位置映射:必须准确计算JSDoc代码块在原文档中的起始偏移,将虚拟文件的诊断、补全位置转换为原文档的对应位置,这是功能生效的核心。
- 性能优化:避免每次请求都重新创建语言服务实例,可以根据代码块内容的哈希值缓存服务,仅在内容变化时更新。
- 多语言支持:如果需要适配js、ts、vue等多种代码块,可根据代码块的标记(如
```ts)调整虚拟文件的编译配置。
内容的提问来源于stack exchange,提问作者termosa
相关产品推荐
相关产品推荐

