【VSC】Snippets不徹底指南

更多文章,參見大搜車技術博客:blog.souche.com/javascript

大搜車無線開發中心持續招聘中,前端,Nodejs,android 均有 HC,簡歷直接發到:sunxinyu@souche.com前端

前言

你們都是出來寫代碼的,少不了要寫上千萬來行代碼,其中重複性的代碼佔比又會很大。那麼如何避免一次又一次寫重複性的代碼呢?除了代碼自身的優雅、可複用,Jetbrains系如Intellij IDEA的Live Templates或Visual Studio Code的Snippet等內置工具都能很大程度上幫咱們少寫不少重複性的代碼。下面就vsc的Snippet,結合demo,講講它的用法。java

建立Snippet

快捷鍵Cmd + Shift + PF1打開命令窗口,輸入snippet,選中配置用戶代碼片斷,這時候會彈出react

能夠在現有的代碼片斷文件上新增snippet,也能夠本身新建代碼片斷文件。android

Snippets片斷以JSON格式定義,官方提供的例子以下正則表達式

{
    "For_Loop": {
        "prefix": "for",
        
        /**
         * 在全局代碼片斷文件新增snippet時,
         * 經過該項來指定該snippet使用範圍,
         * 以下所示,在文件以`.js`或者`.ts`結尾時可以使用該snippet
         */
        "scope": "javascript,typescript",   
        
        "body": [
          "for (const ${2:element} of ${1:array}) {",
          "\t$0",
          "}"
        ],
        "description": "For Loop"
    }
}
複製代碼
  • For_Loop is the snippet name(後續在指定快捷鍵綁定的時候會用到).
  • prefix defines how this snippet is selected from IntelliSense and tab completion. In this case for.(前綴支持N:1,好比prefix值爲["for","fof"],則forfof對應同一條代碼片斷)
  • body is the content and either a single string or an array of strings of which each element will be inserted as separate line.
  • description is the description used in the IntelliSense drop down(非必填).

上述英文解釋引自官方,怕翻譯不到位影響讀者理解,就不做翻譯了。typescript

下面用一張圖展現在使用中各個屬性對應的位置express

上圖左側框中的爲 prefix,右側圈中的爲 description, description下方的爲 body部份內容

Snippet語法

Snippet的語法集中在body上。json

Tabstops: 即$1$2等。使用$1$2等表示按下鍵盤tab後光標將要指向的位置。根據數字大小表示前後順序。$0表示光標最後指向的位置。能夠存在多個相同的Tabstops,並會同步更新。c#

如下是多個相同的Tabstops同步更新的示例

{
    "For_Loop": {
        "prefix": "for",
        "scope": "javascript,typescript",
        "body": [
          "for (const ${2:element} of ${1:array}) {",
          "\tconst item = $1[$2];$0",
          "}"
        ],
        "description": "For Loop"
    }
}
複製代碼

如下是使用上述snippet的過程

Placeholders:佔位符。用戶直接跳過Tabstops不輸入新值時,會使用佔位符。它還能內嵌。

如下是佔位符內嵌的示例

{
    "placeholder": {
    "prefix": "ph",
        "body": "${1:hello ${2:world}}$0"
    }
}
複製代碼

如下是使用上述snippet的過程

Choice: 可選的佔位符

以以下snippet爲例

{
    "choice": {
        "prefix": "ch",
        "body": "${1|hello,hi,how are you|}"
    }
}
複製代碼

輸入ch按下tab鍵後,就會顯示

注: 在Choice中,,|$}\可使用\轉義;其餘狀況有且僅$}\可使用\轉義,其餘字符沒法轉義, 詳見https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar

Variables: 官方定義的變量

TM_SELECTED_TEXT:當前選定的文本或空字符串; 
    TM_CURRENT_LINE:當前行的內容;
    TM_CURRENT_WORD:光標所處單詞或空字符串 
    TM_LINE_INDEX:行號(從零開始);
    TM_LINE_NUMBER:行號(從一開始);
    TM_FILENAME:當前文檔的文件名;
    TM_FILENAME_BASE:當前文檔的文件名(不含後綴名);
    TM_DIRECTORY:當前文檔所在目錄;
    TM_FILEPATH:當前文檔的完整文件路徑;
    CLIPBOARD:當前剪貼板中內容。
    時間相關
    CURRENT_YEAR: 當前年份;
    CURRENT_YEAR_SHORT: 當前年份的後兩位;
    CURRENT_MONTH: 格式化爲兩位數字的當前月份,如 02;
    CURRENT_MONTH_NAME: 當前月份的全稱,如 July;
    CURRENT_MONTH_NAME_SHORT: 當前月份的簡稱,如 Jul;
    CURRENT_DATE: 當天月份第幾天;
    CURRENT_DAY_NAME: 當天周幾,如 Monday;
    CURRENT_DAY_NAME_SHORT: 當天周幾的簡稱,如 Mon;
    CURRENT_HOUR: 當前小時(24 小時制);
    CURRENT_MINUTE: 當前分鐘;
    CURRENT_SECOND: 當前秒數。
    註釋相關
    BLOCK_COMMENT_START: 在PHP中輸出 /* ,在HTML中輸出 <!--
    BLOCK_COMMENT_END: 在PHP中輸出 */ ,在HTML中輸出 -->
    LINE_COMMENT: 在PHP中輸出 // ,在HTML中輸出 <!-- -->
