項目組使用了 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 語言。