前言
vscode 插件在开发中能够帮我们提高大大开发效率和体验,比如一些智能提示的插件(Path Intellisense、Codeium),代码校验和格式化插件(ESLint、Prettier、Stylelint),工具类的插件(Bookmarks、Git Graph、Project Manager、Postman),主题类插件(GitHub Theme、Meterial 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
选择初始化项目模板类型
定义项目名称为 my-extension
后面可以全部 Enter 或根据实际情况选择。初始化完成后打开项目,进行简单的调试
调试
F5 启动调试,这时会打开一个新的 vscode 窗口
Ctrl+Shift+P打开命令行,输入Hello World
右下角弹出提示框Hello World from my-extension!
此时说明,我们项目初始化已经完成
开始开发
项目结构
.
├── 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": "是否在每次启动时显示欢迎提示!"
}
}
}
}
}
配置完成后会出现在 设置 中,如下
然后我们需要在代码中拿到用户配置的值(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提示
官方为我们提供了许多参考样例