VSCode 插件开发

一直想玩一下 VSCode 插件开发,刚好碰到了个需求可以拿来练手。这里记录下整个开发过程。

这个插件的功能就是将当前的打开页面中的 A 格式字符串全部格式化成 B 格式的字符串。

就像下面,执行一个命令或按下一个快捷键,就可以把当前页面中的所有 ![xx](sss) 替换成 ![xx](sss),同时执行一个命令或按下快捷键还能一次性够替换回来。

且先不管这个替换要解决什么问题,总之这就是一个能实现批量替换特定字符串功能的 VSCode 插件。

本文的重点在于如何进行 VSCode 插件开发,和如何进行打包发布。

Hello World

按照国际惯例,先从 Hello World 开始。

  • 安装开发者工具

    1
    $ npm install -g yo generator-code
  • 初始化项目

    1
    $ yo code

初始化项目时会询问一些信息,添加好之后就可以打开它了

打开后的文件很多,但是只需要重点关注三个文件即可:

  • README.md (插件发布以后显示的介绍内容)

  • package.json (插件配置)

  • extension.js (插件代码)

首先从 package.json 开始看起,下面是部分重要内容,icon 是原来没有的,用来指定插件的图片 logo,要求大小是 128*128 的 png 格式

activationEvents 这里配置可以正常激活的命令,这里配置的命令是 extension.sayHello

contributes 这里配置命令的激活方式,commands 配置当调出命令面板时,输入 title 里的内容激活命令,keybindings 是我手动添加的,配置按什么快捷键激活命令

main 配置入口文件的位置,这里配置的是根目录下的 extension.js,可自行改动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"icon": "icon.png",
"activationEvents": [
"onCommand:extension.sayHello"
],
"main": "./extension",
"contributes": {
"commands": [
{
"command": "extension.sayHello",
"title": "Hello World"
}
]
},
"keybindings": [{
"command": "extension.sayHello",
"key": "ctrl+shift+h",
"mac": "cmd+shift+h"
}
]
}

下面再来看 extension.js 里面的内容,这个文件还可以引入其他的 js 文件。

重点看 activate() 这个函数,里面定义了一个 disposable 操作,它要执行的就是之前配置好的 extension.sayHello 命令,这个命令的内容是调用 vscode api 输出 Hello world。最后要将这个操作写到 push 方法里面才能使用。

我们要自己实现的代码就写在这个函数里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const vscode = require('vscode');

function activate(context) {
let disposable = vscode.commands.registerCommand('extension.sayHello', function () {

vscode.window.showInformationMessage('Hello World!');
});

context.subscriptions.push(disposable);
}
exports.activate = activate;

function deactivate() {
}
exports.deactivate = deactivate;

最后来运行一个 Hello World,点击调试就可以个打开一个新窗口,这个新窗口会加载当前插件

按下 Ctrl + Shift + P(mac 是 Cmd + Shift + P)打开命令面板,输入 Hello World 它就会执行 extension.sayHello 命令,然后输入内容。

同时也可以直接按下之前配置好的快捷键激活这个操作。

本文到这里就介绍了 VScode 插件的初始化,命令的配置,以及该在哪里写自己的代码。

插件实现

下面给出了这个要实现的插件的核心代码,分为四个步骤,都给了相应的注释

首先调用 vscode api 提取当前窗口打开的内容,并用正则提取到不包含 a 标签的所有要格式化的内容

然后将这些内容拼接并替换为新的目标内容

接着就把新的内容调用 vscode api 在窗口端替换即可。

反向操作的步骤是一样的,只需要更改正则表达式就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const vscode = require('vscode');

function activate(context) {
let format = vscode.commands.registerCommand('extension.format', function() {
let originText = vscode.window.activeTextEditor.document.getText()

// get all image code without
let getAllImgCodeRegex = /\!\[[^\[\]]*\]\([^\(\)]*\)(?!<\/a>)/g
let imgCode = originText.match(getAllImgCodeRegex)

if (imgCode) {
let newImgCode = imgCode.map((item) => {
let title = item.substring(item.indexOf('[') + 1, item.indexOf(']'))
let url = item.substring(item.indexOf('(') + 1, item.indexOf(')'))
let newItem = ``
return newItem
})

// replace all image code
let newText
let dynamicText = originText
let getOneImgCodeRegex = /\!\[[^\[\]]*\]\([^\(\)]*\)(?!<\/a>)/
for (let i = 0; i < imgCode.length; i++) {
newText = dynamicText.replace(getOneImgCodeRegex, newImgCode[i])
dynamicText = newText
}

// get the Range of the whole text of a document
let textEditor = vscode.window.activeTextEditor
let firstLine = textEditor.document.lineAt(0);
let lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1);
let textRange = new vscode.Range(0,
firstLine.range.start.character,
textEditor.document.lineCount - 1,
lastLine.range.end.character);

// Replace documents with vscode API
let uri = vscode.window.activeTextEditor.document.uri
let textEdits = [];
textEdits.push(new vscode.TextEdit(textRange, newText))
let workspaceEdit = new vscode.WorkspaceEdit()
workspaceEdit.set(uri, textEdits)
vscode.workspace.applyEdit(workspaceEdit)

vscode.window.showInformationMessage('Formatting success.');
} else {
vscode.window.showInformationMessage('Formatting has been completed.');
}
});

let resetFormat = vscode.commands.registerCommand('extension.resetFormat', function() {
// your code
});

context.subscriptions.push(format, resetFormat);
}
exports.activate = activate;

function deactivate() {}
exports.deactivate = deactivate

这个 VSCode 插件的实现就是 js 代码部分加上 VSCode api 部分。这里可以查阅所有 VSCode 插件 api ,全在一页上,非常恐怖。

本插件主要用了两个 API:

这个用来获取当前所打开窗口的文本内容:

1
let originText = vscode.window.activeTextEditor.document.getText()

这个是将 newText 替换当前窗口的全部内容。

这个操作看上去很复杂,这是因为 VSCode 的替换机制确实很复杂,首先要获取到要替换部分的区域坐标,我要替换所有的内容却没有现成的 API,所以前 7 行代码都是在获取整个文档的区域。

而替换操作这是要先获取到当前文档的 uri ,然后才能调取 set 方法并且传入之前要替换的区域,这样才能完成替换操作。完成了之后还要调用 applyEdit 方法应用当前操作。

确实很复杂,下面的这部分代码完全可以封装成一个单独的函数来使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// get the Range of the whole text of a document
let textEditor = vscode.window.activeTextEditor
let firstLine = textEditor.document.lineAt(0);
let lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1);
let textRange = new vscode.Range(0,
firstLine.range.start.character,
textEditor.document.lineCount - 1,
lastLine.range.end.character);

// Replace documents with vscode API
let uri = vscode.window.activeTextEditor.document.uri
let textEdits = [];
textEdits.push(new vscode.TextEdit(textRange, newText))
let workspaceEdit = new vscode.WorkspaceEdit()
workspaceEdit.set(uri, textEdits)
vscode.workspace.applyEdit(workspaceEdit)

总之写一个插件的步骤就是实现自己的代码然后跟 VSCode API 对接,如果不知道有哪些 api 可以用就查文档,如果文档很难懂,就直接 google 。

打包发布

插件开发完成了就可以发布到应用市场了。

注册开发者账号

首先去 这个网站 注册一个账号,如果有微软账号就可以直接登陆

是点击这个登陆,不是网页右上角的登陆:

然后创建一个组织,但是不需要创建工程,然后点击右上角的头像 ——> Security

点击创建一个 Personal access tokens,注意在组织那里一定要选择 All,否则后面会报错,Scope 选全部就行了,其他随意。

之后就会给出一个 tokens,它只会显示一次,要记下来。

发布

  • 首先安装打包发布工具 vsce

    1
    $ npm install -g vsce
  • 然后创建发布者,会提示让输入 tokens,它会记住这个 tokens 的

    1
    $ vsce create-publisher (publisher name)
  • 最后直接发布就行了,如果发布失败就多试几次,一般是网络原因

    1
    $ vsce publish

发布完成之后就可以在应用商店找到自己的插件了。

第一次发布时会提示要改一下 README.md 文件内容,后面发布更新,需要在 package.json 中更改一下 version

本地打包

这条命令可以将插件打包成 VSIX 格式的文件,可以将这个文件发给别人

1
$ vsce package

然后用下面的方式就能导入了。

不过一般这个操作没什么必要,直接发布到应用商店就好了。

0%