【KT】vscode插件开发例子系列(一)

3,620 阅读8分钟

背景

📢 博客首发 : 阿宽的博客

📢 团队博客: SugarTurboS

🌈 仓库源码 : learn-vscode-extension ,求个 ✨ star

好久不见,整了一个月的Electron+React+Node实现项目辅助工具好不容易搞完了,然后在项目组里推广,经过一周在团队内部使用,使用效果并不理想,无人问津,经过反馈,原因在于组员更多操作还是在 vscode 中完成, 这就很蛋疼了,都是给 vscode 惯的,好家伙,向 vscode 低头,于是我尝试这个项目辅助工具做到 vscode 插件中~

⚠️ 请注意:这篇文章是说明在开发 vscode 插件时的一些坑以及 demo 的例子,至于Electron+React+Node实现项目辅助工具系列文章在准备中,还有最终实践落地的 vscode 实战插件文章也躺在草稿箱里

吐槽一下

我怀疑我是被“搞”了,本来这个项目辅助工具使用 Electron 开发时,我就觉得这是个坑,毕竟我没写过 electron,等我做完了之后,好家伙,跟我说:“还需要下载个 PC 包安装,再运行应用?”,“操作繁琐”,让我做个 vscode 插件

vscode 插件我也没搞过啊,这是让我“迎难(男)而上”了!好了,废话不多说,直接开始吧。 怎么说呢,在做之前,也在网上搜过一些文章,奈何大部分都停留在 install + hello world 阶段,意思就是教你如何安装,然后写一个 hello world 简单的小 demo,当然,这也没问题,但这并不是我想要的,我想要的是需要各种场景下的 demo 例子,经过一些文章的查找和翻阅官方文档,整理了一些常见的例子,后续会持续更新,包括项目最终落地的 vscode 实战插件等~

初步尝试

一、安装依赖

微软为了造福大众,提供了一个 yeoman 脚手架,通过此脚手架可以生成开发插件的模版代码。可以通过命令进行安装

npm install -g yo generator-code

这时候你应该是有以下环境的

  • nodejs
  • npm
  • yeoman
  • generator-code

二、初始化 Demo 插件

当上面的安装完毕之后,只需要进入你开发目录文件夹,通过脚手架生成一个开发 vscode 插件的项目。

yo code

根据提示信息填写相关内容

这时候我们的 vscode 插件项目就完成初始化了~ 下面看看相关文件的说明!

文件说明

项目中两个重要的文件我们需要看一下:extension.tspackage.json

3.1 extension.ts

该文件是入口文件,下面我们写一段简单的 demo 代码

此段代码意思为:注册一个 beehive.toastDemo 事件,当触发此事件会显示一段 message

// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  let disposable = vscode.commands.registerCommand('beehive.toastDemo', () => {
    vscode.window.showInformationMessage('toastDemo touched !')
  })
  context.subscriptions.push(disposable)
}
export function deactivate() {}

3.2 package.json

该文件配置项太多,建议去官方文档翻阅

关键的主要是: activationEventscontributes 这两个属性,关于这两个属性你可以看官方文档

下面讲一下 contributes 这玩意,contributes 是这个插件的核心,指代这个插件有哪些功能。通过官方文档我们也能知道(这些都在demo 例子中有写到):

  • configuration:通过这个配置项我们可以设置一个属性,这个属性可以在 vscode 的 settings.json 中设置,然后在插件工程中可以读取用户设置的这个值,进行相应的逻辑。
  • commands:定义的命令,在 vscode 中通过 cmd+shift+p 进行输入定义好的命令就可触发对应事件。
  • menus:自定义编辑器右侧菜单栏
  • keybindings:可以设置快捷键
  • languages:设置语言特点,包括语言的后缀等
  • grammars:可以在这个配置项里设置描述语言的语法文件的路径,vscode 可以根据这个语法文件来自动实现语法高亮功能
  • snippets:设置语法片段相关的路径

以上边注册的 toastDemo 为例,此时的 package.json 应为:

{
  "activationEvents": ["onCommand:beehive.toastDemo"],
  "contributes": {
    "commands": [
      {
        "command": "beehive.toastDemo",
        "title": "demo1: beehive.toastDemo !"
      }
    ]
  }
}

四、运行 vscode 插件

点击此 Debug Icon,或者是 vscode 菜单栏:Run -> Start Debugging

当我们点击下 Run Extension 时,会开一个本地 vscode 窗口

我们在新开的 vscode 窗口中输入 : cmd + shift + P

然后输入我们注册的事件:beehive.toastDemo,然后我们按下回车,就会执行我们写的事件回调了!

至此,我们的第一个简单 Demo 完成!如果你还不了解,👉 建议直接去看源码,特别简单

例子

