如何在VSCode扩展API中监听右键菜单打开并动态修改菜单项
VSCode扩展实现动态上下文菜单(右键触发前执行检查)
方法一:通过上下文变量控制菜单项显隐(推荐)
适合基于检查结果显示/隐藏预定义菜单项,实现简单高效。
1. 在package.json中注册基础菜单项
定义菜单项时,使用自定义上下文变量作为when条件,控制菜单项是否显示:
{ "contributes": { "menus": { "editor/context": [ { "command": "myExtension.doAction1", "when": "editorTextFocus && myExtension.canShowAction1", "group": "myGroup@1" }, { "command": "myExtension.doAction2", "when": "editorTextFocus && myExtension.canShowAction2", "group": "myGroup@2" } ] }, "commands": [ { "command": "myExtension.doAction1", "title": "执行操作1" }, { "command": "myExtension.doAction2", "title": "执行操作2" } ] } }
2. 监听菜单触发事件,执行检查并更新上下文
在扩展激活时,监听菜单显示前的事件,执行自定义检查逻辑后,通过setContext更新上下文变量,从而控制菜单项显隐:
import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // 监听上下文菜单即将显示的事件 const menuListener = vscode.window.onDidShowContextMenu(async () => { const editor = vscode.window.activeTextEditor; let canShowAction1 = false; let canShowAction2 = false; if (editor) { // 示例检查逻辑:根据选中内容长度判断显示哪个菜单项 const selectedText = editor.document.getText(editor.selection); canShowAction1 = selectedText.length > 10; canShowAction2 = selectedText.length <= 10; } // 更新上下文变量,触发菜单条件重新计算 await vscode.commands.executeCommand('setContext', 'myExtension.canShowAction1', canShowAction1); await vscode.commands.executeCommand('setContext', 'myExtension.canShowAction2', canShowAction2); }); context.subscriptions.push(menuListener); // 注册命令具体实现 context.subscriptions.push( vscode.commands.registerCommand('myExtension.doAction1', () => { vscode.window.showInformationMessage('执行操作1'); }) ); context.subscriptions.push( vscode.commands.registerCommand('myExtension.doAction2', () => { vscode.window.showInformationMessage('执行操作2'); }) ); }
方法二:动态注册全新菜单项
适合根据检查结果生成完全自定义的菜单项,而非显隐预定义项。
实现代码
import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { let dynamicMenuDisposables: vscode.Disposable[] = []; const menuListener = vscode.window.onDidShowContextMenu(async () => { // 清理上一次注册的动态菜单项,避免重复 dynamicMenuDisposables.forEach(dispose => dispose.dispose()); dynamicMenuDisposables = []; const editor = vscode.window.activeTextEditor; if (!editor) return; // 执行自定义检查逻辑 const isJSFile = editor.document.languageId === 'javascript'; const hasConsoleLog = editor.document.getText().includes('console.log'); // 根据检查结果动态创建菜单项 if (isJSFile && hasConsoleLog) { // 注册命令 const removeLogCmd = vscode.commands.registerCommand('myExtension.removeConsoleLog', () => { editor.edit(editBuilder => { const newText = editor.document.getText().replace(/console\.log\(.*?\);?/g, ''); editBuilder.replace(new vscode.Range(0, 0, editor.document.lineCount - 1, Infinity), newText); }); }); // 注册菜单项到右键菜单 const menuItem = vscode.menus.create('editor/context', { command: 'myExtension.removeConsoleLog', title: '移除所有console.log', group: 'myGroup@1' }); dynamicMenuDisposables.push(removeLogCmd, menuItem); } }); context.subscriptions.push(menuListener); }
注意事项
- 检查逻辑尽量轻量化,避免菜单弹出延迟;异步检查需处理好时序,防止菜单显示后才完成更新。
- 动态注册菜单项时,必须在每次菜单触发前清理旧的注册,避免重复创建。
内容的提问来源于stack exchange,提问作者Nikolai Kushpela
相关产品推荐
相关产品推荐

