代碼片段是一些使用率極高的代碼,開發的過程中可能會頻繁使用到,如果通過設置編輯器或者IDE的Snippets自動完成代碼,可以顯著地提高開發的效率。
一 什麼是Code Snippets
代碼片段(Code Snippets),指的是一些使用率很高的代碼模板,可以是固定的內容(比如文件頭的版權聲明),或者是可以修改的預定義模板,比如for、while循環的模板。
通過Snippet,輸入特定的關鍵詞,就可以在代碼段引擎的幫助下,生成預定義的模板代碼,接着我們還可以通過在預定義的光標位置之間跳轉,來修改補全模板,得到我們最終想要的代碼。
下面是一個添加頭文件文件頭、保護宏及for循環的示例:
在vscode中就有一些很有用的內置Snippets,並且編輯默認是顯示Snippets建議的。
當然,也有相關的插件支持不同語言的Snippets,可以按需安裝。
準則永遠是:用到才安裝,性能和效率至上。
二 自定義Snippets
有的一些插件或者Snippets可能並不能完全滿足我們的需求,因此我們可能存在一些自定義Snippets的需求,下面以兩個例子展開介紹。更多細節可以查看官方文檔: Snippets in Visual Studio Code。
1 創建自定義代碼片段的步驟
Ctrl+Shift+P
打開命令面板,輸入>Preferences: Configure User Snippets
,選擇首選項:配置用戶代碼片段
,然後會彈出面板:
這裏會顯示:
- 用戶已經創建的代碼片段(針對特定語言的(.json),或者是全局的(.code-snippets))
- 可以選擇創建全局代碼片段文件
- 也可以選擇爲當前項目創建代碼片段文件
- 或者針對特定語言的代碼片段文件
選擇創建後需要設置文件名,文件名沒有特殊要求。
確定後會創建代碼片段文件,開頭有示例模板,如下:
"Print to console": {
"scope": "javascript,typescript",
"prefix": "log",
"body": [
"console.log('$1');",
"$2"
],
"description": "Log output to console"
}
主要就是四個部分:
- key:代碼提示彈窗顯示的此Snippet的名字
- scope:表明該Snippet在哪些語言文件內生效
- prefix:指定觸發此Snippet的關鍵字
- body:爲Snippet的模板內容
- description:對此Snippet的描述
一個代碼片段文件中可以有多個Snippets,並不需要創建多個的代碼片段文件。
2 Snippets的基礎語法
vscode中書寫自己的Snippets主要掌握以下幾點語法:
-
Tabstops:表示創建模板後光標所處位置,以及按下tab後光標如何跳轉。比如
$1
是初始位置,按下tab將會跳轉到$2
的位置,以此類推,而$0
表明光標最後的位置; -
Placeholders:帶默認值的tabstops,比如
${1:Hello Farmer!}
,表示光標處的默認內容爲“Hello Farmer!”,可以直接輸入內容修改,或者按tab接受默認內容;Placeholders是可以嵌套的; -
Choice:Placeholders可以包含多個選擇,比如
${1|one,two,three|}
,當輸入到此處時,會彈出一個下拉框供用戶選擇; -
內置變量:包含一些內置的變量,可以在創建模板的時候使用,通過
$name
或者${name:default}
使用,支持的變量參見官方文檔Snippets Variables; - 變量轉換:這部分的語法和shell中是基本一樣的,主要是爲了實現目標字符串變量的截取、替換、修改,或者是大小寫變換。這部分會涉及到一些正則化語法,可以查看官方的demo來找到自己需要的解決辦法。
下面開始實戰兩個例子。
3 自定義C頭文件模板
比如筆者編寫C語言的頭文件的時候,有以下需求:
- 在頭文件開頭添加版權、作者及創建日期的信息
- 規範要求的頭文件保護宏
- 需要和C++混合編譯,所以需要添加
extern "C"
我們一步步來,第1部分,需要用到日期信息,可以使用內置變量實現,如下:
"Add C include guard": {
"scope": "c",
"prefix": "cheader",
"description": "Add include guard to C header file",
"body": [
"// Copyright (c) RealCoolEngineer. $CURRENT_YEAR. All rights reserved.",
"// Author: Farmer Li",
"// Date: $CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE",
"",
]
},
這裏我們定義了scope
爲c
,所以只有c語言的源文件中,此Snippets纔會生效;
prefix
定義爲cheader
,在源文件宏輸入cheader
就可以彈出此Snippet,按tab應用;
body
部分即包含了版權聲明,作者及當前年月日等信息。
然後是第2部分保護宏:
對於c語言,合乎規範的保護宏的格式應該是<PROJECT>_<PATH>_<FILE>_H_
。
比如foo/bar/baz.h
可按如下方式保護:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
所以我們需要知道當前文件的相對位置,然後將其中的路徑分割符轉換成下劃線,再全部大寫,最後再添加一個下劃線即可。
在vscode的Snippets內置變量中,RELATIVE_FILEPATH
表示的就是當前文件相對於項目根目錄的相對路徑,結合變量轉換語法:
transform ::= '/' regex '/' (format | text)+ '/' options
下面這個語句就可以實現我們的想要的效果:
${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}
語法解釋:
RELATIVE_FILEPATH
:就是我們想要操作的目標字符串,例如"foo/bar/baz.h";
([a-zA-Z0-9]+)([\\/\\.-_])?
:是我們想要查找的內容,其中包含兩個組(正則表達式中一對小括號表示一個小組),第1個組的內容是匹配大小寫字母加數字的組合,第2個小組是匹配".-_"中的一個,並且第2小組後的?表示其可以不存在。
所以在這個例子中,我們的正則表達式會匹配到"foo/"、"bar/"、"baz."和"h"。
${1:/upcase}_
:表示我們把正則表達式匹配到的第1個小組大寫,丟棄了第2小組匹配的內容,並且在後面添加一個下劃線,比如匹配到的"foo/"就會被轉換成"FOO_";
g
:表示對正則表達式匹配的所有內容應用前面這個規則,所以"foo/bar/baz.h"就會被處理成"FOO_BAR_BAZ_H_"。
最後我們稍加完善,加上#define
相關語法,就能得到我們想要的效果:
"#ifndef ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
"#define ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
"",
"#endif // ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
""
內置變量
RELATIVE_FILEPATH
是在最近的版本(1.53)中才添加的,筆者也是等了良久啊,所以更新軟件的時候要關注其release notes。
最後的第3部分完全是固定的代碼,所以無需什麼特殊操作,直接往body
內合適的位置添加即可,最後使用$0
表明代碼模板出入後光標的位置即可。最終的代碼片段如下:
"Add C include guard": {
"scope": "c,cpp",
"prefix": "cheader",
"description": "Add include guard to C header file",
"body": [
"// Copyright (c) RealCoolEngineer. $CURRENT_YEAR. All rights reserved.",
"// Author: Farmer Li",
"// Date: $CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE",
"",
"#ifndef ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
"#define ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
"",
"#ifdef __cplusplus",
"extern \"C\" {",
"#endif",
"",
"$0",
"",
"#ifdef __cplusplus",
"}",
"#endif",
"",
"#endif // ${RELATIVE_FILEPATH/([a-zA-Z0-9]+)([\\/\\.-_])?/${1:/upcase}_/g}",
""
]
},
4 自定義for循環模板
最後以for循環模板演示Tabstops和Placeholders的用法。
文章開頭的gif顯示的for循環模板爲vscode自帶的,但是筆者開發遵循的代碼規範要求左大括號和for在同一行,所以稍微有點不符合筆者的要求。
依舊在剛纔創建的代碼片段文件中,新增一個:
"Add for loop": {
"prefix": "for",
"description": "Insert for loop",
"body": [
"for (${1:size_t} ${2:i} = 0; ${2:i} < ${3:count}; ${4:${2:i}++}) {",
"\t${0:/* code */}",
"}"
]
},
如文章開頭的效果所示,源文件內輸入for
選擇我們自定義的模板,就會自動生成以下內容:
for (size_t i = 0; i < count; i++) {
/* code */
}
一開始的時候光標會處在size_t
上,設定循環變量的類型;
按tab後光標會同時在$2
指示的三個位置,此時設定循環變量的名字;
再次按tab,光標跳轉到$3
處,設定循環執行次數;
再次按下tab,則會跳轉到$4
處,設置循環的結束條件;
最後按tab就會進入到for循環內部開始寫循環體中的代碼。
以上便是對vscode中自定義Snippets的介紹,活用Snippets可以極大提高代碼編寫效率,值得花費一些心思。