vscode進階:運用代碼片段提高效率

代碼片段是一些使用率極高的代碼,開發的過程中可能會頻繁使用到,如果通過設置編輯器或者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,選擇首選項:配置用戶代碼片段,然後會彈出面板:

這裏會顯示:

  1. 用戶已經創建的代碼片段(針對特定語言的(.json),或者是全局的(.code-snippets))
  2. 可以選擇創建全局代碼片段文件
  3. 也可以選擇爲當前項目創建代碼片段文件
  4. 或者針對特定語言的代碼片段文件

選擇創建後需要設置文件名,文件名沒有特殊要求。
確定後會創建代碼片段文件,開頭有示例模板,如下:

"Print to console": {
    "scope": "javascript,typescript",
    "prefix": "log",
    "body": [
        "console.log('$1');",
        "$2"
    ],
    "description": "Log output to console"
}

主要就是四個部分:

  1. key:代碼提示彈窗顯示的此Snippet的名字
  2. scope:表明該Snippet在哪些語言文件內生效
  3. prefix:指定觸發此Snippet的關鍵字
  4. body:爲Snippet的模板內容
  5. description:對此Snippet的描述

一個代碼片段文件中可以有多個Snippets,並不需要創建多個的代碼片段文件。

2 Snippets的基礎語法

vscode中書寫自己的Snippets主要掌握以下幾點語法:

  1. Tabstops:表示創建模板後光標所處位置,以及按下tab後光標如何跳轉。比如$1是初始位置,按下tab將會跳轉到$2的位置,以此類推,而$0表明光標最後的位置;
  2. Placeholders:帶默認值的tabstops,比如${1:Hello Farmer!},表示光標處的默認內容爲“Hello Farmer!”,可以直接輸入內容修改,或者按tab接受默認內容;Placeholders是可以嵌套的;
  3. Choice:Placeholders可以包含多個選擇,比如${1|one,two,three|},當輸入到此處時,會彈出一個下拉框供用戶選擇;
  4. 內置變量:包含一些內置的變量,可以在創建模板的時候使用,通過$name或者${name:default}使用,支持的變量參見官方文檔Snippets Variables
  5. 變量轉換:這部分的語法和shell中是基本一樣的,主要是爲了實現目標字符串變量的截取、替換、修改,或者是大小寫變換。這部分會涉及到一些正則化語法,可以查看官方的demo來找到自己需要的解決辦法。

下面開始實戰兩個例子。

3 自定義C頭文件模板

比如筆者編寫C語言的頭文件的時候,有以下需求:

  1. 在頭文件開頭添加版權、作者及創建日期的信息
  2. 規範要求的頭文件保護宏
  3. 需要和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",
            "",
        ]
    },

這裏我們定義了scopec,所以只有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可以極大提高代碼編寫效率,值得花費一些心思。

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