VS Code插件开发

1,431 阅读10分钟

插件开发

一个demo流程举例:

  • 通过官方脚手架创建插件项目
  • extension.ts入口中通过vscode.window.registerTreeDataProvider注册vscode树形数据提供者,创建侧边栏,创建一个类实现vscode.TreeDataProvider<vscode.TreeItem>给registerTreeDataProvider,完成包含树形结构数据的侧边栏创建。
  • 在TreeDataProvider实例中,通过vscode.TreeItem的contextValue属性,为树视图中的每个项(即节点)提供一个上下文值。在package.json中的menus下配置view/item/context相关菜单事件内容,实现树节点显示不同的上下文菜单(右键菜单)。
  • 创建一个类,完成webview相关初始化和交互逻辑,实现一个窗口创建方法并暴露出去给命令调用,通过vscode.window.createWebviewPanel创建webview
  • 在view/item/context中配置一个打开webview的菜单项
  • 在extension.ts中注册一个打开webview的命令context.subscriptions.push->vscode.commands.registerCommand

官方文档:code.visualstudio.com/api/get-sta…

安装脚手架

npm install -g yo generator-code

生成插件项目

yo code

# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension? HelloWorld
### Press <Enter> to choose default for all options below ###

# ? What's the identifier of your extension? helloworld
# ? What's the description of your extension? LEAVE BLANK
# ? Enable stricter TypeScript checking in 'tsconfig.json'? Yes
# ? Setup linting using 'tslint'? Yes
# ? Initialize a git repository? Yes
# ? Which package manager to use? npm

code ./helloworld

完成一个基本的插件需要理解下面三个关键概念

  • 激活事件: 插件激活的时机。
  • 发布内容配置: VS Code扩展了 package.json
  • VS Code API: 你的插件代码中需要调用的一系列JavaScript API。

激活事件

激活事件是在package.json中的activationEvents字段声明的一个JSON对象

  • onLanguage: 打开特定语言时激活
  • onCommand:调用没那个了时激活
  • onDebug:调试会话启动前激活
  • workspaceContains:文件夹打开后,且文件夹中至少包含一个符合glob模式的文件时触发.
  • onFileSystem:以协议(scheme)打开文件或文件夹时触发,通常是file-协议,也可以用自定义的文件供应器函数替换掉,比如ftpssh.
  • onView:指定id的视图展开时触发:
  • onUri:插件的系统级URI打开时触发
  • onWebviewPanel:VS Code需要恢复匹配到viewTypewebview视图时触发.
  • *:当VS Code启动时触发
"activationEvents": [
  "onLanguage:typescript"
  "onCommand:extension.sayHello"
  // ...
]

发布内容配置

  • contributes.configuration :在configuration中配置的内容会暴露给用户,用户可以从“用户设置”和“工作区设置”中修改你暴露的选项。

  • contributes.configurationDefaults:为特定的语言配置编辑器的默认值,修改这个配置会覆盖编辑器已经为语言提供的默认配置。

  • contributes.commands:设置命令标题和命令体,随后这个命令会显示在命令面板中。你也可以加上category前缀,在命令面板中会以分类显示

  • contributes.menus : 为编辑器或者文件管理器设置命令的菜单项

    • 全局命令面板 - commandPalette
    • 资源管理器上下文菜单 - explorer/context
    • 编辑器上下文菜单 - editor/context
    • 编辑器标题栏 - editor/title
    • 编辑器标题上下文菜单 - editor/title/context
    • 调试栈视图的上下文菜单 - debug/callstack/context
    • SCM 标题菜单 - scm/title
    • SCM 资源组 - scm/resourceGroup/context
    • SCM 资源 - scm/resource/context
    • SCM 改变标题 - scm/change/title
    • 视图的标题菜单 - view/title
    • 视图项的菜单 - view/item/context
  • contributes.keybindings:这个配置确定了用户输入按键组合时的触发规则

  • contributes.views:为VS Code 添加视图。需要为视图指定唯一标识和名称

  • contributes.viewsWelcome:给自定义视图配置欢迎内容。欢迎内容只能应用到空的树视图中

  • contributes.viewsContainers:配置自定义视图的视图容器。你需要为视图指定唯一标识和标题和图标。目前你只可以配置活动栏(activitybar)

VS Code API

VS Code API

插件目录结构

.
├── .vscode
│   ├── launch.json     // 插件加载和调试的配置
│   └── tasks.json      // 配置TypeScript编译任务
├── .gitignore          // 忽略构建输出和node_modules文件
├── README.md           // 一个友好的插件文档
├── src
│   └── extension.ts    // 插件源代码
├── package.json        // 插件配置清单
├── tsconfig.json       // TypeScript配置
  • launch.json 用于配置VS Code 调试
  • tasks.json 用于定义VS Code 任务

