【VS Code插件开发】通用功能(二)

通用功能

本篇主要从command(命令)、menus(菜单)、快捷键三个方面讲解,都归于通用功能,详细可参考官方文档

一、Command (命令)

这个例子在入门教程中已经说过,extension.ts文件中,在activate生命周期中使用registerCommand注册命令。

javascript 复制代码
export function activate(context: vscode.ExtensionContext) {
     context.subscriptions.push(vscode.commands.registerCommand('demoPlugin.helloWorld', () => {
		vscode.window.showInformationMessage('Hello World from helloWord!');
	}));
}	

packag.json中注册

javascript 复制代码
 "contributes": {
  "commands": [
       {
        "command": "demoPlugin.helloWorld",
        "title": "Hello World"
       }
   ]   
}      

比如我写一个注释的命令:

1、含有回调参数的命令

比如你想在执行某个命令之后执行某个事件,可以把事件放到registerCommand的第二个参数

javascript 复制代码
//extension.ts
const commandHandler = (name: string = 'world') => {
		console.log(`Hello ${name}!!!`);
	};
	context.subscriptions.push(vscode.commands.registerCommand("demoPlugin.sayHello", commandHandler));
javascript 复制代码
//package.json
 "contributes": {
    "commands": [
      {
        "command": "demoPlugin.sayHello",
        "title": "SayHello"
      }
    ]
}

如图,mac本cmd+shift+p,windows本ctrl+shift+p在开发工具的顶部就会出现下拉框,可以看到SayHello命令就在下拉框中,当我们选择SayHello后就会执行回调函数,可以看到在控制台打印出Hello world

2、控制命令是否显示在命令选项板中

注册了demoPlugin.sayHello命令之后,在某个条件下显示,在某条件下不显示,可以通过commandPalette来进行设置,用when来设置条件。 editorLangId == markdown意思是只有在markdown文件才显示

javascript 复制代码
   "menus": {
      "commandPalette": [
        {
          "command": "demoPlugin.sayHello",
          "when": "editorLangId == markdown"
        }
      ]
    }

3、手动执行命令

executeCommand可以手动执行已注册的命令,该方法允许您以编程方式触发命令并传递可能需要的参数。必须是显式触发了某个操作然后才能手动执行命令。

javascript 复制代码
    vscode.commands.executeCommand('yourcommand', 'params1', 'params2', ...).then(result => {
	console.log('命令结果', result);
});

在插件被激活时,命令会被注册,但命令 demoPlugin.helloWorld 并不会立即执行,除非有其他代码或事件触发了这个命令。例如当用户打开了一个包含支持插件的文件类型的文件或者触发了特定的命令/动作。 所以下面的这种写法,是不会被触发的。

javascript 复制代码
export function activate(context: vscode.ExtensionContext) {context.subscriptions.push(vscode.commands.registerCommand('demoPlugin.helloWorld', () => {
		vscode.window.showInformationMessage('Hello World from helloWord!');
	}));
	vscode.commands.executeCommand('demoPlugin.helloWorld')
}

案例

重新定义注释,设置ctrl+e或者cmd+e触发note命令,然后执行editor.action.addCommentLine

javascript 复制代码
context.subscriptions.push(vscode.commands.registerCommand('demoPlugin.note', () => {
		vscode.commands.executeCommand('editor.action.addCommentLine');
	}));
javascript 复制代码
 "contributes": {
    "commands": [
      {
        "command": "demoPlugin.note",
        "title": "Note"
      }
    ], 
    //快捷键触发
    "keybindings": [
      {
        "command": "demoPlugin.note",
        "key": "ctrl+e",
        "mac": "cmd+e",
        "when": "editorTextFocus"
      }
    ]
  }

4、获取所有命令

getCommands()会异步的获取所有命令。

javascript 复制代码
vscode.commands.getCommands().then(res => {
    console.log('所有命令:',res);
});

5、内置命令

比如这个打开文件夹命令:vscode.openFolder,注册demoPlugin.openFolder,当我们触发openFolder时候,会执行vscode.openFolder内置命令,打开我们指定路径的文件夹。其他的内置命令可参考官网介绍:Built-in Commands