我写个 vscode 插件,肯定不只是想展示一个 toastMessage 弹窗吧 ???我想做些交互效果咋办,想自定义 Menu、想展示 WebView、想自定义左侧侧边栏咋整??不慌,往下看,下面是我整理的一些小 demo,虽然不一定能满足你的需求,但是希望应该能给你一些帮助~

1.输入内容(Input)

场景

该场景适合一些需要输入内容之后做的操作;

  • 输入 text ,发起接口请求等
  • 输入 text ,执行一些逻辑操作
  • ......

代码展示

// demo1 需要Input输入内容
// beehive-inputName.ts
import * as vscode from 'vscode'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand('beehive.inputName', () => {
    vscode.window
      .showInputBox({
        ignoreFocusOut: true,
        password: false,
        prompt: 'entry your name',
      })
      .then((value) => {
        if (value === undefined || value.trim() === '') {
          vscode.window.showInformationMessage('Please type your name.')
        } else {
          const name = value.trim()
          vscode.window.showInformationMessage('your name is: ', name)
          return
        }
      })
  })

  context.subscriptions.push(disposable)
}

入口文件需要引入该文件

// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-inputName')(context) // demo1 输入Input内容
}

export function deactivate() {}

通过 package.json 也需要注册此事件

{
  "activationEvents": ["onCommand:beehive.inputName"],
  "contributes": {
    "commands": [
      {
        "command": "beehive.inputName",
        "title": "demo1: beehive.inputName !"
      }
    ]
  }
}

效果展示

源码展示

2.自定义 WebView 页面

场景

该插件场景适合做一些简单页面展示、欢迎页面等

代码展示

// demo2 自定义显示页
// 具体看 package.json 中的 configuration 和 commands
import * as vscode from 'vscode'
import * as fs from 'fs'
import * as path from 'path'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand(
    'beehive.customWelcome',
    () => {
      const panel = vscode.window.createWebviewPanel(
        'welcome',
        '自定义欢迎页',
        vscode.ViewColumn.One,
        {
          enableScripts: true,
        }
      )

      const htmlPath = path.join(
        context.extensionPath,
        'src/customWelcome.html'
      )
      let html = fs.readFileSync(htmlPath, 'utf-8')
      panel.webview.html = html
    }
  )

  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-customWelcome')(context) // demo2 加载自定义WebView欢迎页
}

export function deactivate() {}
// package.json
{
  "activationEvents": ["onCommand:beehive.customWelcome"],
  "contributes": {
    "commands": [
      {
        "command": "beehive.customWelcome",
        "title": "demo4: beehive.customWelcome !"
      }
    ]
  }
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>自定义欢迎页面</title>
    <style>
      .app {
        margin: 12px 0;
        font-size: 24px;
      }
      .title {
        font-size: 18px;
        margin-top: 24px;
      }
    </style>
  </head>
  <body>
    <div class="app">
      Welcome BeeHiver ~
      <div class="title">后人哀之而不鉴之 亦使后人而复哀后人也 !</div>
    </div>
    <img
      src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif"
      width="300"
    />
  </body>
</html>

效果展示

源码展示

3.快捷键注册

场景

每次我们都需要 cmd+shift + P 调出选择器,然后输入我们注册的事件名,特别麻烦,vscode 支持快捷键注册,下面看看如何实现吧!

代码展示

{
  "activationEvents": ["onCommand:beehive.keybindings"],
  "contributes": {
    "commands": [
      {
        "command": "beehive.keybindings",
        "title": "demo4: beehive.keybindings !"
      }
    ],
    "keybindings": [
      {
        "command": "beehive.keybindings",
        "key": "Cmd+]",
        "mac": "Cmd+]",
        "when": "editorTextFocus"
      }
    ]
  }
}

然后此时我们通过 Run Extension (上面有说如何运行插件) ,在本地窗口,我们随便打开一个文件,然后按下 : cmd+] 就可以触发我们定义的 beehive.keybindings 事件

🙋‍♂️ 可能有人要问,为什么要打开文件?直接按快捷键为什么没有反应?原因是:我们上面写了 when 条件,当编辑器被聚焦编辑(editorTextFocus)时,才去注册的此事件,懂?

源码展示

4.自定义菜单 Menu

场景

适用于一些快捷按钮的自定义,可通过 Menu 操作

代码展示

// demo4 自定义菜单
import * as vscode from 'vscode'
module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand('beehive.customMenu', () => {
    vscode.window.showInformationMessage("I' am custom menu !")
  })
  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-customMenu')(context) // demo4 自定义菜单
}
export function deactivate() {}
// package.json
{
  "activationEvents": ["onCommand:beehive.customMenu"],
  "contributes": {
    "commands": [
      {
        "command": "beehive.customMenu",
        "title": "demo5: 启动自定义菜单"
      }
    ],
    "menus": {
      "editor/title": [
        {
          "command": "beehive.customMenu",
          "alt": "beehive.customMenu",
          "group": "navigation"
        }
      ]
    }
  }
}

