vscode插件开发入门: 简述

109 阅读10分钟

搭建环境

首先搭建好开放环境我们需要安装的根据有如下:

  • Node.js
  • Yeoman 和 VS Code Extension Generator, 可以通过 npm install --global yo generator-code 进行安装

创建插件项目

在开发环境安装完成之后,在命令行执行 yo code 命令来生成 VSCode 插件项目。VSCode 插件支持使用 Typescript 和 JavaScript 来进行开发。

创建1.png

调试插件

按“F5”或者“运行->启动调试”,就可以运行起来插件了,第一次运行会进行编译,然后会拉起一个新的 VSCode 窗口,如下图所示:

调试.gif

开发插件

插件项目的文件结构

在插件项目的所有文件中,最核心的是 package.json 和 extension.ts 这两个文件

├── .vscode
│ ├── launch.json // 插件调试的配置文件
│ └── tasks.json // 构建任务的配置文件
│
├── .gitignore // Git 忽略文件
├── .vscodeignore // VSCode 忽略文件
├── README.md // 项目描述文件
├── src
│ └── extension.ts // 插件入口文件
├── package.json // 插件清单文件
├── tsconfig.json // Typescript 配置文件
package.json 插件清单文件

在任何一个 VSCode 插件项目都必须包含一个 package.json 文件作为插件清单文件。package.json 文件包含了 Node.js 项目的属性(如 script 和 dependencies)和 VSCode 插件项目的属性(如: publisher、activationEvents 和 contributes)。其中,有以下几个比较重要的属性:

  • name 和 publisher:VSCode 使用、作为插件的唯一 ID
  • main: 插件的文件入口
  • activationEvents: 插件的激活事件(Activation Event), 定义插件在何种情况下被激活
  • contributes: 插件贡献点(Contribution Point), 定义插件的各种扩展功能
  • engines.vscode: VSCode API 的最小版本
    // package.json
    {
        "name": "helloworld",
        "displayName": "helloworld",
        "description": "",
        "version": "0.0.1",
        "engines": {
            "vscode": "^1.92.0"
        },
        "categories": [
            "Other"
        ],
        "activationEvents": [],
        "main": "./dist/extension.js",
        "contributes": {
            "commands": [
                {
                    "command": "vs.helloWorld",
                    "title": "Hello World"
                }
            ]
        },
        "scripts": {
            "vscode:prepublish": "yarn run package",
            "compile": "webpack",
            "watch": "webpack --watch",
            "package": "webpack --mode production --devtool hidden-source-map",
            "compile-tests": "tsc -p . --outDir out",
            "watch-tests": "tsc -p . -w --outDir out",
            "pretest": "yarn run compile-tests && yarn run compile && yarn run lint",
            "lint": "eslint src --ext ts",
            "test": "vscode-test"
        },
        "devDependencies": {
            "@types/vscode": "^1.92.0",
            "@types/mocha": "^10.0.7",
            "@types/node": "20.x",
            "@typescript-eslint/eslint-plugin": "^7.14.1",
            "@typescript-eslint/parser": "^7.11.0",
            "eslint": "^8.57.0",
            "typescript": "^5.4.5",
            "ts-loader": "^9.5.1",
            "webpack": "^5.92.1",
            "webpack-cli": "^5.1.4",
            "@vscode/test-cli": "^0.0.9",
            "@vscode/test-electron": "^2.4.0"
        }
    }

其中,最需要重点关注的就是 activationEvents 和 contributes 属性了。
在 contributes 插件贡献点(Contribution Point)属性中,插件注册了 Hello World 命令,并且绑定了 extension.helloWorld 命令 ID。

在 activationEvents 激活事件(Activation Event)属性中,通过 onCommand:extension.helloWorld 定义了当用户执行 extension。helloWorld 所绑定的 Hello World 命令时插件就会被激活。

extension.ts 插件入口文件

