项目组使用了 protobuf 作为数据协议,其好处不用多说。可是由于 go 默认数字类型是 int,且 go 属于强类型语言,切换类型就成了家常便饭。
一般来说,切换类型的步骤包括:
- 选中数字部分。
- 加括号。
- 输入新类型。
时间长了,不免觉得有些复杂。
vscode + gopls 没有对后缀代码补全的支持,需要通过扩展支持。详见 gopls 官方 issue。
已有的扩展 https://github.com/yokoe/vscode-postfix-go 是一个接近的选择,支持 len
这类表达。
可惜该扩展没有提供数值转换相关的支持。
在 issue 提出建议没有收到回应后,我决定在他的基础进行补充,毕竟两种表达相差不多。
Fork
首先将原项目 fork 出来,对作者信息以及许可证进行修改。在这个过程中我发现它也是在别人的基础上 fork 而来的,感谢开源精神。
npm
vscode 扩展是通过 ts/js 实现的,自然少不了 npm 的使用。在 sudo pacman -S npm
安装好 npm 后(笔者为 arch 发行版)。
然后在项目目录使用 sudo npm install
安装工程。
~/code/vscode-postfix-go (master) [05:14:53]
p1gd0g$sudo npm install
> [email protected] postinstall
> node ./node_modules/vscode/bin/install
Detected VS Code engine version: ^1.12.0
Found minimal version that qualifies engine range: 1.12.0
Fetching vscode.d.ts from: https://raw.githubusercontent.com/Microsoft/vscode/72672be0b7d3eef0784077b880615f91b7ec85aa/src/vs/vscode.d.ts
vscode.d.ts successfully installed!
Coding
在该项目中很容易就可以找到对应后缀代码补全的代码。
比如 len 后缀的实现:
export class LenTemplate extends BaseExpressionTemplate {
buildCompletionItem (code: string, position: vsc.Position) {
const dotIdx = code.lastIndexOf('.', position.character)
const codeBeforeDot = code.substr(0, dotIdx)
// 找到 "." 前的最后一个单词/部分
let lastComponent = getLastComponent(codeBeforeDot)
let builder = CompletionItemBuilder
.create('len', lastComponent)
.description(`len(expr)`)
// 插入 len(*)
builder.insertText('len(' + lastComponent + ')')
// 删除原字符
builder.deleteTextBeforeCursor(position, lastComponent.length + 1)
return builder.build()
}
}
export const build = () => new LenTemplate()
和 type 的实现:
export class TypeTemplate extends BaseExpressionTemplate {
constructor (private keyword: string) {
super()
}
buildCompletionItem (code: string, position: vsc.Position) {
return CompletionItemBuilder
.create(this.keyword, code)
.description(`type expr ${this.keyword}`)
.replace(`type {{expr}} ${this.keyword} {\n${getIndentCharacters()}\${0}\n}`, position, true)
.build()
}
}
// 我们需要了解的重点在这里,复用同一个逻辑去实现不同的代码补全
export const build = () => [
new TypeTemplate('struct'),
new TypeTemplate('interface')
]
我们要做的就是将两者结合起来。我想即使是没有接触 js/ts 的同学,只要有一定的编程基础,不难完成这一目标。
export class IntTemplate extends BaseExpressionTemplate {
constructor(private keyword: string) {
super()
}
buildCompletionItem(code: string, position: vsc.Position) {
const dotIdx = code.lastIndexOf('.', position.character)
const codeBeforeDot = code.substr(0, dotIdx)
let lastComponent = getLastComponent(codeBeforeDot)
let builder = CompletionItemBuilder
.create(this.keyword, lastComponent)
.description(this.keyword + '(expr)')
// 这里借鉴 len
builder.insertText(this.keyword + '(' + lastComponent + ')')
builder.deleteTextBeforeCursor(position, lastComponent.length + 1)
return builder.build()
}
}
// 这里借鉴 type
export const build = () => [
new TypeTemplate('int64'),
new TypeTemplate('uint32'),
new TypeTemplate('int')
]
Debug
通过 vscode 自带的调试,就可以看到我们想要的结果。
Package & Publish
打包和发布的流程请参考官方文档。https://code.visualstudio.com/api/working-with-extensions/publishing-extension
发布成功后即可在 vscode 扩展上商店中看到我们自己的扩展。
以上内容不限于 go 语言。