关于这些字段信息,我就不一一贴了,官方文档很多说明,直接看这里: 👉 文档相关字段说明

效果展示

点击之后,触发事件

源码展示

5.悬停提示

场景

当你鼠标光标 hover 至某个代码时,你想要显示一些文字内容

代码展示

// demo5 对package.json中的author进行悬停提示
import * as vscode from 'vscode'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.languages.registerHoverProvider('json', {
    provideHover(document, position, token) {
      const fileName = document.fileName
      const word = document.getText(document.getWordRangeAtPosition(position))
      if (/\/package\.json$/.test(fileName) && /\bauthor\b/.test(word)) {
        return new vscode.Hover('悬停提示: 彭道宽牛逼!')
      }
      return undefined
    },
  })

  context.subscriptions.push(disposable)
}

上面对于文件以及关键词 keyword 可以根据业务自行抽离,这里就不写那么多,自行领悟~

// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-hoverTips')(context) // demo5 悬停提示
}
export function deactivate() {}

效果展示

image

源码展示

更多例子看第二篇文章 : 【KT】vscode插件开发例子系列(二)

打包、发布

  1. 第一步先安装 vsce
npm install vsce -g
  1. 第二步创建账号

首先访问 login.live.com/ 登录你的Microsoft账号,没有的先注册一个,然后访问: aka.ms/SignupAzure… ,如果你从来没有使用过Azure,那么会看到如下提示:

image.png

点击继续,默认会创建一个以邮箱前缀为名的组织。

  1. 第三步进入组织创建令牌

下图为默认创建好的组织

image.png

此时点击右上角的用户设置

image.png

点击创建新的个人访问令牌

image.png

注意这里必须要选择 all accessible organizations,Scopes 要选择 full access,否则后面发布会失败。

image.png

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

创建好之后,就会看到一条信息

image.png

  1. 第四步创建发布账号

获得个人访问令牌后,进入项目,打开终端,使用 vsce 以下命令创建新的发布者:

vsce create-publisher your-publisher-name

your-publisher-name 必须是字母数字下划线,这是全网唯一的账号,然后会依次要求输入昵称、邮箱、令牌,这个令牌就上面记录的令牌。

创建成功后会默认登录这个账号,接下来你可以直接发布了。

如果你是在 marketplace.visualstudio.com/manage 创建的账号,那么你可以 通过 vsce login your-publisher-name 来登录。

  1. 第五步发布插件
vsce publish

可能问题:

  • 前面所说,如果 Organization 没有选择 all accessible organizations,或者 Scopes 没有选择 full access,发布的时候可能会报如下错误
Error: Failed Request: Unauthorized(401) - https://marketplace.visualstudio.com/_apis/gallery
Be sure to use a Personal Access Token which has access to **all accessible accounts**.
See https://code.visualstudio.com/docs/tools/vscecli#_common-questions for more information.

解决方案就是重新创建一个 PAT

  • 令牌过期,因为我们的令牌有时间限制,如果过期了的话,就会在发布时报错
 ERROR  {"$id":"1","customProperties":{"Descriptor":null,"IdentityDisplayName":null,"Token":null,"RequestedPermissions":0,"NamespaceId":"00000000-0000-0000-0000-
 000000000000"},"innerException":null,"message":"Access Denied: The Personal Access Token used has expired.","typeName":"Microsoft.VisualStudio.Services.Security.AccessCheckException, 
 Microsoft.VisualStudio.Services.WebApi","typeKey":"AccessCheckException","errorCode":0,"eventId":3000}

解决方案就是:重新生成一个 Personal Access Token,然后更新一下即可。然后重新登陆一下当前帐号,它会问你是否重写 PAT,输入: y,然后将新生成的 PAT 填进去即可

image.png

唠嗑几句

上面的几个例子差不多够一个新手开发 vscode 插件了,所有的 demo 例子我都放在: learn-vscode-extension中,如果你想看实战源码,请移步这里: 👉 vscode-beehive-extension

如果你开发过vscode插件,你就知道文档写的有多“全”了,属于你知道很全,但是这个属性或者字段在插件中长什么样,得自己去“悟”,很蛋疼,我开发过程,如果需要些部分的插件功能,得去插件市场下载对应插件,使用一波,然后再去 github 阅读对应源码。比如我们常用的 : vscode-gitlens

顺便澄清一下,组员不用 PC 端的项目辅助工具真的不是我写的烂!!!

太多堆积文章需要写了,等过完年再慢慢整理吧,祝大家过个好年

相关链接

夸一下小茗同学的文章,虽然我没怎么认真看内容,我都是看图,然后去看该图对应的源码,不过在前期开发vscode插件,可以说帮助还是比较大的~