HyperSnips:解放Coding速度的VSCode自動補全插件
本人開發(fā)一直都喜歡vscode,由于RTL開發(fā)時冗余代碼很多,對于重復(fù)的冗余代碼,進行代碼片段化,借助了官方的snippet功能,但是每次進行設(shè)置代碼片段都需要進行手動調(diào)整對齊換行,很不友好,效率也不高。
但是最近發(fā)現(xiàn)一個小眾但是巨好用的VSCode自動補全插件:HyperSnips,這個插件實現(xiàn)了類似snippet的功能,但是它的補全功能更加強大,而且可以自定義補全內(nèi)容,還可以自定義補全的觸發(fā)方式。由于這個插件使用的是JavaScript進行開發(fā)設(shè)計,所以具有JavaScript編程特性,甚至可以拿到 VSCode 的 API 接口,可以豐富插件功能,利用插件實現(xiàn)一些編輯器的操作或者模仿其他插件的功能。(根據(jù)腦洞拓展)
安裝
在vscode中搜索HyperSnips,安裝即可。
基礎(chǔ)配置
安裝完插件,使用ctrl+shift+p打開命令面板,輸入HyperSnips,選擇Open Snippets Directory,打開插件的snippets的文件夾。在這個文件夾下新建 *.hsnips 文件,如希望在Verilog補全就建立verilog.hsnips。如果希望使用全局片段,則建立 all.hsnips。
基本用法
代碼片段
這個插件的語法類似Vim Ultisnips,snippet和endsnippet標志了片段的開始和結(jié)束。下面寫一個最簡單的片段:
snippet hello "greeting"
hello world!
endsnippet
hello是觸發(fā)字符。"greeting"是片段名稱用于標識片段。第二行就是要展開的片段。此時如果輸入hello,就會提示補全展開hello world!。
用法類似VSCode自帶的片段,比Vscode方便是Vscode去構(gòu)建json文件的"body"時,非常的繁瑣,雖然可以借助腳本和小工具進行轉(zhuǎn)換,但是還是比較麻煩。而HyperSnips插件在片段包圍內(nèi)的多行輸入直接可以把需要片段化的代碼和內(nèi)容進行寫入,空格和換行會自動保留,所以使用起來非常方便。
同樣,它也可以使用占位符,可以使用Tab鍵實現(xiàn)跳轉(zhuǎn)位置,例如:
snippet alwayscomb "Generate always_comb" A
always@(*)begin
if(i_rst_n == 1'b0)begin
$1 = ;
end
else if(${2:condition})begin
${3:logic} = ;
end
else begin
${4:logic} = ;
end
end
endsnippet
占位符最簡單的方式有以下兩種:
- 直接使用數(shù)字,例如表示第一個占位符的位置,2表示第二個,$0是最后一個;
- 大括號中使用數(shù)字和文本,例如${1:text}表示第一個占位符中有預(yù)設(shè)的文本。
執(zhí)行效果如下:
在這個代碼片段后可以看到有一個“flags”字段這里填充的是A,這個字段可以用來修改代碼片段的行為:
- A:自動代碼片段擴展 — 通常,按下 tab 鍵時會激活代碼片段,使用 A 標志,代碼片段一旦其觸發(fā)條件匹配就會激活,這對于正則表達式代碼片段特別有用。
- i:詞內(nèi)擴展 — 默認情況下,代碼片段觸發(fā)僅在觸發(fā)條件前面有空白字符時才匹配。具有此選項的代碼片段無論前面的字符是什么都會被觸發(fā),例如,代碼片段可以在一個單詞的中間被觸發(fā)。
- w:單詞邊界 — 使用此選項,代碼片段觸發(fā)將在觸發(fā)條件為單詞邊界字符時匹配。例如,使用此選項可以允許在觸發(fā)條件跟隨標點符號時進行擴展,而不會擴展較大單詞的后綴。
- b:行首擴展 — 具有此選項的代碼片段僅在 tab 觸發(fā)條件是該行的第一個單詞時才會擴展。換句話說,如果只有空白字符在 tab 觸發(fā)條件之前,則進行擴展。
- M:多行模式 — 默認情況下,正則表達式匹配僅匹配當前行的內(nèi)容,啟用此選項后,最后 hsnips.multiLineContext 行將可用于匹配。
需要注意標志設(shè)置A時,這個只有在全部的觸發(fā)字符完成匹配時才會執(zhí)行,以上面的alwayscomb片段為例,當輸入我輸入簡寫alco,不會自動完成匹配輸入,而當我全部鍵入alwayscomb時,會自動觸發(fā)。
內(nèi)嵌代碼與正則識別
其實如果只需要上面的功能,vscode自帶的代碼片段也可以實現(xiàn),但是HyperSnips的正則識別功能更加強大,例如:
snippet `if(\d+)` "Generate N-level if-else if structure" iA
``
let levels = Number(m[1]);
let conditionBlocks = [];
let tabIndex = 2; // 新增 tabstop 索引計數(shù)器
for (let i = 0; i < levels; i++) {
conditionBlocks.push(
i === 0 ?
`if ( ${snip.tabstop(tabIndex++)} ) begin\n ${snip.tabstop(1,'logic')}\nend` :
`else if ( ${snip.tabstop(tabIndex++)} ) begin\n ${snip.tabstop(1,'logic')}\nend`
);
}
rv = conditionBlocks.join('\n');
``
endsnippet
這個片段可以生成N級的if-else if結(jié)構(gòu),例如輸入if(3),會生成3級的if-else if結(jié)構(gòu)。
有了這個功能,我們就可以給自己的代碼片段添加一些動態(tài)的功能。如果我想在進行時序邏輯塊設(shè)計時,可以利用內(nèi)嵌的代碼功能獲取模塊的時鐘和復(fù)位信號的名稱,這樣我就不用重復(fù)修改代碼片段了。
snippet alwaysff "Generate always_ff" A
``
// 獲取當前文檔內(nèi)容
const vscode = require('vscode');
const editor = vscode.window.activeTextEditor;
const currentDocument = editor.document;
const text = currentDocument.getText();
// 查找時鐘信號
const clkMatch = text.match(/\bi_clk\w*/);
const clkSignal = clkMatch ? clkMatch[0] : 'i_clk';
// 查找復(fù)位信號
const rstMatch = text.match(/\bi_rst\w*/);
const rstSignal = rstMatch ? rstMatch[0] : 'i_rst_n';
rv = `always@(posedge ${clkSignal} or negedge ${rstSignal})begin
\tif(${rstSignal} == 1'b0)begin
\t\t${snip.tabstop(1,'logic')} <= ${snip.tabstop(2,'rst_value')};
\tend
\telse if(${snip.tabstop(3,'condition')})begin
\t\t${snip.tabstop(1,'logic')} <= ${snip.tabstop(4,'logic_value')};
\tend
end`;
``
endsnippet
如果之前有設(shè)計一些自動化處理的腳本,同樣可以使用這個插件的代碼片段功能進行集成。
動態(tài)片段
使用插件的示例片段:
snippet box "Box" A
``rv = '┌' + '─'.repeat(t[0].length + 2) + '┐'``
│ $1 │
``rv = '└' + '─'.repeat(t[0].length + 2) + '┘'``
endsnippet
JavaScript片段使用四個 ` 括起來,其中rv表示返回的片段。在這個片段中,rv = '┌' + '─'.repeat(t[0].length + 2) + '┐'
和rv = '└' + '─'.repeat(t[0].length + 2) + '┘'
是內(nèi)嵌的代碼,會在片段展開時執(zhí)行。 執(zhí)行效果如下:
全局程序
在.hsnips文件開頭加入如下片段:
global
endglobal
即可在其中定義全局函數(shù)。這里定義的函數(shù)可以在當前文件中的任何片段中使用。 例如,我們在這個片段中加入以下內(nèi)容:
// vscode api
const vscode = require("vscode");
let editor=vscode.window.activeTextEditor
let document=editor.document
這樣就可以在當前文件中使用vscode的api了。
時間小插件
那么可以利用這個功能實現(xiàn)在vscode編輯器做插件,例如:
// show time in StatusBar
setInterval(function(){myTimer()},1000);
function myTimer(){
vscode.window.setStatusBarMessage(new Date().toLocaleTimeString());
}
這樣就可以在狀態(tài)欄中顯示當前時間了。
自定義按鈕
如果我們常用語言的代碼片段文件,每次都使用ctrl+shift+p打開命令面板,輸入HyperSnips,選擇Open Snippets File,這樣就會非常的麻煩,那么可以在下面的狀態(tài)欄中加入一個按鈕,點擊按鈕就可以打開代碼片段文件了。同時可以設(shè)計一個功能讓用戶在修改代碼片段后,完成插件的重新加載,只需要在全局程序的片段中加入如下內(nèi)容:
// 創(chuàng)建底部欄圖標
const disposable = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
disposable.text = '$(file-code) Verilog.hsnips'; // 使用 VS Code 圖標,可替換為其他圖標
disposable.command = 'extension.mycmd'; // 定義點擊命令
disposable.show();
// 注冊點擊命令
vscode.commands.registerCommand('extension.mycmd', async () => {
const filePath = vscode.Uri.file('C:/Users/Administrator/AppData/Roaming/Code/User/globalStorage/draivin.hsnips/hsnips/verilog.hsnips');
try {
// 打開文件
const document = await vscode.workspace.openTextDocument(filePath);
// 顯示文件
await vscode.window.showTextDocument(document);
} catch (error) {
vscode.window.showErrorMessage(`打開文件時出錯: ${error.message}`);
}
});
// 監(jiān)聽文件保存事件
vscode.workspace.onDidSaveTextDocument((document) => {
if (document.fileName.endsWith('.hsnips')) {
// 重啟 VS Code 窗口以重載插件
vscode.commands.executeCommand('workbench.action.reloadWindow');
}
});
實現(xiàn)效果如下:
總結(jié)
這個插件的功能非常強大,而且可以自定義補全內(nèi)容,還可以自定義補全的觸發(fā)方式。開發(fā)者可以根據(jù)自己的需求進行代碼片段設(shè)計和插件功能擴展。因為插件用到了javascript,所以可能很多人開發(fā)相關(guān)功能會有些難度,但是現(xiàn)在都可以交給AI來完成(內(nèi)置代碼和自定義相關(guān)功能均是由AI輔助開發(fā)完成)
當然,這個插件的功能也有一些不足,例如:
- 插件會存在有時加載問題,需要重新加載插件或者重新打開文件;
- 當疊加了太多的占位符時,可能出現(xiàn)多光標。