package.json

{
  "name": "test",
  "description": "test",
  "version": "0.0.1",
  "publisher": "test", // 发行方
  "engines": {
    "vscode": "^1.47.0" // 描述插件依赖的最低 VSCode API版本
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [ // 激活事件
    "*"
  ],
  "repository": {
    "type": "git",
    "url": ""
  },
  "main": "./dist/extension.js",//插件入口文件
  "contributes": { // 发布内容配置
    "commands": [
      {
        "command": "test.debug",
        "title": "查看"
      },
      {
        "command": "test.showWebview",
        "title": "打开webview"
      },
    ],
    "views": {
      "testView": [
        {
          "id": "test",
          "name": "test"
        }
      ]
    },
    "viewsContainers": {
      "activitybar": [
        {
          "icon": "resources/test.svg",
          "id": "testView",
          "title": "test"
        }
      ]
    },
    "viewsWelcome": [
      {
        "view": "test",
        "contents": "[test]"
      }
    ],
    "menus": {
      "view/item/context": [
        {
          "command": "test.debug",
          "group": "inline"
        },
        {
          "command": "test.showWebview",
          "title": "打开webview"
        },
      ]
    }
  },
  "scripts": {
    "clean": "rimraf dist/",
    "compile": "yarn run clean && webpack --mode production",
    "watch": "yarn run clean && webpack --mode none --watch"
  },
  "dependencies": {
    "@microsoft/vscode-azext-utils": "^0.3.15",
    "axios": "^0.27.2",
    "react": "^18.2.0",
    "react-vsc-treeview": "^0.2.3"
  },
  "devDependencies": {
    "@types/node": "^18.7.18",
    "@types/react": "^18.0.20",
    "@types/vscode": "^1.71.0",
    "@types/vscode-webview": "^1.57.0",
    "@typescript-eslint/eslint-plugin": "^4.16.0",
    "@typescript-eslint/parser": "^4.16.0",
    "eslint": "^7.21.0",
    "ts-loader": "^9.4.0",
    "typescript": "^4.2.2",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  }
}

extension.ts

// 'vscode'模块包含了VS Code extensibility API
// 按下述方式导入这个模块
import * as vscode from 'vscode';

// 一旦你的插件激活,vscode会立刻调用下述方法
export function activate(context: vscode.ExtensionContext) {

    // 用console输出诊断信息(console.log)和错误(console.error)
    // 下面的代码只会在你的插件激活时执行一次
    console.log('Congratulations, your extension "my-first-extension" is now active!');

    // 入口命令已经在package.json文件中定义好了,现在调用registerCommand方法
    // registerCommand中的参数必须与package.json中的command保持一致
    let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
        // 把你的代码写在这里,每次命令执行时都会调用这里的代码
        // ...
        // 给用户显示一个消息提示
        vscode.window.showInformationMessage('Hello World!');
    });

    context.subscriptions.push(disposable);
}

调试插件

直接运行 Run Extension 可以开启断点调试

打包插件

安装vsce

npm install -g vsce

使用vsce打包,生成vsce文件

vsce package

vsce只能用Personal Access Tokens发布插件,所以至少要创建一个 Token 以便发布插件。

  1. 获取 Personal Access Token

首先,你得有一个Azure DevOps 组织

选择 Personal Access Token,点击New Token创建一个新的 Personal Access Token

给 Personal Access Token 添加描述,过期时间等等,你最好把过期时间设置为 1 年,这样你接下就能方便很多,选择custom defined(用户自定义)范围,然后点击Show all scopes(显示全部)

最后,在这个列表中找到Marketplace,并勾选Acquire和Manage

保存好该token(7k2ikehuuihpbdubnclq3ota2ht3yaiewfyyperswjp3eempbqfa)

  1. 创建发行方

发行方是 VS Code 市场有权发布插件的唯一标识,每个插件的package.json文件都包含着publisher字段。在市场的发行方管理页中创建发行方,然后用这个账号登录vsce

  1. 发行方登录
vsce login (publisher name)

create-publisher命令类似地,vsce会要求输入你的 Personal Access Token。

你也可以用命令参数-p <token>直接登录然后立即发布插件:

vsce publish -p <token>

VS Code Extension API

VS Code内置了扩展能力,在插件API加持之下,VS Code几乎每一个部分都可以自定义或者加强。而且,VS Code中的很多核心功能已编译为插件,它们都共用了一套插件API。

使用 Extension API 能够做到

