vscode扩展开发(三): 自定义菜单, 悬停提示, 代码片段

1,490 阅读2分钟

自定义菜单

自定义菜单可以通过package.json中的contributes.menu来进行配置, 直接贴配置:

// pageage.json

"activationEvents": [
    "onCommand:learn-vscode-extends.helloWorld",
    "onCommand:learn-vscode-extends.editorTitle",
    "onCommand:learn-vscode-extends.explorerContext"
],
"contributes": {
    "commands": [
        {
            "command": "learn-vscode-extends.helloWorld",
            "title": "helloWorld",
            "icon": "$(getting-started-setup)" 
        },
        {
            "command": "learn-vscode-extends.editorTitle",
            "title": "编辑器标题",
            "icon": "$(tools)" // 指定icon(当被菜单绑定时显示, 否则显示 title)
        },
        {
            "command": "learn-vscode-extends.explorerContext",
            "title": "资源右键菜单"
        }
    ],
    "menus": {
        "editor/title": [ //编辑器标题
            {
                "command": "learn-vscode-extends.editorTitle", // 点击触发的事件id
                "alt": "learn-vscode-extends.helloWorld", // 按住 ait 键显示切换到那个命令
                "group": "navigation", // 指定分组 "navigation" 总是被排序到菜单的顶部/开头, 详细的分组信息见这里 https://code.visualstudio.com/api/references/contribution-points#Sorting-of-groups
                "when": "editorFocus && resourceLangId == typescript" // 编辑器具有焦点,并且打开的是ts文件才会出现
            }
        ],
        "explorer/context": [ // 资源管理器右键菜单
            {
                "command": "learn-vscode-extends.explorerContext",
                "group": "6_copypath" // 详细的分组信息见这里 https://code.visualstudio.com/api/references/contribution-points#Sorting-of-groups
            }
        ]
    }
},

对应的注册命令代码:

// src/extension.ts

import * as vscode from "vscode";

export function activate(context: vscode.ExtensionContext) {

    // helloWorld 命令
    context.subscriptions.push(
        vscode.commands.registerCommand("learn-vscode-extends.helloWorld", () => {
            vscode.window.showInformationMessage("hello world");
        })
    );

    context.subscriptions.push(
        vscode.commands.registerCommand("learn-vscode-extends.editorTitle", () => {
            vscode.window.showInformationMessage("编译器标题菜单");
        })
    );

    // 资源右键菜单的回调可以拿到当前点击和所有选中的 Uri 信息(通过命令触发则没有)
    // clickFileUrl: 当前选中的 Uri 信息
    // filePaths: 所欲选中的 Uri 信息
    context.subscriptions.push(
        vscode.commands.registerCommand("learn-vscode-extends.explorerContext", (clickFileUrl: vscode.Uri, filePaths: vscode.Uri[]) => {
            vscode.window.showInformationMessage("资源右键菜单");
            if (Array.isArray(filePaths)) {
                // "\n" + 路径 拼成字符串
                const msg = filePaths.map(url => ("\n" + url.fsPath)).join("");
                vscode.window.showInformationMessage("选中的文件路径: " + msg);
            }
        })
    );
}

export function deactivate() { }

编辑器菜单点击效果

注意: 编辑器需要获取焦点, 并且这个文件需要是ts文件才会出现(详细了解见when)

image.png

image.png

编辑器菜单alt+点击效果

image.png

资源右键菜单效果

image.png

image.png

悬停提示

悬停提示可以使用vscode.languages.registerHoverProvider, 如下:

// src/extension.ts

import * as vscode from "vscode";

export function activate(context: vscode.ExtensionContext) {
    console.log("activate run ...");
    vscode.languages.registerHoverProvider(
        { scheme: "file", language: "json", }, // 匹配 json 文件
        {
            provideHover(doc: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) {

                // 当前文件类型
                console.log(doc.languageId);

                // 文件是否未保存更改
                console.log(doc.isDirty);

                // 获取当前文件
                console.log(doc.fileName);

                // 获取当前hover的单词 
                const word = doc.getText(
                    doc.getWordRangeAtPosition(position)
                );

                // 对 json 文件中的 description 单词进行悬停提示
                if (/\bdescription\b/.test(word)) {
                    return new vscode.Hover("悬停提示: 填写描述信息");
                }
            },
        });
}
export function deactivate() { }

到这里如果使用F5运行, 会发现没有效果, activateconsole.log没有输出, 修改pageage.json中的activationEvents, 如下:

"activationEvents": [
    "onLanguage:json" // 当碰到 json 文件时激活扩展(调用`activate`函数)
  ],

扩展激活相关的详细可以见 Activation Events

效果如下:

image.png

代码片段

扩展中设置代码片段, 在package.json中的contributes.snippets设置:

// package.json
"contributes": {
    "snippets": [
        {
            "language": "javascript", // 指定语言
            "path": "./src/snippets/js.json" // 设置代码片段的路径
        }
    ]
},

对应的src/snippets/js.json文件配置, 跟在vscode自定义代码片段是一样的, 如下:

{
  "console.log": {
    "prefix": ["log", "logv"], // 代码片段匹配的前缀
    "body": "console.log(\"${0} :\", ${0});", // 代码块(可以是数组), ${n} 表示是第几个光标, 0 表示最后一个光标
    "description": "打印变量" // 对应描述
  }
}

效果:

image.png

image.png

下面是一些常用的代码片段语法:

  • ${n}: 表示是第几个光标(多个光标通过tab跳到下一个光标), ${0}则表示最后一个光标
  • ${1:hello}: 第一个光标中的默认值为hello
  • ${1|one,two,three|}: 第一个光标中提供三个下拉单选值, 默认为one

下面是两个代码片段对应了生成vue2vue3的模块:

{
  "vue2-template": {
    "prefix": "vue2-template",
    "body": [
        "<template>",
        "\t<div></div>",
        "</template>",
        "",
        "<script>",
        "export default {",
        "\tname: \"${1}\",",
        "\tdata() {",
        "\t\treturn {",
        "\t\t\t$0",
        "\t\t};",
        "\t},",
        "};",
        "</script>",
        "",
        "<style lang=\"${2|scss,less|}\" ${3:scoped}>",
        "</style>"
    ],
    "description": "vue2 模板"
  },
  "vue3-template": {
    "prefix": "vue3-template",
    "body": [
        "<template>",
        "$0",
        "</template>",
        "",
        "<script ${1:lang=\"ts\"} setup>",
        "import { ref, reactive, computed, watch, nextTick, onMounted } from \"vue\";",
        "defineProps({});",
        "defineEmits([]);",
        "",
        "</script>",
        "",
        "<style ${2:scoped} ${3:module}>",
        "",
        "</style>"
    ],
    "description": "vue3 模板"
  }
}

想了解更多可以见 snippet-syntax