VS Code插件开发教程(5) Command

Visual Studio Code中命令会触发一系列行为,如果你曾经配置过快捷键,那么你就算是和命令打过交道了,命令经常被插件用于给用户暴露某项功能,或是给UI绑定某些行为,又或是实现内部逻辑

命令的使用

VS Code包含一系列 内置命令 ,通过这些内置命令你可以和编辑器交互、控制用户界面、执行后台任务,很多的插件通常也是通过命令导出它们的核心功能以便其它插件或使用者复用

编码执行命令

通过 vscode.commands.executeCommand 接口可以编码方式执行命令,以便开发插件的时候可以使用到VS Code内置的功能或者利用内置的GitMarkdown插件。

举例来说,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);
            },
        })
    );
}
复制代码

当悬停时显示一个链接:

image.png

点击该链接会执行editor.action.addCommentLine命令,将当前行变为注释

image.png

对于需要给命令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在命令的显示上,whenenablement有所区别,在编辑器或explorer的上下文菜单中会显示enablement/disablement的菜单项,但是在Command Palette中会过滤掉

自定义when、enablement取值条件

whenenablement字段中的取值条件 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的形式

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享