javascript 复制代码
//extension.ts	
context.subscriptions.push(vscode.commands.registerCommand('demoPlugin.openFolder',async () => {
		// vscode.window.showInformationMessage('Hello World from helloWord!');
		const folderUri = vscode.Uri.file('/Users/zbs/dongwan/dist');
		const success = await vscode.commands.executeCommand('vscode.openFolder', folderUri);
		if (success) {
			vscode.window.showInformationMessage('Successfully opened the folder.');
		} else {
			vscode.window.showErrorMessage('Failed to open the folder.');
		}
	}));
javascript 复制代码
//package.json
 { 
        "command": " demoPlugin.openFolder",
        "title": "openFolder",
        "category": "demoPlugin"
      }

6、命令面板菜单项的上下文特定可见性

当在 package.json中注册命令时,它们将自动显示在命令面板**(⇧⌘P)**中。为了更好地控制命令可见性,有commandPalette菜单项。它允许您定义一个when条件来控制命令是否应在命令面板中可见。 下面的案例指:当编辑器中有选中文本(即有选择的文本)时才会显示该菜单项。只有当用户选中了文本时,菜单项才会被显示。

javascript 复制代码
{
  "commands": [
    {
      "command": "extension.sayHello",
      "title": "Hello World"
    }
  ],
  "menus": {
    "commandPalette": [
      {
        "command": "extension.sayHello",
        "when": "editorHasSelection"
      }
    ]
  }
}

二、menus(菜单)

向编辑器或资源管理器提供命令的菜单项。菜单项定义包含选择时应调用的命令以及该项目应显示的条件。后者是通过when子句定义的,该子句使用键绑定when 子句 contexts。 属性command指示选择菜单项时要运行哪个命令。属性submenu指示在此位置呈现哪个子菜单。

下面是一些常见的场景:

  • 全局命令面板 -commandPalette
  • 文件菜单和入门页面中的新建文件项 -file/newFile
  • 资源管理器上下文菜单 -explorer/context
  • 编辑器上下文菜单 -editor/context
  • 编辑器标题菜单栏 -editor/title
  • 编辑器标题上下文菜单 -editor/title/context
  • 调试调用堆栈视图上下文菜单 -debug/callstack/context
  • 调试调用堆栈视图内联操作 -debug/callstack/context组inline
  • 调试变量视图上下文菜单 -debug/variables/context
  • 调试工具栏 -debug/toolBar

1、editor/title

editor/title为编辑器标题菜单栏,设置当resourceLangIdmarkdown时候,即资源(文件)的语言标识为 markdown 时显示该菜单项。

  • editor/title:定义这个菜单出现的位置,位于编辑器标题菜单栏
  • when:控制菜单合适出现
  • command:定义菜单被点击后要执行什么操作
  • alt:定义备用命令,按住alt键打开菜单时将执行对应命令
  • group:定义菜单分组
javascript 复制代码
{
  "contributes": {
    "menus": {
      "editor/title": [
        {
         //用于确定在何时显示这个菜单项。
          "when": "resourceLangId == markdown",
        //菜单项点击后要执行的命令,当用户点击这个菜单项时,会执行 markdown.showPreview 命令。这是 VS Code 默认提供的命令,用于显示 Markdown 预览
          "command": "markdown.showPreview",
          //alt为菜单项的备选命令,如果 markdown.showPreview 命令无法执行,则会尝试执行 markdown.showPreviewToSide 命令。alt 属性允许您为菜单项提供一个备选的命令。当然也可以是其他按键
          "alt": "markdown.showPreviewToSide",
          //菜单项的分组,在编辑器标题菜单中,菜单项通常分组显示
          "group": "navigation"
        }
      ]
    }
  }
}

在不设置上面的属性之前,当文件类型为markdown时,默认有四个菜单

  • Open Changes :文件有哪些改变,会跟之前md文件对比
  • Open Preview to the Side:在侧边窗口预览
  • Split editor right:在右侧新开一个编辑器窗口
  • More Actions:更多 设置"editor/title"属性之后,则会多一个按钮
  • Open Preview:在当前窗口,打开预览(新开一个tab)

