VSCode扩展开发:如何获取用户选中的CompletionItem信息?
解决VSCode扩展中监听补全项选中事件的问题
我明白你遇到的痛点了——resolveCompletionItem的触发时机确实容易让人误解,它是在补全列表渲染前就被调用,用来提前完善补全项的信息(比如添加上下文相关的文档),而不是等用户真正选中某一项时才触发。要精准捕获用户选中并插入补全项的动作,这里有两种可靠的实现方案:
方案一:利用CompletionItem的command属性(推荐)
这是VSCode官方设计的、最直接的补全项选中回调方式。我们可以给每个补全项绑定一个自定义命令,当用户选中该补全项时,会自动执行这个命令,同时把对应的CompletionItem作为参数传递进去。
import * as vscode from 'vscode'; class FuncCompletionProvider implements vscode.CompletionItemProvider { provideCompletionItems( document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext ): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> { const completionItems: vscode.CompletionItem[] = []; // 创建示例补全项 const funcItem = new vscode.CompletionItem('customFunc', vscode.CompletionItemKind.Function); funcItem.insertText = 'customFunc(${1:param})'; funcItem.detail = '自定义工具函数'; // 绑定选中后执行的命令,把当前补全项作为参数传递 funcItem.command = { command: 'extension.onCompletionSelected', title: '处理补全项选中事件', arguments: [funcItem] }; completionItems.push(funcItem); return completionItems; } // 保留resolveCompletionItem用于预处理(比如添加文档),但它不是选中触发 resolveCompletionItem( item: vscode.CompletionItem, token: vscode.CancellationToken ): vscode.ProviderResult<vscode.CompletionItem> { item.documentation = '这是一个用于XXX场景的自定义函数'; return item; } } export function activate(context: vscode.ExtensionContext) { // 注册补全提供者 const providerDisposable = vscode.languages.registerCompletionItemProvider( { scheme: 'file', language: 'typescript' }, // 替换为你的目标语言/选择器 new FuncCompletionProvider(), '.' ); // 注册处理选中事件的自定义命令 const commandDisposable = vscode.commands.registerCommand( 'extension.onCompletionSelected', (selectedItem: vscode.CompletionItem) => { // 这里就是用户选中补全项后的回调逻辑 console.log('选中的补全项信息:', selectedItem); console.log('补全项标签:', selectedItem.label); console.log('插入文本:', selectedItem.insertText); // 在这里可以执行任何你需要的操作,比如统计用户使用习惯、触发后续逻辑等 } ); context.subscriptions.push(providerDisposable, commandDisposable); }
方案二:监听文本编辑器选择变化(备选)
如果因为某些原因无法使用command属性,也可以通过监听编辑器的选择变化事件,结合补全项的缓存来匹配用户选中的内容。这种方法需要处理更多边界情况,但适合特殊场景:
import * as vscode from 'vscode'; class FuncCompletionProvider implements vscode.CompletionItemProvider { // 缓存最近一次提供的补全项列表,用于后续匹配 public lastProvidedItems: vscode.CompletionItem[] | undefined; provideCompletionItems( document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext ): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> { const items: vscode.CompletionItem[] = []; // 创建补全项逻辑... const item = new vscode.CompletionItem('anotherFunc', vscode.CompletionItemKind.Function); item.insertText = 'anotherFunc()'; items.push(item); // 缓存补全项 this.lastProvidedItems = items; return items; } } export function activate(context: vscode.ExtensionContext) { const provider = new FuncCompletionProvider(); const providerDisposable = vscode.languages.registerCompletionItemProvider( { scheme: 'file', language: 'typescript' }, provider, '.' ); // 监听编辑器选择变化事件 const selectionDisposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { // 过滤掉非命令触发的选择变化(比如用户手动选中文本) if (event.kind === vscode.TextEditorSelectionChangeKind.Command) { const editor = event.textEditor; const position = editor.selection.active; const doc = editor.document; // 获取光标位置的单词,匹配缓存的补全项 const wordRange = doc.getWordRangeAtPosition(position); if (wordRange && provider.lastProvidedItems) { const insertedText = doc.getText(wordRange); const matchedItem = provider.lastProvidedItems.find( item => item.insertText === insertedText ); if (matchedItem) { console.log('选中的补全项(通过选择变化):', matchedItem); } } } }); context.subscriptions.push(providerDisposable, selectionDisposable); }
总结
- 方案一(
command属性)是官方推荐的方式,逻辑清晰、可靠性高,适合大多数场景。 - 方案二适合无法使用命令的特殊场景,但需要处理文本匹配、缓存失效等边界问题。
内容的提问来源于stack exchange,提问作者Godon
相关产品推荐
相关产品推荐

