vscode extension 开发

547 阅读4分钟

前言

vscode 插件在开发中能够帮我们提高大大开发效率和体验,比如一些智能提示的插件(Path IntellisenseCodeium),代码校验和格式化插件(ESLintPrettierStylelint),工具类的插件(BookmarksGit GraphProject ManagerPostman),主题类插件(GitHub ThemeMeterial Theme)等。

在我们日常开发中,想必大家或多或少的都会安装一些插件。那么接下来我们探究一下,如何开发一个自定义插件。

首先介绍一下,vscode extension 一共有以下 8种 类型

  • New Color Theme
  • New Language Support
  • New Code Snippets
  • New Keymap
  • New Extension Pack
  • New Language Pack (Localization)
  • New Web Extension (Typescript)
  • New Notebook Renderer (Typescript)

环境准备

npm install -g yo generator-code

开发

创建项目

#初始化项目模板
npx yo code --ignore-version-check

选择初始化项目模板类型

image.png

定义项目名称为 my-extension

image.png

后面可以全部 Enter 或根据实际情况选择。初始化完成后打开项目,进行简单的调试

调试

F5 启动调试,这时会打开一个新的 vscode 窗口

image.png

Ctrl+Shift+P打开命令行,输入Hello World

image.png

右下角弹出提示框Hello World from my-extension!

image.png

此时说明,我们项目初始化已经完成

开始开发

项目结构

.
├── CHANGELOG.md
├── README.md
├── out
│   ├── extension.js
│   ├── extension.js.map
│   └── test
│       ├── extension.test.js
│       └── extension.test.js.map
├── package.json
├── pnpm-lock.yaml
├── src
│   ├── extension.ts
│   └── test
│       └── extension.test.ts
├── tsconfig.json
└── vsc-extension-quickstart.md

入口文件

import * as vscode from 'vscode';
import { registLinkBaidu } from './commands';

export function activate(context: vscode.ExtensionContext) {
    // 注册各种命令、菜单项
}

export function deactivate() {}

配置命令

定义一个名为 linkBaidu命令

// src/commands/registLinkBaidu.ts
import * as vscode from 'vscode';
import * as os from 'os';
import { exec } from 'child_process';
enum PlatForm {
    darwin = 'darwin', // mac
    linux = 'linux',  // linux
    windows = 'win32' // windows
}
const platform = os.platform();

export const registLinkBaidu = (context: vscode.ExtensionContext) => {
  let disposable = vscode.commands.registerCommand('rich.registLinkBaidu', () => {
      vscode.window.showInformationMessage('是否前往百度', '是', '否', '不再提示')
        .then(res => {
            if (res === '是') {
                platform === PlatForm.darwin ? exec(`open https://www.baidu.com/`) : exec(`star thttps://www.baidu.com/`);
            }
        });
    });
    context.subscriptions.push(disposable);
};

然后在 extension.ts 中引入

import * as vscode from 'vscode';
import { registLinkBaidu } from './commands';

export function activate(context: vscode.ExtensionContext) {
    // 注册跳转百度命令
    registLinkBaidu(context);
}

export function deactivate() {}

package.json 中维护命令, 这里的 command 需要和 registerCommand 注册名保持一致

{
    "contributes": {
        "commands": [
            {
                "command": "rich.registLinkBaidu",
                "title": "link to baidu"
            }
        ]
}

如果我们想要让用户能够通过配置来决定是否需要弹窗,则需要在package.json中配置

{
    "contributes": {
        "configuration": {
            "title": "rich",
            "properties": {
                "rich.showTip": {
                    "type": "boolean",
                    "default": true,
                    "description": "是否在每次启动时显示欢迎提示!"
                }
            }
        }
    }
}

配置完成后会出现在 设置 中,如下

image.png

然后我们需要在代码中拿到用户配置的值(vscode.workspace.getConfiguration().get), 来做一下判断

import * as vscode from 'vscode';
import * as os from 'os';
import { exec } from 'child_process';
enum PlatForm {
    darwin = 'darwin', // mac
    linux = 'linux',  // linux
    windows = 'win32' // windows
}
const platform = os.platform();

export const registLinkBaidu = (context: vscode.ExtensionContext) => {
  let disposable = vscode.commands.registerCommand('rich.registLinkBaidu', () => {
        if (vscode.workspace.getConfiguration().get('rich.showTip')) {
            vscode.window.showInformationMessage('是否前往百度', '是', '否', '不再提示')
            .then(res => {
                if (res === '是') {
                    platform === PlatForm.darwin ? exec(`open https://www.baidu.com/`) : exec(`star thttps://www.baidu.com/`);
                }
            });
        }
    });
    context.subscriptions.push(disposable);
};

配置右键菜单

定义一个名为 registModuleMenuItem命令

// src/menus/registModuleMenuItem.ts
import * as vscode from 'vscode';
export const registModuleMenuItem = (context: vscode.ExtensionContext) => {
  let disposable = vscode.commands.registerCommand('rich.module', () => {
    vscode.window.showInformationMessage('create module');
  });

  context.subscriptions.push(disposable);
};

然后在 extension.ts 中引入

import * as vscode from 'vscode';
import { registLinkBaidu } from './commands';
import { registModuleMenuItem } from './menus';

export function activate(context: vscode.ExtensionContext) {
	// 注册跳转百度命令
	registLinkBaidu(context);
	// 注册右击菜单
	registModuleMenuItem(context);
}

export function deactivate() {}

package.json 中维护命令, 这里的 command 需要和 registerCommand 注册名保持一致。并且需要通过 menus 字段配置

{
    "contributes": {
        "commands": [
            {
                "command": "rich.registLinkBaidu",
                "title": "link to baidu"
            },
            {
                "command": "rich.module",
                "title": "create module"
            }
        ],
        "configuration": {
            "title": "rich",
            "properties": {
                "rich.showTip": {
                    "type": "boolean",
                    "default": true,
                    "description": "是否在每次启动时显示欢迎提示!"
                }
            }
        },
        "menus": {
          "explorer/context": [
            {
              "when": "explorerResourceIsFolder",
              "command": "rich.module",
              "group": "navigation@1"
            }
          ]
        }
    }
}             

菜单出现位置

  • explorer/context: 资源管理器视图上下文菜单
  • editor/context: 编辑器上下文菜单
  • editor/title: 编辑器标题菜单栏
  • view/title: 视图标题菜单
  • commandPalette: 全局命令面板

when

  • explorerResourceIsFolder: 如果在资源管理器选择了文件夹,则为 true

group:对菜单项进行分组

editor/context 有这些默认组:

  • navigation
  • 1_modification
  • 9_cutcopypaste
  • z_commands

explorer/context 有这些默认组:

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

editor/title有这些默认组:

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

组内排序

{
  "editor/context": [ 
      { 
          "when": "editorFocus", 
          "command": "extension.sayHello", // 强制放在navigation组的第2个 
          "group": "navigation@2" 
      }, 
      { 
          "when": "editorFocus", 
          "command": "extension.demo.getCurrentFilePath", // 强制放在navigation组1个
          "group": "navigation@1" 
      }
   ]
}

快捷键

代码片段

webview

命令

  • vscode.commands.registerCommand: 用于注册命令
  • vscode.window.showInformationMessage: 右下角Toast提示

官方为我们提供了许多参考样例