extensions.ts 文件是插件的入口文件,包含以下两个函数:

  • activate: 当插件激活后,会执行此函数
  • deactivate: 当插件失效(如被禁用)时,会执行此函数

    // 以下是Hello World插件的extensions.ts文件中的内容
    // The module 'vscode' contains the VS Code extensibility API
    // Import the module and reference it with the alias vscode in your code below
    import * as vscode from 'vscode';

    // This method is called when your extension is activated
    // Your extension is activated the very first time the command is executed
    export function activate(context: vscode.ExtensionContext) {
        // Use the console to output diagnostic information (console.log) and errors (console.error)
        // This line of code will only be executed once when your extension is activated
        console.log('Congratulations, your extension "helloworld" is now active!');

        // The command has been defined in the package.json file
        // Now provide the implementation of the command with registerCommand
        // The commandId parameter must match the command field in package.json
        // 注册命令,命令ID(vs.helloWorld)需要在package.json文件中定义
        const disposable = vscode.commands.registerCommand('vs.helloWorld', () => {
            // The code you place here will be executed every time your command is executed
            // Display a message box to the user
            vscode.window.showInformationMessage('Hello World from helloworld!');
        });

        context.subscriptions.push(disposable);
    }

    // This method is called when your extension is deactivated
    // 插件失效时,该函数会被调用
    export function deactivate() {}

Vscode 插件的扩展能力

Vscode 为插件开发提供了丰富的扩展能力。通过强大的 Vscode 插件 API, 可以打造出不同功能的插件

插件设计理念

稳定性与性能是一直是 Vscode 主要的两个设计理念。同样地,这两个设计理念也是 Vscode 插件的核心价值观。因此,在实际的插件架构设计中,Vscode 团队设计出两个核心的插件模型:进程隔离和界面渲染隔离

  1. 进程渲染 为了保证 Vscode 的稳定性和性能,避免插件影响到整个 VSCode 的使用体验。Vscode 设计了进程隔离的插件模型。所有的插件都运行在一个名为 Extension Host 的独立进程中。 Vscode 第一次启动时会创建一个主进程(Main Process),还会为每个 VSCode 窗口创建一个渲染进程(Renderer Process)及一个插件宿主进程(Extension Host)

进程隔离.png 插件宿主进程是一个 NodeJs 进程,负责加载与运行 Vscode 插件。插件宿主进程与 Vscode 的主进程和渲染进程相互隔离,互不影响,这样才能确保插件:

  • 不影响启动速度
  • 不会降低用户界面的启响应速度
  • 不会改变用户界面的样式

此外,通过设置插件的激活事件(Activation Event),可以实现插件的懒加载。比如,只有当用户打开了 Markdown 文件时,Markdown 插件才会被加载。这样可以使插件不会浪费 CPU 和内存资源,更有效地确保 Vscode 的整体性能

  1. 界面渲染隔离
    Vscode 基于 Electron 框架开发,用到了 HTML、CSS、Typescript 等前端技术。然而,Vscode 并没有向插件开放 DOM 的控制权限,使得插件不能随意更改 Vscode 的界面

Atom 编辑器也是基于 Electron 框架开发的。与 VSCode 不同的是,Atom 向插件开发者完全开放了 DOM 控制权限,这使得插件有更大的灵活性。从技术上来讲,通过一个 Atom 插件,就可以从用户界面上把 Atom 变成 Vscode.不过,Atom 的插件在拥有灵活性的同时,也随之带来了诸多问题。不同开发者的插件质量难以控制,直接访问 DOM 进行界面渲染,会对整个编辑器的性能造成影响。虽然都是基于 Electron 开发的,但在同样配置的机器下,Atom 的整体运行速度会比 Vscode 慢一些。插件造成的性能影响也是其中一个原因

Vscode 进程隔离的插件模型使得插件不能随意更改 DOM。插件被关在插件宿主进程中,而用户界面的控制权限则在渲染进程中,所以插件自然无法直接在用户界面上做手脚。Vscode 统管所有用户交互入口,制定交互标准,所有用户的操作都被转化为各种请求发送给插件,插件能做的就是响应这些请求,专注于业务逻辑。但从始至终,插件都不能直接“决定”或“影响”界面元素如何被渲染(颜色、字体一概不行)

Vscode 渲染进程完全把控了界面渲染。由此带来的好处就是,所有的插件都有统一的界面风格,使用者可以拥有一致的用户体验,大大降低了插件使用者的学习曲线

通用功能的扩展能力

通用功能的扩展能力十分重要,几乎每个插件都会用到其中的功能

  1. 命令
    命令是 VSCode 的核心功能。通过命令面板,可以运行 VSCode 的任何命令。此外,还可以通过快捷键或快捷菜单来执行命令。

