背景
📢 博客首发 : 阿宽的博客
📢 团队博客: 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.ts 和 package.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
该文件配置项太多,建议去官方文档翻阅
关键的主要是: activationEvents 和 contributes 这两个属性,关于这两个属性你可以看官方文档
下面讲一下 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() {}
效果展示
源码展示
更多例子看第二篇文章 : 【KT】vscode插件开发例子系列(二)
打包、发布
- 第一步先安装 vsce
npm install vsce -g
- 第二步创建账号
首先访问 login.live.com/ 登录你的Microsoft账号,没有的先注册一个,然后访问: aka.ms/SignupAzure… ,如果你从来没有使用过Azure,那么会看到如下提示:
点击继续,默认会创建一个以邮箱前缀为名的组织。
- 第三步进入组织创建令牌
下图为默认创建好的组织
此时点击右上角的用户设置
点击创建新的个人访问令牌
注意这里必须要选择 all accessible organizations,Scopes 要选择 full access,否则后面发布会失败。
注意!!!创建令牌成功后你需要本地记下来,因为网站是不会帮你保存的!!!
创建好之后,就会看到一条信息
- 第四步创建发布账号
获得个人访问令牌后,进入项目,打开终端,使用 vsce 以下命令创建新的发布者:
vsce create-publisher your-publisher-name
your-publisher-name 必须是字母数字下划线,这是全网唯一的账号,然后会依次要求输入昵称、邮箱、令牌,这个令牌就上面记录的令牌。
创建成功后会默认登录这个账号,接下来你可以直接发布了。
如果你是在 marketplace.visualstudio.com/manage 创建的账号,那么你可以 通过 vsce login your-publisher-name 来登录。
- 第五步发布插件
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 填进去即可
唠嗑几句
上面的几个例子差不多够一个新手开发 vscode 插件了,所有的 demo 例子我都放在: learn-vscode-extension中,如果你想看实战源码,请移步这里: 👉 vscode-beehive-extension
如果你开发过vscode插件,你就知道文档写的有多“全”了,属于你知道很全,但是这个属性或者字段在插件中长什么样,得自己去“悟”,很蛋疼,我开发过程,如果需要些部分的插件功能,得去插件市场下载对应插件,使用一波,然后再去 github 阅读对应源码。比如我们常用的 : vscode-gitlens等
顺便澄清一下,组员不用 PC 端的项目辅助工具真的不是我写的烂!!!
太多堆积文章需要写了,等过完年再慢慢整理吧,祝大家过个好年
相关链接
夸一下小茗同学的文章,虽然我没怎么认真看内容,我都是看图,然后去看该图对应的源码,不过在前期开发vscode插件,可以说帮助还是比较大的~