背景
最近在参与go项目开发时,发现团队其他小伙伴使用的是Goland,而我使用的是VSCode+gopls。gopls默认使用goimports格式化代码,且无法修改,这样子每次提交代码时import的部分会频繁变动,不利于项目的维护。趁着这个机会,学习了下vscode的插件开发,实现go文件import按字母表顺序排列。
环境
开发VSCode插件只需要安装好VSCode和Node.js,就可以进行开发了。
生成项目
首先安装 yeoman 脚手架生成工具
cmd
npm install -g yo
npm install -g generator-code
然后生成插件项目
shell
yo code
此时会询问你插件的相关信息,我们使用JS开发,插件命名为 go-imports-alphabetical
最后直接用vscode打开
生成的项目结构很简单,我们所有的逻辑写在 extension.js
即可。
shell
drwxr-xr-x 1 KZJ 197609 0 Feb 24 12:46 .git/
drwxr-xr-x 1 KZJ 197609 0 Feb 24 12:46 .vscode/
drwxr-xr-x 1 KZJ 197609 0 Feb 24 12:46 node_modules/
drwxr-xr-x 1 KZJ 197609 0 Feb 24 12:46 test/
-rw-r--r-- 1 KZJ 197609 429 Feb 24 12:46 .eslintrc.json
-rw-r--r-- 1 KZJ 197609 34 Feb 24 12:46 .gitignore
-rw-r--r-- 1 KZJ 197609 113 Feb 24 12:46 .vscode-test.mjs
-rw-r--r-- 1 KZJ 197609 144 Feb 24 12:46 .vscodeignore
-rw-r--r-- 1 KZJ 197609 250 Feb 24 12:46 CHANGELOG.md
-rw-r--r-- 1 KZJ 197609 2.0K Feb 24 12:46 README.md
-rw-r--r-- 1 KZJ 197609 1.4K Feb 24 12:46 extension.js
-rw-r--r-- 1 KZJ 197609 183 Feb 24 12:46 jsconfig.json
-rw-r--r-- 1 KZJ 197609 98K Feb 24 12:46 package-lock.json
-rw-r--r-- 1 KZJ 197609 738 Feb 24 12:46 package.json
-rw-r--r-- 1 KZJ 197609 2.6K Feb 24 12:46 vsc-extension-quickstart.md
extension.js
默认注册了一个helloworld命令,会弹出消息,我们直接按下f5
会打开调试窗口。
在调试窗口按 CTRL+SHIFT+P
,然后输入 Hello World就会调用该插件。
开发
了解了以上信息后我们就可以进行插件开发了。
分析需求
我们希望对go文件的import包进行排序,所以我们需要先解析出import的范围,然后把每一行的pakcage字符串放进js数组中,调用JsArray::sort(),最后修改原先的import范围的字符串就行。
准备工作
首先我们将extension.js中原来的命令由 go-imports-alphabetical.helloWorld
修改为 go-imports-alphabetical.sortImportsInAlphabetical
,这个命令和package.json是要对应的,
我们也修改package.json中的相应属性。title
即为Ctrl+Shift+p
时显示的命令。
获取当前文档
js
const editor = vscode.window.activeTextEditor
if (!editor) {
error("no activated editor")
return
}
const document = editor.document
let text = document.getText()
直接使用vscode提供的api获取当前打开的代码文件。
然后调用document.getText()
便可以获取达凯的文件的字符串。
获取import范围
使用正则来匹配import的范围,典型的go文件import格式为
go
import (
"fmt"
"os"
"github.com/gin/"
)
所以我们定义正则然后调用 JsString::match() 来匹配
javascript
const regex = new RegExp("(import\\s*\\()([\\s\\S]+?)(\\))")
const matchResult = text.match(regex)
let pendingPackages = matchResult[2].split(\n)
*由于在正则使用了(),text.match()会返回匹配的字符串和括号中的内容,matchResult[2]就是上面go文件的
arduino
"fmt"
"os"
"github.com/gin/"
这部分内容
然后我们再使用split即可获取每一行的package的字符串组成的数组。
最后我们遍历pendingPackages
,去除空行,调用sort,便能获取排序后的数组,同时去除空行。
js
let packages = []
for (let i in pendingPackages) {
const val = pendingPackages[i].trim()
if (val.length == 0) continue
packages.push(pendingPackages[i])
}
packages = packages.sort()
之后便可以修改原先的import为排序后的import了。
修改代码
VScode提供了两个类表示编辑框中的光标和选中的范围。
vscode.Position
类
构造器 (line: number, character: number);
指代第几行第几个字符,类似我们写代码时候的光标位置。
如下图为 vscode.Position(1,3)
*注意和ide中显示的行、列不同,代码中是从0开始,ide的用户界面是从1开始
vscode.Range
类 构造器 (start: Position, end: Position);
指代代码范围,也就是我们选中某段代码的范围。
如下图为 vscode.Range( vscode.Position(1,1), vscode.Position(2,3) )
*注意和ide中显示的行、列不同,代码中是从0开始,ide的用户界面是从1开始
了解了以上概念后便可以调用api修改代码了
javascript
let newImportCode = matchResult[1]
newImportCode += "\n"
for (let val of packages) {
newImportCode += val
newImportCode += "\n"
}
newImportCode += matchResult[3]
const offset = text.indexOf(matchResult[1])
const beginPos = document.positionAt(offset)
const endPos = document.positionAt(offset + matchResult[0].length)
const range = new vscode.Range(beginPos, endPos)
editor.edit((edit) => {
edit.replace(range, newImportCode)
})
使用 document.positionAt
可以快速获取相对应的 vscode.Position,最后调用 editor.edit
来修改代码。
最后附上完整的代码 extension.js
javascript
const vscode = require('vscode');
function activate(context) {
let disposable = vscode.commands.registerCommand('go-imports-alphabetical.sortImportsInAlphabetical',
sortImportsInAlphabetical);
context.subscriptions.push(disposable);
}
function deactivate() { }
function info(...args) {
vscode.window.showInformationMessage(...args)
}
function warn(...args) {
vscode.window.showWarningMessage(...args)
}
function error(...args) {
vscode.window.showErrorMessage(...args)
console.error(...args)
}
function fatal(...args) {
console.error(...args)
}
function debug(...args) {
console.log(...args)
}
const regex = new RegExp("(import\\s*\\()([\\s\\S]+?)(\\))")
function sortImportsInAlphabetical() {
try {
const editor = vscode.window.activeTextEditor
if (!editor) {
error("no activated editor")
return
}
const document = editor.document
const text = document.getText()
const matchResult = text.match(regex)
if (!matchResult) {
error("no go imports found")
return
}
const pendingPackages = matchResult[2].split("\n")
let packages = []
for (let i in pendingPackages) {
const val = pendingPackages[i].trim()
if (val.length == 0) continue
packages.push(pendingPackages[i])
}
packages = packages.sort()
let newImportCode = matchResult[1]
newImportCode += "\n"
for (let val of packages) {
newImportCode += val
newImportCode += "\n"
}
newImportCode += matchResult[3]
const offset = text.indexOf(matchResult[1])
const beginPos = document.positionAt(offset)
const endPos = document.positionAt(offset + matchResult[0].length)
const range = new vscode.Range(beginPos, endPos)
editor.edit((edit) => {
edit.replace(range, newImportCode)
})
} catch (e) {
fatal(e)
}
}
module.exports = {
activate,
deactivate
}
按下f5开始调试,在调试的窗口中打开一份go代码,使用快捷键CTRL+SHIFT+P
,调用Sort Go Imports in Alphabetical
就能发现go的import已经按我们想要的顺序排列了。
附录
该项目也进行了开源,进行了一些更新并持续维护中,欢迎提交需求,给个star。 github: github.com/AlpsMonaco/...
同时也已经在vscode marketplace上架
marketplace.visualstudio.com/items?itemN...
欢迎使用~