保姆级指南:一文拥有属于你的vscode插件

avatar
前端开发 @微医

王志远,微医前端技术部

背景

经常在掘金公司团队发文章,而所在公司前端发文存在自己的固定格式,主要包含

  • ([\u4e00-\u9fa5]+)([\da-zA-Z]+)替换为$1 $2:中文和英文要有一个空格
  • ([\da-zA-Z]+)([\u4e00-\u9fa5]+)替换为$1 $2:中文和英文要有一个空格
  • !\[.+\]替换为![]:处理图片描述
  • 头部插入主题,内容如下
---
theme: cyanosis
highlight: atom-one-dark
---
> 自我介绍

这些动作每次需要人为处理,费时费力且易出错,固化流程尝试改用 vscode 插件自动实现,遂有 edit-article 插件出现;

相关链接

贴下相关链接,可以直接down下来看看,如有类似需求可以留言提需求我来完成。(也求 star)

当前实现效果

本文目标

实现一款自己的 vscode 插件并发布在应用市场,所有人可以在 vscode 扩展中搜索并下载;由于文章目标是实现效果,所以不打算用自己实现的 edit-article 插件作为案例。

  • 思路:vscode 插件是个node应用,那我们只需要找到对应的脚手架创建基础,再实现我们插件特定的功能即可

  • 本文 demo 插件功能:快捷键反转选中的字符串

  • 实现效果:

实战系列文章专注产出,看一个东西出现的满足感是会上瘾的,让我们抛弃文档式的学习,开始吧!

脚手架创建基础模板

安装及使用

首先你需要的是 yeoman,一个脚手架工具。通过 yeoman 你可以快速创建代码模板,如下所示:

npm install -g yo

然后你需要安装 VS Code 的模板:

npm install -g generator-code

有了脚手架,你就可以创建一个 VS Code 的插件模板了。接下来运行:

yo code myextension

请注意,第三个参数将是你新创建的插件的文件夹名字。

VS Code 插件脚手架介绍

由上图,你可以看到有七个插件模板:

  • 前两个是通过编程来提供插件功能,你可以选择 TypeScript 或者 JavaScript,结果都是类似的,因为 TypeScript 最后也需要被编译成 JavaScript 再发布;
  • 第三个是主题插件,你可以将你自己创建的主题分享给其他人;
  • 第四个是语言支持,也就是语法高亮、语言定义等;
  • 第五个是代码片段的分享;
  • 第六个则是分享快捷键;
  • 第七个就是对多个插件进行组合分享。

先实现最基础的New Extension (JavaScript),即javascript版模板

接下来,你会依次被提示输入插件的名字、介绍、想要用哪个账号发布、是否要打开 type check 以及是否要使用 git 等。你可以暂时按照我的样例进行输入,之后也可以再根据需要修改。

项目基础信息介绍

切换到项目根目录

cd myextension

项目目录结构及对应信息

├── .vscode
│ ├── launch.json // 插件加载和调试的配置
│ └── tasks.json // 配置 TypeScript 编译任务
├── .gitignore // 忽略构建输出和 node_modules 文件
├── README.md // 插件文档
├── CHANGELOG.md // 插件更新日志
├── extension.ts // 插件源代码
├── package.json // 插件配置清单
├── jsconfig.json //
查看效果

启动默认项目,按下F5启动调试,如果打开了一个vscode新窗口,试试打开命令面板执行hello world,如果执行出现了弹窗,就说明我们创建 vscode 插件项目成功啦。

命令面板唤起快捷键

  • mac:cmd + shift + p
  • win:ctrl + shift + p

关于面板的更多信息可以阅读:你不知道的 vscode 之空间控制

实现业务 -- 反转字符串

我们在之前已经知道了怎么用脚手架创建 vscode 插件项目,接下来就是实现我们自己的业务逻辑了。本章目标只有两个

  • 功能:实现命令【选中文字反转字符串】

    • package.json 中注册命令和对应的描述
    • extension.js 中完成命令的实现
  • 优化:绑定快捷键

注意:建议在 launch.json 中的“args”中添加"--disable-extensions",阻止其他插件加载,不然要调试的插件加载很慢。

命令的注册:package.json

我们先了解下【命令的注册】,先说流程

  • package.json 中注册命令和对应的描述
  • extension.js 中完成命令的实现
package.json 中注册命令和对应的描述

package.json中存在contributes对象,有属性commands对应着命令数组,每个命令是一个对象有如下属性

  • command:命令的 key,在命令的实现中会被用到
  • title:命令描述,会出现在命令面板中

配置如下

"commands": [
      {
        "command": "edit-article.reserve",
        "title": "Hello reserve"
      }
    ],

配置完成!

命令的实现:extension.js

前置总结
  • package.json中通过配置activationEvents,定义插件的激活时机
  • 项目根路径的extension.js文件中通过active钩子注册命令和对应的实现
激活时机:activationEvents

我们的业务逻辑实现在项目根路径的extension.js文件中,默认导出两个钩子方法

  • activate:插件激活时
  • deactivate:插件关闭时
module.exports = {
  activate,
  deactivate,
};