改变VS Code的颜色和图标主题

VS Code中的主题分为两类:

  • 色彩主题:UI组件ID和文本符号ID到色彩间的映射。通过色彩主题你可以修改VS Code UI组和编辑器中的文本。

  • 图标主题:文件类型/名称到图片之间的映射。文件图标显示于VS Code的资源管理侧边栏、快速打开列表和编辑器Tab等UI中。

插件灵感
  • 改变你的代码颜色
  • 改变VS Code UI颜色
  • 将现有的TextMate主题应用到VS Code中
  • 添加自定义图标

在UI中添加自定义组件和视图

“工作台”是指整个VS Code UI和其中包含的下列UI 组件:

  • 标题栏
  • 活动栏
  • 侧边栏
  • 面板
  • 编辑器群
  • 状态栏

  • 活动栏:Azure App Service extension添加了一个视图容器
  • 侧边栏:内置的NPM 插件 添加了一个 Tree View 到资源管理器视图
  • 编辑器群:内置的Markdown 插件 添加了一个Webview 到编辑器的旁边
  • 状态栏:VSCodeVim 插件 添加了一个状态栏项目
插件灵感
  • 自定义资源管理侧边栏的菜单行为
  • 在侧边栏中创建新的、交互式的TreeView
  • 定义新的活动栏视图
  • 在状态栏显示新的信息
  • 使用WebView API显示自定义内容
  • 配置源控制(git/svn等) 来源

创建Webview,使用HTML/CSS/JS显示自定义网页

当VS Code原生API不够用时,可以选择webview插件,Webview重度依赖资源,所以它脱离插件的进程而单独运行在其他环境中

在使用webview之前,请作以下考虑:

  • 这个功能真的需要VS Code来提供吗?分离成一个应用或者网站会不会更好?
  • webview是实现这个特性的最后方案吗?VS Code原生API是否能达到同样的目的呢?
  • 你的webview所牺牲的高资源占用是否能换得同样的用户价值?

支持新的编程语言

VS Code通过语言插件可以为各式各样的编程语言提供智能的编辑体验。VS Code并不含内置语言支持,不过提供了一整套支持富文本特性的API。

语言特性大致可以分为下面两种:

声明式语言特性

  • 语法高亮
  • 代码片段补全
  • 括号匹配
  • 自动闭合括号
  • 括号识别
  • 启动、关闭注释
  • 自动缩进
  • 代码折叠
插件灵感
  • 将常用的JS代码片段打包到插件中
  • 为VS Code添加新的语言支持
  • 为一门语言添加或替换语法
  • 通过注入的方式,扩展一门语法
  • 将现有的 TextMate 语法迁移到VS Code中

编程式语言特性

  • 悬停信息
  • 自动补全
  • 转跳到定义
  • 错误检查
  • 格式化
  • 重构
  • 代码折叠
插件灵感
  • 鼠标悬停于API上时, 出现用法示例
  • 使用诊断,报告代码风格错误
  • 注册新的HTML代码格式化
  • 提供丰富的IntelliSense中间件
  • 为一门语言添加代码折叠、面包屑、轮廓支持

支持特定运行时的调试

VS Code基于抽象协议,实现了一个原生(非语言相关的)的调试器UI,它可以和任意后台调试程序通信

调试适配器(Debug Adapter):连接真正的调试程序(或运行时)和调试界面的插件称之为调试适配器。VSCode没有原生调试程序,而是依赖【调试器插件】调用通信协议(调试适配器协议)和VSCode的调试器界面实现。

VS Code提供了一个配置点debuggers,调试适配器在这里可以配置特定的调试类型(例如:Node.js调试器使用node)。用户只要启动了这个类型的调试适配器会话,VS Code就能加载注册好的调试适配器

因此调试适配器的最小形式就是声明一个配置,对应调试适配器的实现,这个插件就是调试适配器的装载容器,不需要多余代码

一个更贴近现实的调试器插件往往会添加很多配置,如下面的:

  • 调试器支持的语言。VS Code会为这些语言启用UI界面的断点功能
  • 由调试器引入的JSON格式的调试配置属性。VS Code会使用这个格式校验launch.json中的配置,并提供补全功能
  • 首次加载调试时,VS Code自动生成初始launch.json文件
  • 用户可以给launch.json添加的调试配置片段
  • 声明调试配置中可以使用的变量
插件灵感
  • 通过调试适配器将VS Code的调试UI连接到真实的调试器或者运行时上
  • 通过调试器插件添加语言支持
  • 为调试配置文件添加丰富的智能提示或者悬停信息
  • 为调试配置文件添加代码片段