2、editor/context

编辑器上下文菜单,即编辑器的右键菜单 ,在编辑器中右键单击时出现的菜单。demoPlugin.helloWorld命令在上面代码中已经有写过。

javascript 复制代码
 "menus": {
      "editor/context": [
        {
          "command": "demoPlugin.helloWorld",
          "group": "navigation"
        }
      ]
 }     

单机右键的时候,我们可以看到helloWorld的选项,如下图:

3、when

when的具体用法可参考官方文档。when可以控制什么时候显示菜单项,但是不仅仅适用于菜单项的控制。部分用法如下:

javascript 复制代码
resourceLangId == javascript:当编辑的文件是js文件时;
resourceFilename == test.js:当当前打开文件名是test.js时;
isLinux、isMac、isWindows:判断当前操作系统;
editorFocus:编辑器具有焦点时;
editorHasSelection:编辑器中有文本被选中时;
view == someViewId:当当前视图ID等于someViewId时;
editorTextFocus:表示当前编辑器是否处于焦点状态(即是否具有输入焦点)。当编辑器具有输入焦点时,用户可以在编辑器中输入和编辑文本。

也可以用在多个条件,可以通过与或非进行组合,比如:

javascript 复制代码
 "when": "debuggersAvailable && !inDebugMode"

对于when子句条件表达式,以下条件运算符对于键绑定很有用:

Operator Symbol Example
Equality == "editorLangId == typescript"
Inequality != "resourceExtname != .js"
Or ||(这里实际是英文竖杠,英文竖杠不显示,所以用中文竖杠显示) "isLinux || isWindows"
And && "textInputFocus && !editorReadonly"
Matches =~ "resourceScheme =~ /^untitled$ ^file$/"

4、group

菜单项可以分组。它们按字典顺序排序,并遵循以下默认值/规则。您可以将菜单项添加到这些组中,或者在其间、下方或上方添加新的菜单项组。

编辑器上下文菜单具有以下默认组:

  • navigation-navigation在任何情况下,团队都是第一位的。
  • 1_modification- 该组紧随其后,包含修改代码的命令。
  • 9_cutcopypaste- 倒数第二个默认组,包含基本编辑命令。
  • z_commands- 最后一个默认组,带有用于打开命令面板的条目。

explorer/context有这些默认组:

navigation : 放在这个组的永远排在最前面 2_workspace :与工作空间操作相关的命令 3_compare :与差异编辑器中的文件比较相关的命令。 4_search :与在搜索视图中搜索相关的命令 5_cutcopypaste :与剪切,复制和粘贴文件相关的命令 7_modification :与修改文件相关的命令

在编辑器选项卡上下文菜单有这些默认组:

1_close - 与关闭编辑器相关的命令。 3_preview - 与固定编辑器相关的命令

在editor/title有这些默认组:

1_diff - 与使用差异编辑器相关的命令 3_open - 与打开编辑器相关的命令 5_close - 与关闭编辑器相关的命令

组内排序

组内的顺序取决于标题或顺序属性。菜单项的组本地顺序是通过附加@到组标识符来指定的,如下所示:

javascript 复制代码
      "editor/context": [
        {
          "command": "demoPlugin.helloWorld",
          "group": "navigation@2"
        },
        {
          "command": "demoPlugin.note",
          "group": "navigation@1"
        }
      ]

可以看到@2Hello World@1Note,按@后的数字进行排序

三、快捷键

快捷键更多描述看官方文档,快捷键操作的时候其实背后是有一套逻辑在运行,这套逻辑与快捷键互相绑定。 下面我们设置了三个快捷键:复制、粘贴、剪切,并且配置了windows和mac两个操作系统,快捷键与command相结合,在触发快捷键时执行命令的操作。

1、复制

目的是获取复制的文本、复制内容的起始行以及当前文件的路径。

javascript 复制代码
  "contributes": {
      "keybindings": [
      {
        "command": "extension.copyCommand",
        "key": "ctrl+c",
        "mac": "cmd+c",
        "when": "editorTextFocus"
      }
    ]
 }