这里的激活时机并不是一打开 vscode 就执行,而是需要在package.json中存在一个属性activationEvents,值是一个数组,存储着所有会触发插件激活的时机;改为打开 js 文件时才会激活,配置如下

"activationEvents": [
    "onLanguage:javascript"
  ],
业务逻辑实现

extension.js文件中还引入了一个vscode对象,这是 vscode 提供能力的载体,我们在后面单独分析。

const vscode = require("vscode");

这里的 active 函数实现的逻辑很清晰。首先,我们要读取的信息就是当前的文档信息和主光标的信息。

let document = editor.document;
let selection = editor.selection;

有了这两个信息,读取光标选中的内容就简单了。

let text = document.getText(selection);

document 一共哪些 API 这里我就不介绍了,相信你可以自己探索,这里我们使用就是 getText,以获取某段代码。

接下来就是将这段文本进行反转了,我们可以写一个非常简单的版本,将字符串分割成字母数组,然后反转,最后重新组合成字符串。

let result = text.split('').reverse().join('');

最后一步操作就是将原来编辑器内的文本进行替换了。此时我们就要用到 edit 这个 API 函数 了。值得注意的是,这个 API 的第一参数,是一个 callback,callback 的参数是 editBuilder,也就是真正用于修改代码的对象。

editBuilder 有以下几个 API:

  • delete
  • insert
  • replace
  • setEndOfLine

这里我们要使用的当然就是 replace 了。

editBuilder.replace(selection, result);

我们只需将原先的 selection 里的内容,替换成新的 result 即可。

完整代码如下

const vscode = require("vscode");

function activate(context) {

  let reserve = vscode.commands.registerCommand(
    "edit-article.reserve",
    function () {
      let editor = vscode.window.activeTextEditor;
      if (!editor) {
        return;
      }
      let document = editor.document;
      let selection = editor.selection;
      let text = document.getText(selection);
      let result = text.split("").reverse().join("");
      editor.edit((editBuilder) => {
        editBuilder.replace(selection, result);
      });
    }
  );
  context.subscriptions.push(reserve);
}
function deactivate() {}

module.exports = {
  activate,
  deactivate,
};

至此,如何注册一个命令我们也就了解到了,当我们要实现其他功能,自然重点就成了vscode这个对象,在文末【扩展】模块分享。

优化:绑定快捷键

实现绑定

支持我们完成了命令的设定、命令的实现,但每次都要自己找命令面板去执行;如果我们能用一个快捷键绑定命令,按下快捷键就执行,会不会方便很多?说干就干!

package.jsoncontributes中还存在keybindings属性,就是用于实现绑定快捷键的,这是一个对象数组,存储的对象结构如下

  • key:快捷键
  • command:命令
  • when:何时触发

假定我们将反转命令绑定在ctrl+shift+r上,就可以添加如下内容

"keybindings": [
      {
        "key": "ctrl+shift+r",
        "command": "edit-article.reserve"
      }
    ]

實現效果

发布插件

终于,到了最激动人心的时刻!还记得第一次在浏览器上看到我们页面时的激动吗?再一次,让我们回到那个瞬间。按后文一步步走,遇到过按别人文章走结果遇问题卡一半的痛苦,所以详细(啰嗦)了点,大家多担待,无论如何,结果亲测有效!

要发布,先创建账号

Visual Studio Code 的应用市场基于微软自己的Azure DevOps;所以需要在两个平台都注册好账号。有账号后,根据 Azure DevOps 平台生成PATPersonal Access Token,个人访问令牌);根据应用市场创建发布者账号,有了这两者就能发布了。

整理如下

  • 注册账号
  • 根据 Azure DevOps 平台生成PATPersonal Access Token,个人访问令牌);
  • 根据应用市场创建发布者账号
  • 根据 vsce 在本地发布

根据 Azure DevOps 平台生成 token

登录 Microsoft

首先访问Microsoft 官网登录你的Microsoft账号,没有的先注册一个:

中间会要求输入邮箱,没有也可以直接通过手机号获取一个

一路 next,直到创建成功。

登录 Azure

然后访问:Azure 官网 ,如果你从来没有使用过 Azure,那么会看到如下提示:

image-20220510092738875

点击继续,默认会创建一个以邮箱前缀为名的组织。(只需要填写验证码即可)

image-20220510092853732

创建令牌

默认进入组织的主页后,点击右上角的Security

点击创建新的个人访问令牌,这里特别要注意

  • Organization要选择`all accessible organizations``
  • ``Scopes要选择Full access

否则后面发布会失败。

创建令牌成功后你需要本地记下来,因为网站是不会帮你保存的。

根据应用市场创建发布者账号

访问应用市场,填完名称,滑到最后保存即可

image-20220510093343787

至此,我们就有了如下内容

  • PAT:Personal Access Token,个人访问令牌
  • publisher:发布者账号

就可以执行在本地发布的动作啦!

根据 vsce 在本地发布

进入自己的插件,如果没有,可以阅读【插件脚手架:如何创建一个插件】了解创建

配置插件信息

在发布前,我们需要配置一些项目信息在 package.json 中,下面是必备信息

  • publisher:发布者 id
  • activationEvents:扩展的激活事件,建议先填指定文件类型激活(如 js 文件,onLanguage:javascript)配置项可见官网文档
  • repository:仓库地址

其他还有很多配置,我们慢慢了解,文末放置 package.json 配置总览。

安装命令包

利用发布工具发布插件

vsce是一个用于将插件发布到市场上的命令行工具。

请确认本机已经安装了Node.js,然后运行:

npm install -g vsce

然后你就可以在命令行里直接使用vsce了。下面是一个快速发布的示例

  1. 登陆
vsce login [publisherName]
# 然后输入之前获取到的 token
  1. 执行发布(这里的 patch 是指自动更新版本后发布,不带也可以,就得手动修改package.json文件了)
$ vsce publish patch
Publishing uuid@0.0.1...
Successfully published uuid@0.0.1!

完成后过几分钟就可以去 vscode 的扩展区看自己的插件啦!

查看结果

一图胜千言,静静体会成就感的美妙吧!

尾声

至此,阅读本文的小伙伴应该已经在插件市场上可以找到自己的插件啦。

老规矩,分享一个感悟:重视你的、甚至是别人的每一个吐槽,尝试解决它,这就是成长。

扩展知识:package.json 配置总览

// package.json
{
    "name": "vscode-demo",
    "displayName": "vscode-demo",
    "description": "",
    "version": "0.0.1",
    "repository""",// 仓库地址
    "publisher": "", //发布者 id,后面会讲怎么发布到插件市场
    "engines": { //vscode 最低版本
        "vscode": "^1.57.0"
    },
    "categories": [ //插件市场的分类 可以设置成语言主题等其他类型
        "Other"
    ],
    "activationEvents": [ //扩展的激活事件
        "onCommand:vscode-demo.helloWorld", //调用命令时激活
        "onLanguage:python", //py 时激活插件
        "workspaceContains:**/.editorconfig",// 文件夹打开时激活
        "onDebug",//调试前激活
        ...
        "*" //启动时候激活,使用这个不需要设置其他的
    ],
    "main": "./extension.js", //指定插件入口文件
    "icon": "", //插件图标
    "contributes": { //大部分的配置都要在这里配置
        "commands":[], //配置命令,如果需要暴露给用户使用的需要在这里配置
        "menus":[], //配置菜单
        "submenus":[], //配置子菜单会用到
        "languages": [], //配置语言
        "grammars":[], //为语言配置 TextMate 语法
        "keybindings":[], //配置快捷键
        "snippets":[], //配置代码片段
        "themes":[], //配置主题
        "views":[], //配置活动栏视图
        "viewsWelcome":[], //配置左侧视图欢迎页
        "viewsContainers":[], //配置视图容器
        "configuration":{},  //配置插件配置
        "configurationDefaults":{}, //配置插件默认配置
        "colors":{},
        "problemMatchers":{},
        "taskDefinitions":{},
        "typescriptServerPlugins":{}
    },
}

扩展知识:vscode 发布相关

发布钩子

预发布步骤

可以在清单文件中添加预发布步骤,下面的命令会在插件每次打包时执行:

{
  "name": "uuid",
  "version": "0.0.1",
  "publisher": "joaomoreno",
  "engines": {
    "vscode": "0.10.x"
  },
  "scripts": {
    "vscode:prepublish": "tsc"
  }
}

上面的示例会在每次插件打包时调用 Typescript 编译器。

增量更新插件版本

用 SemVer 语义标识符:major(最大位加一),minor(次位加一),patch(最小位加一)增量更新插件版本号。

例如,你想把插件从 1.0.0 更新到 1.1.0,那么加上minor

vsce publish minor

插件package.json的 version 会先更新,然后才发布插件。

你也可以通过命令行指定版本号:

vsce publish 2.0.1

更多可用的命令参数,请使用vsce --help

扩展知识:vscode 提供能力

获取编辑器对象

既然是编辑器相关的命令,那么我们肯定需要能够访问到编辑器,以及其中的内容。首先我们要获取的就是:当前工作区内,用户正在使用的编辑器。

const vscode = require('vscode');
let editor = vscode.window.activeTextEditor;

我们重点关注的也是这个对象。

有了这个编辑器,我们就能获取非常多的信息了。不过先别急,editor 这个变量并非一定总是有效的值,比如用户现在并没有打开任何文件,编辑器是空的,那么此时 editor 的值就是 undefined。所以, 在正式使用 editor 之前,我们要判断一下,editor 是否为 undefined,是的话就结束命令的运行。

if (!editor) {
    return;
}

接下来,我们可以输入 editor.,自动补全立刻给我们提示了不少的属性。

编辑器对象能力

  • document,也就是当前编辑器中的文档内容;
  • edit,用于修改编辑器中的内容;
  • revealRange,用于将某段代码滚动到当前窗口中;
  • selection,当前编辑器内的主光标;
  • selections,当前编辑器中的所有光标,第一个光标就是主光标,后面的则是用户创建出来的多光标;
  • setDecorations,设置编辑器装饰器(我会在后面的章节专门介绍这个 API 的使用)。

image.png