複製代碼

Variable transforms 變量轉換可將變量的值格式化處理後插入預約的位置。 它包括三個部分:

  1. 正則表達式
  2. 格式字符串
  3. 正則表達式匹配選項

正則表達式和正則表達式匹配選項不做解釋。

格式字符串在官方文檔的Grammar中以下

format      ::= '$' int | '${' int '}'
                | '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}'
                | '${' int ':+' if '}'
                | '${' int ':?' if ':' else '}'
                | '${' int ':-' else '}' | '${' int ':' else '}'
複製代碼

假設某總體片斷爲${variable/regexp/(format|text)/options},再結合上述語法, 總體片斷可變爲

  1. ${var_name/regular_expression/$1/options}
  2. ${var_name/regular_expression/${1}/options}
  3. ${var_name/regular_expression/${1:/upcase}/options} // /downcase/capitalize使用方式同/upcase
  4. ${var_name/regular_expression/${1:+if}/options}
  5. ${var_name/regular_expression/${1:?if:else}/options}
  6. ${var_name/regular_expression/${1:-else}/options}
  7. ${var_name/regular_expression/${1:else}/options}
  8. ${var_name/regular_expression/text/options}

上述format$後的數字1表示分組捕獲組(正則表達式中()匹配到的數組)的第1項,同理$2表示分組捕獲項第2項。分組捕獲相關信息詳見分組捕獲

現有文件名global-js.json,以demo說明上述片斷含義。爲了精簡,輸出值直接以body末尾註釋表示,另外多個body直接放在一個snippet(實際操做不規範,僅僅爲了精簡)

注:使用transform末尾必須加/,才能被識別。如${TM_FILENAME_BASE/(global).*/$1/}


其中12意義相同

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global).*/$1/}"    // 輸出: global
}
// 待匹配字符串 global-js
// 正則表達式 (global).*
// 所有匹配項 global-js
// 分組捕獲組 ['global'],即$1=global
// $1替換global-js,即輸出 global
複製代碼

3表示對匹配到的相應分組捕獲組轉化成大寫,或小寫,或首字母大寫,以替換正則表達式匹配到的所有字符串

代碼片斷(該片斷僅展現轉化成大寫)
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global).*/${1:/upcase}/}"  // 輸出: GLOBAL
}
// 待匹配字符串 global-js
// 正則表達式 (global).*
// 所有匹配項 global-js
// 分組捕獲組 ['global'],即$1=global
// 經${1:/upcase}轉換後,爲GLOABl
// ${1:/upcase}替換global-js,即輸出GLOABl
複製代碼

4表示匹配成功時,將if所述語句徹底替換正則表達式匹配項。

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:+if}/}",   //輸出: if-js
    // 待匹配字符串 global-js
    // 正則表達式 (global)
    // 所有匹配項 global
    // 分組捕獲組 ['global'],即$1=global
    // $1匹配成功, if替換global,即輸出if-js

    "body": "${TM_FILENAME_BASE/(global).*/${1:+if}/}"   //輸出: if
    // 待匹配字符串 global-js
    // 正則表達式 (global).*
    // 所有匹配項 global-js
    // 分組捕獲組 ['global'],即$1=global
    // $1匹配成功, if替換global-js,即輸出if
}
複製代碼

上述三、4兩個示例匹配失敗時,不作轉換,即仍輸出global.js


5表示匹配成功,且相應的分組捕獲成功時,將if所述語句徹底替換正則表達式匹配項;

匹配成功,且相應的分組捕獲爲空時,將else所述語句徹底替換正則表達式匹配項;

匹配失敗時,else所述語句即爲處理後的變量

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:?if:else}/}",   //輸出: if-js
    // 待匹配字符串 global-js
    // 正則表達式 (global)
    // 所有匹配項 global
    // 分組捕獲組 ['global'],即$1=global
    // $1匹配成功, if替換global,即輸出if-js

    "body": "${TM_FILENAME_BASE/global/${1:?if:else}/}",   //輸出: else-js
    // 待匹配字符串 global-js
    // 正則表達式 global
    // 所有匹配項 global
    // 分組捕獲組 [],$1對應的分組捕獲爲空
    // else替換global,即輸出else-js

    "body": "${TM_FILENAME_BASE/(globald)/${1:?if:else}/}",   //輸出: else
    // 待匹配字符串 global-js
    // 正則表達式 (globald),匹配失敗
    // 所有匹配項 null
    // 分組捕獲組 無
    // 輸出 else
}
複製代碼