registerTextEditorCommand 注册一个命令时,这个命令将与特定的文本编辑器关联,接收两个参数,一个是命令标识符,一个是命令触发执行的回调。

回调中的参数解释如下

  • 第一个参数是一个 TextEditor 对象,表示与命令关联的文本编辑器
  • 第二个参数是一个 TextEditorEdit 对象,用于对文本进行编辑
javascript 复制代码
vscode.commands.registerTextEditorCommand('extension.copyCommand', (textEditor, edit) => {
		const selection = textEditor.selection;
		// 获取选中的文本
		const selectedText = textEditor.document.getText(selection);
		// 将选中的文本存储到剪贴板
		vscode.env.clipboard.writeText(selectedText);
		const activeTextEditor = vscode.window.activeTextEditor;

		let currentFilePath;
		if (activeTextEditor) {
		//输出当前文件的路径
			currentFilePath = activeTextEditor.document.uri.fsPath;
		}
		// 获取选中文本的起始行和结束行,如果是第一行则line为0,故一般+1
		const startLine = selection.start.line + 1;
		const endLine = selection.end.line + 1;
		console.log(startLine, 'startLine')
		console.log(endLine, 'endLine')
	})

2、粘贴

目的是获取粘贴的内容、粘贴后的起始行以及当前文件的路径。

javascript 复制代码
  "contributes": {
      "keybindings": [
      {
        "command": "extension.pasteCommand",
        "key": "ctrl+v",
        "mac": "cmd+v",
        "when": "editorTextFocus"
      }
    ]
 }
javascript 复制代码
//粘贴
	vscode.commands.registerCommand('extension.pasteCommand', () => {
		const textEditor = vscode.window.activeTextEditor;
		if (!textEditor) {
			return;
		}
		const selections = textEditor.selections;
		const originalStartLine = selections[0].start.line + 1; // 获取粘贴前的开始行号
		const originalEndLine = selections[0].end.line + 1; // 获取粘贴前的结束行号
		// 获取粘贴内容
		vscode.env.clipboard.readText().then((clipboardText) => {
			const edits: any[] = [];
			const filePath = textEditor.document.uri.fsPath;
			selections.forEach((selection) => {
				const startPosition = selection.start;
				const endPosition = selection.end;

				// 创建粘贴操作对应的编辑
				edits.push({
					range: new vscode.Range(startPosition, endPosition),
					newText: clipboardText,
				});
			});
			// 使用 Promise.all 等待所有编辑操作完成
			Promise.all(
				edits.map((edit) =>
					textEditor.edit((editBuilder) => {
						editBuilder.replace(edit.range, edit.newText);
					})
				)
			).then(() => {
				// 获取粘贴后的选择区域信息
				const newSelections = textEditor.selections;
				const newStartLine = newSelections[0].start.line + 1; // 获取粘贴后的开始行号
				const newEndLine = newSelections[0].end.line + 1; // 获取粘贴后的结束行号
			});
		});
	});

3、剪切

目的是获取剪切的文本,并且获取剪切的起始行以及当前文件的路径

javascript 复制代码
  "contributes": {
      "keybindings": [
      {
        "command": "extension.cutCommand",
        "key": "ctrl+x",
        "mac": "cmd+x",
        "when": "editorTextFocus"
      }
    ]
 }
javascript 复制代码
vscode.commands.registerTextEditorCommand('extension.cutCommand', (textEditor, edit) => {
			const selection = textEditor.selection;
			// 获取被剪切的文本
			const cutText = textEditor.document.getText(selection);
			// 删除被剪切的文本
			edit.delete(selection);
			// 将剪切的文本存储到剪切板
			vscode.env.clipboard.writeText(cutText);
			const activeTextEditor = vscode.window.activeTextEditor;
			let currentFilePath;
			if (activeTextEditor) {
				currentFilePath = activeTextEditor.document.uri.fsPath;
				console.log(currentFilePath); // 输出当前文件的路径
			}
			// 获取选中文本的起始行和结束行
			const startLine = selection.start.line + 1;
			const endLine = selection.end.line + 1;
		});
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax