前言
VSCode 非常强大,原因在于开发者可以使用插件来丰富和扩展它的功能。
目前插件市场的插件非常多,但是需求复杂多变的,总有一些场景是现有插件无法满足,因此需要开发者手动实现需要的功能。
下面就开始介绍如何开发一个VSCode插件。
准备工作
在开始之前,确保您的开发环境已经安装有以下工具:
- Node.js: 插件开发需要使用到 Node.js。
- npm (Node Package Manager): 用于安装 VSCode 扩展生成器及其他依赖项。
- Visual Studio Code: 显然,您需要 VSCode 来开发插件。
创建插件
-
安装 Yeoman 和 VS Code Extension Generator
Yeoman 帮助开发者快速启动新项目,而 VS Code Extension Generator 是一个 Yeoman 插件,专为 VSCode 插件设计。
npm install -g yo generator-code -
生成插件项目:
在终端上运行以下命令:
yo code
选择生成插件(JS 或 TS),跟随提示填写插件名称、描述、作者等信息:
打开生成的项目,其中最重要的两个文件:
- 入口文件
extension.js - 配置文件
package.json
VSCode插件是功能的集合,而这些功能又对应着一个个命令,每个命令又对应着某些操作。
那么,如何注册一个命令呢?
注册命令
命令需要在入口文件先注册,绑定对应的回调函数。
下面是初始化项目后脚手架生成的 extension.js, 其中 demo 是项目名:
所有命令的注册都需要放在 activate 方法内。
总的来说,注册命令分为两步:
1. 使用vscode.commands.registerCommand 注册命令。
这个方法接受两个参数:
-
第一个参数是一个字符串,表示命令的唯一标识符。在这个例子中是
'demo.helloWorld'。你可以通过这个标识符在其他地方(比如编辑器的命令面板、快捷键绑定或菜单中)引用这个命令。 -
第二个参数是一个函数,这个函数定义了当命令被执行时应该进行的操作。在此例中,函数体内调用了
vscode.window.showInformationMessage方法,显示一个信息框给用户,内容是'Hello World from demo!'。这意味着每次用户触发这个命令时,VSCode 都会弹出一个包含 “Hello World from demo!” 的信息对话框。
2.将命令添加到context
context.subscriptions.push(disposable);
此行代码将前面创建的 disposable 添加到 VSCode 扩展的上下文订阅中,确保当扩展被停用或卸载时,与这些资源相关联的清理工作能够被执行。
注册完命令,需要在 package.json 中声明命令。
contributes.commands是一个命令数组,每个元素包括command和title两个属性。 (有没有其他属性笔者也不清楚,反正一直都只用这两个)
- command:入口文件中注册的命令名 (入口文件是extension.js,因为 main 是这么配的)
- title:搜索命令时展示的名称(不理解的话,可以把它当做前端Select组件的Label, 而command就是value)
调试
按下F5就会弹出一个新VSCode窗口。
新窗口标题会注明扩展开发宿主,表示当前窗口已经加载了调试插件。
接下来就是输入注册的命令:
- macOS:使用
Cmd+Shift+P打开命令面板 - Windows 和 Linux:使用
Ctrl+Shift+P打开命令面板
(如果记不住快捷键,直接点击上面那个搜索栏,输入> )
好了,现在就能输命令了。
结果发现没找到这个命令。
这种情况一般是插件package.json限制的vscode版本高于当前vscode版本
将插件的 engines.vscode 版本降一下:
注意:修改版本后需要将 devDependencies 中的 @types/vscode版本也一起改,不然打包会失败。
改完需要重新启动调试。(每次修改插件都需要重启调试)
先停止原来的调试
然后重新启动调试(F5), 打开命令面板输入命令
然后点击命令,就会执行命令的回调函数(弹窗提示)
最简单的插件就实现了!
—————--------------------------------我是分隔线---------------------------------------—————
下面会介绍一些插件的配置,如果不需要可以跳过,直接看最后的打包和发布。
插件激活
插件并不是安装启用之后就会激活。
像下面这个插件就处于安装启用但尚未激活状态:
插件具体什么时候激活是在 package.json 中的 activationEvents 声明。
每个插件都应该声明具体的加载时机。
以下是几种常用的声明方式:
*: VSCode 启动时,插件开始激活
{
"activationEvents": ['*']
}
onStartupFinished: VSCode 启动一段时间后才会激活插件。类似于*类激活事件,但它不会减慢 VSCode 启动,该事件在所有*类插件激活完成后触发:
{
"activationEvents": [ "onStartupFinished" ]
}
-
onCommand:Hello World: 当执行命令 Hello World 时激活插件: -
onLanguage:languageId: 编辑区包含特定语言文件时激活插件。
"activationEvents": [
"onLanguage:json",
"onLanguage:markdown",
"onLanguage:typescript"
]
只要编辑区(上图红色区域)打开 json、markdown 或者 ts文件时,就会激活该插件。
这几个激活事件可以覆盖大部分情况,还有其他一些激活事件,没有一一列举,感兴趣的可以阅读一下官方文档。
内置命令
在开发 VSCode 插件时,通常需要利用 VSCode 提供的 API 执行各种操作,比如打开、修改及保存文档内容、关闭文档以及刷新文件夹等。
使用vscode.commands.executeCommand API,你可以调用命令以集成 VSCode 的内置功能到你的插件中
例如,下面的代码用来关闭 VSCode 当前打开的文档:
vscode.commands.executeCommand('workbench.action.closeActiveEditor');
关闭所有打开的文档:
vscode.commands.executeCommand('workbench.action.closeAllEditors');
效果就和这个一样
刷新当前工作区文件夹:
vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
更多命令详见:Built-in Commands
有些功能可以通过查看 VSCode 官方文档找到,但很多是没有写在文档上的。
笔者发现了一个方便查找命令的方式,就是直接看 VSCode 的键盘快捷方式:
然后可以在这里搜索命令。
右键点击需要的命令,选择复制命令ID
右键菜单配置
什么是右键菜单?
就是把注册的命令添加到点击右键后出现的选项中,类似这样:
右键菜单有很多配置,这里只列举比较常见的。
1. 编辑器右键菜单
编辑器就是文件的内容区域。
如果想要命令出现在文件内容区域右键时的菜单上,那就需要在package.json中配置contributes.menus
{
"contributes": {
"menus": {
"editor/context": [
{ "command": "helloWorld",
"group": "navigation",
"when": "true"
}
]
}
}
}
command定义菜单被点击后要执行的命令group定义菜单分组when控制菜单出现时机
when语句语法有很多,这里列举几个常用的:
- resourceLangId == javascript:当编辑的文件是js文件时;
- resourceFilename == test.js:当当前打开文件名是test.js时;
- isLinux、isMac、isWindows:判断当前操作系统;
- editorFocus:编辑器具有焦点时;
- editorHasSelection:编辑器中有文本被选中时;
- view == someViewId:当当前视图ID等于someViewId时;
- ……
多个条件可以通过与或非进行组合,例如:editorFocus && isWindows && resourceLangId == javascript。
这么讲可能过于抽象,下面介绍笔者的插件配置:
- 将注册完的命令在
contributes.commands中配置 - 在
contributes.menus.editor/context配置编辑器右键菜单
当安装启动插件后,在编辑器右键就能看到这3个命令。
2. 资源管理器面板菜单
下面所示就是资源管理器
如果想把命令加到这里的右键菜单上,需要配置contributes.menus.explorer/context
下面介绍笔者的插件配置:
- 将注册完的命令在
contributes.commands中配置 - 在
contributes.menus.explorer/context配置资源管理器右键菜单
当安装启动插件后,在资源管理器区域右键就能看到这2个命令。
快捷键配置
快捷键是提升用户体验的一种有效方式。
- 在
contributes.commands声明已经注册的命令 - 在
contributes.keybindings配置快捷键 (可以为不同操作系统指定不同的快捷键)
在搜索命令时,能看到快捷键:
注意:在配置快捷键时,确保不与 VSCode 的默认快捷键或其他插件的快捷键冲突。
contributes 还有 views 和 viewsContainers 等配置,这里不一一讲述。
接下来,就以一个开发中经常遇到的问题为例,实现一个简单的插件。
插件案例
在实际开发中,经常有一些和后端约定好的枚举值,然后页面上有一个筛选项,展示每个枚举值对应的名称。
例如:
枚举Statement有5个枚举值:
export enum Statement {
SaPe = 1,
LineHaul = 2,
PreInvoice = 3,
Config = 4,
GlobalRule = 5,
}
需要把枚举转成label和value的形式。
export const StatementOptions: Array<TagOptions<Statement>> = [
{ label: 'Sa Pe', value: Statement.SaPe },
{ label: 'Line Haul', value: Statement.LineHaul },
{ label: 'Pre Invoice', value: Statement.PreInvoice },
{ label: 'Config', value: Statement.Config },
{ label: 'Global Rule', value: Statement.GlobalRule },
];
如何实现呢?
首先,将功能拆分成4个小功能点,来一一实现:
- 获取选中内容(也就是整个枚举)
- 解析选中的枚举字符串
- 生成目标代码
- 插入到当前文件
1.获取选中内容
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const selection = editor.selection;
const selectedText = editor.document.getText(selection);
selectedText就是用户在文件上选中的字符串。
2.解析选中的枚举字符串
这里采取正则匹配的方式。
function parseEnumText(text: string) {
const enumNameMatch = text.match(/\s*enum (\w+)/);
if (!enumNameMatch) {
return null;
}
const enumName = enumNameMatch[1];
const options = [];
const lines = text.split("\n");
for (const line of lines) {
const match = line.trim().match(/(\w+)\s*=\s*(-?\w+),?/);
if (match) {
options.push({
label: addSpaceBeforeCapital(match[1]),
value: match[1],
});
}
}
return { enumName, options };
}
对于每个枚举值,按照一定规范生成它的label。
function addSpaceBeforeCapital(str: string) {
// 使用正则表达式匹配所有的大写字母,并在其前面添加空格
// [A-Z] 匹配任意大写字母
// (?<!^) 是一个负向后查找,确保匹配的大写字母不是字符串的开始
// g 代表全局匹配
return str.replace(/(?<!^)([A-Z])/g, " $1");
}
3. 生成目标代码
目标代码的生成就是单纯的字符串拼接。
function generateOptionsCode(
enumName: string,
options: Array<{
label: string;
value: number | string;
}>
) {
const items = options.map(
(opt) =>
` { label: '${opt.label}', value: ${enumName}.${opt.value}},`
);
return `export const ${enumName}Options: Array<TagOptions<${enumName}>> = [\n${items.join(
"\n"
)}\n];`;
}
4. 将生成的字符串插入到文档
editor.edit((editBuilder) => {
editBuilder.insert(selection.end, "\n\n" + generatedCode);
});
整体代码以及配置如下:
效果:
—————--------------------------------我是分隔线---------------------------------------—————
打包
写完代码调试确认后,需要将你的插件打包成.vsix文件,这是 VSCode 插件的打包格式。
-
确保你的插件代码没有错误,所有功能正常。
-
安装 VSCode 的包管理工具
vsce
npm install -g vsce
vsce 是 VSCode Extension CLI 的缩写,用于管理 VSCode 插件的各种操作,包括打包和发布。
- 打包插件
在项目的根目录中打开命令行,运行以下命令:
vsce package
这个命令将会自动收集所有必需的文件和依赖,然后创建一个 .vsix 文件。这个文件就是你的插件的打包版本,你可以将它上传至 Marketplace 或自行分发。
下面对demo进行打包。
失败了,提示需要修改 README.md 文件
改完 README.md 重新打包:
成功了,在当前项目根目录生成了一个 demo-0.0.1.vsix 文件,其中 demo是项目名,0.0.1是package.json中的 version。
注意:请确保每次打包version都不一样,否则无法发布到Marketplace。
如果不想发布到 Marketplace,可以在本地手动安装。
右键点击生成的.vsix文件,选择安装扩展VSIX,然后重启VSCode就可以使用插件了。
发布
为了让我们的插件能在VSCode插件市场中被搜到,需要发布到 Marketplace。
- 打开 marketplace.visualstudio.com/vscode
- 登录
- 点击右上角 Publish extension
4. Create publisher
- New extension
- 选择 Visual Studio Code, 上传打包生成的
.vsix - 如果上传成功,一般得等几分钟后才能在vscode的插件市场搜索到