插件可以通过 vscode.commands API 注册并执行命令。在 package.json 文件中,通过 contributes.commands 贡献点可以使命令出现在命令面板中

  1. 配置项
    在 package.json 中,插件可以通过 contributes.configuration 贡献点来定义 settings.json 文件中的配置项,通过 workspace.getConfiguration API 来读取 settings.json 文件中配置项的值
  2. 快捷键
    在 package.json 文件中,插件可以通过 contributes.keybindings 贡献点来添加
  3. 快捷菜单
    在 package.json 文件中,插件可以通过 contributes.menus 贡献点来定义快捷菜单中的内容,包括文件管理器、编辑区域、编辑标题栏、调试调用栈视图等快捷菜单中的内容。
  4. 数据存储
    VSCode 为插件提供了数据存储的读写 API,每个插件都有独立的存储空间,主要分为以下两种:
  • ExtensionContext.globalState: 全局范围的存储空间
  • ExtensionContext.workspaceState: 针对当前工作区的存储空间
  1. 通知栏
    VSCode 提供了以下 3 个通知栏的 API,可以在窗口的右下角显示不同级别的信息
  • window.showInfomationMessage:显示消息信息
  • window.showWarningMessage: 显示警告信息
  • wondow.showErrorMessage: 显示错误信息
  1. 用户输入
    通过 vscode.QuickPick API,插件可以调出选择列表,让用户进行单选或多选操作
  2. 文件选择器
    通过 vscode.window.showOpenDialog API,插件可以调出操作系统文件选择器,让用户选择文件或文件夹
  3. 输出面板
    输出面板可以用于输出日志信息。通过 window.createOutputChannel API,插件可以创建新的输出面板
  4. 进度条
    一些操作需要花费较长的时间,如上传、下载等。通过 vscode.Process API,插件可以实时向用户展示进度信息

工作区用户界面的扩展能力

虽然 VSCode 没有向插件开放 DOM 的直接控制权限,但 VSCode 开放了一系列的 API,使得开发者可以通过插件对用户界面进行扩展

  1. 视图容器
    默认情况下,在 VSCode 左侧的侧边栏中有 5 个内置的视图容器,分别是自愿管理器、搜索视图、源代码管理视图、运行与调试视图、插件视图。 在 package.json 文件中,插件可以通过 contributes.viewsContainers 贡献点来定义新的视图容器
  2. 树状视图
    在 package.json 文件中,通过 contributes.views 贡献点,插件可以在任何视图容器中添加新的树状视图
  3. 网页视图
    网页视图为插件提供了一个高度可定制化的界面。在编辑区域中,开发者可以通过前端技术(HTML/CSS/JavaScript)来构建一个用户界面
  4. 状态栏项目
    通过 window.createStatusBarItem API,插件可以在底部状态栏添加新的项目,新的项目主要有以下两个功能:
  • 显示文本图标,比如:可以显示 CPU 和内存信息
  • 通过单击直接运行命令。可以作为常用命令的快捷方式
  1. 主题的扩展能力
    在 VSCode 中,有以下两种类型的主题插件
  • 颜色主题: 第一管理 VSCode 用户界面的颜色,颜色又分为如下两大类:
    1、编辑器的颜色主题:定义了编辑区域的相关颜色配置 2、工作区的颜色主题:工作区包含了除编辑区域外的所有区域,例如活动栏、资源管理器、菜单栏、状态栏、通知、滚动条、进度条、输入控件、按钮等
  • 图标主题: 定义了 VSCode 文件资源管理器中所有文件和文件夹的图标

编程语言的扩展能力

VSCode 的强大之处就是可以通过插件为不同编程语言提供丰富的支持,功能包括但不限于:

  • 语法高亮
  • 代码片段提示
  • 括号匹配
  • 括号自动闭合
  • 代码自动缩进
  • 代码折叠
  • 显示悬停信息
  • 智能提示
  • 静态代码检查及自动修复
  • 代码导航
  • 代码格式化
  • 重构

调试功能的扩展能力

VSCode 提供统一的调试界面。基于 Debug Adaper Protocol,插件可以通过丰富的调试功能,包括但不限于:

  • 各种类型的调试断点
  • 变量查看
  • 多进程及多线程支持
  • 调用堆栈
  • 表达式监控
  • 调试控制台