在Visual Studio Code
中命令会触发一系列行为,如果你曾经配置过快捷键,那么你就算是和命令打过交道了,命令经常被插件用于给用户暴露某项功能,或是给UI绑定某些行为,又或是实现内部逻辑
命令的使用
VS Code
包含一系列 内置命令 ,通过这些内置命令你可以和编辑器交互、控制用户界面、执行后台任务,很多的插件通常也是通过命令导出它们的核心功能以便其它插件或使用者复用
编码执行命令
通过 vscode.commands.executeCommand 接口可以编码方式执行命令,以便开发插件的时候可以使用到VS Code
内置的功能或者利用内置的Git
、Markdown
插件。
举例来说,editor.action.addCommentLine
命令会把当前选中的行注释掉
import * as vscode from 'vscode';
function commentLine() {
vscode.commands.executeCommand('editor.action.addCommentLine');
}
复制代码
有些命令会接收一些参数来控制行为,命令可能还会返回结果。例如vscode.executeDefinitionProvider
命令,会接收一个文档URI
和位置作为参数,返回一个定义列表
import * as vscode from 'vscode';
async function printDefinitionsForActiveEditor() {
const activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
return;
}
const definitions = await vscode.commands.executeCommand<vscode.Location[]>(
'vscode.executeDefinitionProvider',
activeEditor.document.uri,
activeEditor.selection.active
);
for (const definition of definitions) {
console.log(definition);
}
}
复制代码
我们可以从下面两个地方找到有效的命令:
命令URIs
命令URI是若干链接,用于执行给定的命令,可以用在悬浮文案、webview中的的可点击连接上。一个命令URI用command
协议,后面接着命令名称,例如对于editor.action.addCommentLine
命令,它的命令URI是command:editor.action.addCommentLine
。我们这里有个hover provider
,展示了在链接中使用命令URI:
const vscode = require('vscode');
function activate(context) {
context.subscriptions.push(
vscode.languages.registerHoverProvider("javascript", {
provideHover: (document, position) => {
const commentCommandUri = vscode.Uri.parse(`command:editor.action.addCommentLine`);
const contents = new vscode.MarkdownString(`[Add comment](${commentCommandUri})`);
// To enable command URIs in Markdown content, you must set the `isTrusted` flag.
// When creating trusted Markdown string, make sure to properly sanitize all the
// input content so that only expected command URIs can be executed
contents.isTrusted = true;
return new vscode.Hover(contents);
},
})
);
}
复制代码
当悬停时显示一个链接:
点击该链接会执行editor.action.addCommentLine
命令,将当前行变为注释
对于需要给命令URI以JSON形式传递若干参数的情况,需要将参数URI编码,例如:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
vscode.languages.registerHoverProvider(
'javascript',
new (class implements vscode.HoverProvider {
provideHover(
document: vscode.TextDocument,
_position: vscode.Position,
_token: vscode.CancellationToken
): vscode.ProviderResult<vscode.Hover> {
const args = [{ resourceUri: document.uri }];
const stageCommandUri = vscode.Uri.parse(
`command:git.stage?${encodeURIComponent(JSON.stringify(args))}`
);
const contents = new vscode.MarkdownString(`[Stage file](${stageCommandUri})`);
contents.isTrusted = true;
return new vscode.Hover(contents);
}
})()
);
}
复制代码
你可以通过在WebviewOptions
中设置enableCommandUris
来实现在webview中使用命令URI
创建命令
注册新命令
vscode.commands.registerCommand 方法在插件中负责将一个命令ID和处理函数绑定到一起:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const command = 'myExtension.sayHello';
const commandHandler = (name: string = 'world') => {
console.log(`Hello ${name}!!!`);
};
context.subscriptions.push(vscode.commands.registerCommand(command, commandHandler));
}
复制代码
commandHandler
函数将会在myExtension.sayHello
命令执行时(如通过executeCommand
接口、界面上的操作或通过快捷键)被触发
创建一个用户可用的命令
vscode.commands.registerCommand 方法仅能将命令ID和处理函数绑定,如果想让用户在Command Palette
里使用这么个命令,还需要在package.json
里设置“命令贡献”(command contribution):
{
"contributes": {
"commands": [
{
"command": "myExtension.sayHello",
"title": "Say Hello"
}
]
}
}
复制代码
“命令贡献”(command contribution)会告知VS Code
当前的插件贡献出了一个命令:
当然,我们还需要执行registerCommand
方法将命令ID和处理函数绑定起来,这就意味着如果用户选中了myExtension.sayHello
命令,而插件还没有激活(activated),那么注册的处理函数并不会执行,为了此类事情的发生,我们还需要注册onCommand activationEvent
:
{
"activationEvents": ["onCommand:myExtension.sayHello"]
}
复制代码
现在,当用户运行myExtension.sayHello
命令时,插件将会被激活并执行registerCommand
方法将命令ID和处理函数绑定在一起。
如果出现以下情况,你必须注册onCommand activationEvent
:
- 命令可以从
Command Palette
被唤醒 - 命令可以被快捷键被唤醒
- 命令可以从界面被唤醒,如
title bar
- 被其它插件以API形式调用
控制命令在Command Palette里的显示
通常情况下我们在package.json
里设置contributes.commands
来控制命令在Command Palette
里的展示,不过有时候我们希望命令只在特定场景下作展示,比如只在特定语言或指定配置下展示命令给用户,这类需求我们可以用contributes.menus.commandPalette
来实现,它会控制一个命令何时在Command Palette
中显示:
{
"contributes": {
"commands": [{
"command": "extension.sayHello",
"title": "Hello World"
}],
"menus": {
"commandPalette": [{
"command": "extension.sayHello",
"when": "editorLangId == markdown"
}]
}
}
}
复制代码
现在Hello World
命令只会当用户在一个Markdown
中时才会在Command Palette
中显示出来,关于when
的取值,可以参见 when clause contexts
命令的Enablement属性
命令支持通过contributes.commands.enablement
字段控制命令的可用性,它的取值和上文的when
属性一致,其实二者的语义上有一定的重叠,when
经常用来避免菜单中充满了不可用项,防止菜单的凌乱(在VS Code
插件开发实践中这是被推荐的,比如我们实现一个分析JavaScript正则表达式的命令,用 when 来控制该命令只在当前是JavaScript文件时才展示,然后用 enablement 来控制只有鼠标悬浮在一个正则表达式上时才启用)
另外,VS Code
在命令的显示上,when
和enablement
有所区别,在编辑器或explorer
的上下文菜单中会显示enablement/disablement
的菜单项,但是在Command Palette
中会过滤掉
自定义when、enablement取值条件
when
和enablement
字段中的取值条件 when clause contexts 是可以通过vscode.commands.executeCommand
方法自定义的:
vscode.commands.executeCommand('setContext', 'ext.supportedFolders', [
'test',
'foo',
'bar'
]);
vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', false);
// package.json
{
"menus": {
"explorer/context": [{
"command": "ext.doSpecial",
"when": "explorerResourceIsFolder && resourceFilename in ext:supportedFolders"
}],
"commandPalette": [{
"command": "extension.sayHello",
"when": "jsonOutlineEnabled"
}]
}
}
复制代码
注意,如果执行executeCommand
时条件名称是ext.supportedFolders
,则在package.json
中应该改为ext:supportedFolders
的形式