在VS Code中并行运行SCIP自定义补全与TS语言服务的方案问询
解决方案:利用VS Code的
isIncomplete属性实现并行补全展示 要实现先展示SCIP的快速补全,再合并TS语言服务的结果,核心是利用VS Code的CompletionList.isIncomplete属性,结合异步更新补全列表的机制。以下是具体实现思路和代码示例:
核心原理
VS Code允许补全提供者返回标记为isIncomplete: true的CompletionList,此时编辑器会先展示当前返回的补全项,同时保持补全菜单处于"加载中"状态,等待后续的补全更新。你可以利用这一点:
- 先快速返回SCIP的补全结果,标记为
isIncomplete: true - 在后台异步获取TS的补全结果
- 合并两者结果后,更新补全列表并标记为
isIncomplete: false
具体实现步骤
1. 注册SCIP补全提供者
确保提供者的触发字符与TS语言服务一致(如., :等),保证能同时触发两者的补全逻辑。
2. 异步返回并更新补全列表
在provideCompletionItems方法中,先返回SCIP的结果,再异步获取TS结果并合并更新。
import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { const scipCompletionProvider = vscode.languages.registerCompletionItemProvider( ['typescript', 'typescriptreact'], { async provideCompletionItems(doc: vscode.TextDocument, pos: vscode.Position) { // 1. 快速查询SCIP索引,获取补全项 const scipItems = await fetchSCIPCompletions(doc, pos); // 2. 返回标记为未完成的补全列表,让VS Code先展示SCIP结果 const initialList = new vscode.CompletionList(scipItems, true); // 3. 异步获取TS语言服务的补全结果,不阻塞初始返回 process.nextTick(async () => { try { // 获取TS的补全结果(模拟TS服务的触发条件) const triggerChar = getTriggerCharacter(doc, pos); const tsItems = await vscode.languages.getCompletionItems(doc, pos, { triggerCharacter: triggerChar }); // 4. 合并补全项,去重(SCIP项优先级高于TS) const mergedItems = mergeCompletions(scipItems, tsItems); // 5. 更新补全列表,标记为已完成 vscode.languages.setTextDocumentCompletionItems(doc, new vscode.CompletionList(mergedItems, false)); } catch (err) { // 处理TS服务异常,保持SCIP结果并标记为完成 vscode.languages.setTextDocumentCompletionItems(doc, new vscode.CompletionList(scipItems, false)); } }); return initialList; } }, '.', ':' // 与TS一致的触发字符 ); context.subscriptions.push(scipCompletionProvider); } // 模拟SCIP索引查询逻辑 async function fetchSCIPCompletions(doc: vscode.TextDocument, pos: vscode.Position): Promise<vscode.CompletionItem[]> { // 替换为你的SCIP索引查询代码 return [ new vscode.CompletionItem('SCIPGeneratedFunction', vscode.CompletionItemKind.Function), new vscode.CompletionItem('SCIPGeneratedClass', vscode.CompletionItemKind.Class) ]; } // 获取触发补全的字符(用于匹配TS服务的触发条件) function getTriggerCharacter(doc: vscode.TextDocument, pos: vscode.Position): string | undefined { const prevRange = new vscode.Range(pos.translate(0, -1), pos); const prevChar = doc.getText(prevRange); return ['/', '.', ':'].includes(prevChar) ? prevChar : undefined; } // 合并补全项,去重逻辑可自定义 function mergeCompletions(scipItems: vscode.CompletionItem[], tsItems: vscode.CompletionItem[]): vscode.CompletionItem[] { const itemMap = new Map<string, vscode.CompletionItem>(); // 先添加SCIP项,确保优先级 scipItems.forEach(item => itemMap.set(item.label.toString(), item)); // 添加TS项,不覆盖已有的SCIP项 tsItems.forEach(item => { const key = item.label.toString(); if (!itemMap.has(key)) itemMap.set(key, item); }); return Array.from(itemMap.values()); }
为什么之前的方案无效?
- 直接使用
CompletionItemProvider合并:如果你的实现是同步等待TS结果后再返回,自然会被TS的慢响应阻塞,无法先展示SCIP结果。 - TS语言服务插件:插件运行在TS语言服务的内部流程中,必须等待TS自身的补全逻辑完成后才能返回结果,无法实现真正的并行。
类似实践案例
- GitHub Copilot:先返回AI生成的补全结果,后台异步获取本地语言服务的补全,合并后更新补全菜单,用户会先看到AI建议,后续本地补全逐步加载。
- Tabnine:同样基于
isIncomplete机制,先展示AI补全,再合并本地语言服务的结果,实现快结果优先的体验。
内容的提问来源于stack exchange,提问作者Akshay Naik
相关产品推荐
相关产品推荐

