45

【VSC】Snippets不完全指南

 5 years ago
source link: https://blog.souche.com/untitled-18/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

大家都是出来写代码的,少不了要写上千万来行代码,其中重复性的代码占比又会很大。那么如何避免一次又一次写重复性的代码这无聊的事情呢?除了代码自身的优雅、可复用,Jetbrains系如Intellij IDEA的 Live Templates 或Visual Studio Code的 Snippet 等内置工具都能很大程度上帮我们少写很多重复性的代码。下面就vsc的 Snippet ,结合demo,讲讲它的用法。

创建Snippet

快捷键 Cmd + Shift + PF1 打开命令窗口,输入 snippet ,选中 配置用户代码片段 ,这时候会弹出

aeayQri.png!web

可以在现有的代码片段文件上新增snippet,也可以自己新建代码片段文件。

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(非必填).

上述英文解释引自 官方 ,怕翻译不到位影响读者理解,就不作翻译了。

下面用一张图展示在使用中各个属性对应的位置

2myQn2U.png!web 上图左侧框中的为 prefix ,右侧圈中的为 description , description 下方的为 body 部分内容

Snippet语法

Snippet 的语法集中在 body 上。

Tabstops : 即 $1$2 。使用 $1$2 等表示按下键盘 tab 后光标将要指向的位置。根据数字大小表示先后顺序。 $0 表示光标最后指向的位置。可以存在多个相同的 Tabstops ,并会同步更新。

以如下snippet为例

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

以下是使用上述snippet的过程

vYVRb2n.gif

Placeholders :占位符。用户直接跳过 Tabstops 不输入新值时,会使用占位符。它还能内嵌。

以如下snippet为例

{
    "placeholder": {
    "prefix": "ph",
        "body": "${1:hello ${2:world}}$0"
    }
}

以下是使用上述snippet的过程

RRjUbya.gif

Choice : 可选的占位符

以如下snippet为例

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

输入 ch 按下tab键后,就会显示

i26vie2.png!web

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(实际操作不规范,仅仅为了精简)

其中 12 相同

代码片段
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global).*/$1/}"    // 输出: global
}

3 表示对匹配到项转化成大写,或小写,或首字母大写

代码片段
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global).*/${1:/upcase}/}"  // 输出: GLOBAL
}

4 表示匹配成功时,将 if 所述语句完全替换正则表达式匹配项。

代码片段
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:+if}/}",   //输出: if-js
    "body": "${TM_FILENAME_BASE/(global).*/${1:+if}/}"   //输出: if
}

5 表示匹配成功,且分组捕获成功时,将 if 所述语句完全替换正则表达式匹配项;

匹配成功,且分组捕获失败时,将 else 所述语句完全替换正则表达式匹配项;

匹配失败时, else 所述语句即为处理后的变量

代码片段
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:?if:else}/}",   //输出: if-js
    "body": "${TM_FILENAME_BASE/global/${1:?if:else}/}",   //输出: else-js
    "body": "${TM_FILENAME_BASE/(globald)/${1:?if:else}/}",   //输出: else
}

67 ,表示匹配成功,且分组捕获(正则表达式中 () 匹配到的项)成功时,不变;

匹配成功,且分组捕获失败时,将 else 所述语句完全替换正则表达式匹配项;

匹配失败时, else 所述语句即为处理后的变量

代码片段
"transform": {
    "prefix": "tr",
    "body": "${TM_FILENAME_BASE/(global)/${1:else}/}",   //输出: global-js
    "body": "${TM_FILENAME_BASE/global/${1:else}/}",   //输出: else-js
}

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

Eb6bYrr.png!web

然后可以添加如下

{
  "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`
  }
}

使用场景

  • 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}\"}",
      "}"
    ]
  }
  • 某些固定格式的数组对象

如某个数组长度较大的对象 {title:'input',dataIndex:'input'} ,该数组中对象的每个 titledataIndex 不尽相同,则可以写个临时snippet,直接生成该结构。

"templates": {
    "prefix": "tem",
    "body": [",{dataIndex:$1,", "title:$2}"],
    "description": ""
}
  • 某些固定内容的文件或者某些常用的 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"
    ]
  },
  • 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)"
    ]
  }

只需要输入组件名,就可以一次性生成必备的格式。

ArM7fim.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK