VSCode 扩展入门,类型转换代码补全的实现(完整版)

项目组使用了 protobuf 作为数据协议,其好处不用多说。可是由于 go 默认数字类型是 int,且 go 属于强类型语言,切换类型就成了家常便饭。

一般来说,切换类型的步骤包括:

  1. 选中数字部分。
  2. 加括号。
  3. 输入新类型。

时间长了,不免觉得有些复杂。

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 语言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章