67,表示匹配成功,且分組捕獲(正則表達式中()匹配到的項)成功時,不變;

匹配成功,且分組捕獲爲空時,將else所述語句徹底替換正則表達式匹配項;

匹配失敗時,else所述語句即爲處理後的變量

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:else}/}",   //輸出: global-js
    // 待匹配字符串 global-js
    // 正則表達式 (global)
    // 所有匹配項 global
    // 分組捕獲組 ['global'],即$1=global
    // 不變,輸出global-js

    "body": "${TM_FILENAME_BASE/global/${1:else}/}",   //輸出: else-js
    // 待匹配字符串 global-js
    // 正則表達式 global
    // 所有匹配項 global
    // 分組捕獲組 [],$1對應的分組捕獲爲空
    // else替換global,輸出else-js

     "body": "${TM_FILENAME_BASE/(globald)/${1:else}/}",   //輸出: else
    // 待匹配字符串 global-js
    // 正則表達式 (globald),匹配失敗
    // 所有匹配項 null
    // 分組捕獲組 無
    // 輸出 else
}
複製代碼

8表示匹配成功時,將text徹底替換正則表達式匹配項; 匹配失敗時,不變

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/text/}",   //輸出: text-js
}
複製代碼

Placeholder Transform 本質與Variable Transform的第8條一致,只是正則表達式變爲佔位符常量

代碼片斷
"transform": {
    "prefix": "tr",
    "body": "$1 -> ${1/placeholder/text/}",   //輸入: placeholder 輸出: placeholder -> text
}
複製代碼

快捷鍵導入snippet

首先打開keybindings.json

而後能夠添加以下

{
  "key": "cmd+k 1",
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "snippet": "console.log($1)$0"
  }
}
複製代碼

亦或者導入已有的snippet

{
  "key": "cmd+k 1",
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "langId": "csharp", // 語言包,如javascript,typescript
    "name": "myFavSnippet"  // 對應最開始提過的name,以下面使用場景的`import snippet`
  }
}
複製代碼

使用場景

  1. snippet寫snippet
"import snipppet": {
    "prefix": "sni",
    "body": ["\"$1\": {", "\t\"prefix\": \"$2\",", "\t\"body\": [\"$3\"]", "}"]
  },
複製代碼

加個可選的description

"snipppet": {
    "prefix": "sni",
    "body": [
      "\"$1\": {",
      "\t\"prefix\": \"$2\",",
      "\t\"body\": [\"$3\"]${4:,\n\t\"description\":\"${5}\"}",
      "}"
    ]
  }
複製代碼
  1. 某些固定格式的數組對象

如某個數組長度較大的對象{title:'input',dataIndex:'input'},該數組中對象的每一個titledataIndex不盡相同,則能夠寫個臨時snippet,直接生成該結構。

"templates": {
    "prefix": "tem",
    "body": [",{dataIndex:$1,", "title:$2}"],
    "description": ""
}
複製代碼
  1. 某些固定內容的文件或者某些經常使用的import組合
"muji store file": {
    "prefix": "store",
    "body": [
      "import { createStore } from '@souche-f2e/muji'",
      "",
      "type IState = {",
      "",
      "}",
      "const state: IState = {",
      "",
      "}",
      "const store = createStore({",
      " state,",
      " reducers: {",
      " ",
      " },",
      " effects: {",
      " ",
      " },",
      "})",
      "export default store"
    ]
  },
複製代碼
  1. muji react 下的pages文件
"index.tsx under pages": {
    "prefix": "ind",
    "scope": "typescriptreact",
    "body": [
      "import React, { Component } from 'react'",
      "import { dispatch, IRootState } from '@@/store'",
      "import { connect } from '@souche-f2e/muji'",
      "const mapStateToProps = (state: IRootState) => state.${1/(.*)/${1:/downcase}/}",
      "",
      "interface I$1Props extends ReturnType<typeof mapStateToProps> {",
      " ",
      "}",
      "interface I${1}State {",
      "",
      "}",
      "class $1 extends Component<I${1}Props, I${1}State> {",
      " render() {",
      " return ${3:null}",
      " }",
      "}",

      "export default connect(mapStateToProps)($1)"
    ]
  }
複製代碼

只須要輸入組件名,就能夠一次性生成必備的格式。

最後

安利一下prettier,配合snippet使用,簡直絕配。

相關文章
相關標籤/搜索