TOC
背景
日常开发中很多代码是模式高度一致的或重复的,对于这些代码片段,往往会到 VSCode 商店中寻找各种 Snippet 插件。然而 VSCode 和插件提供的 Snippet 很可能不符合日常使用和记忆的习惯,或者干脆并没有插件能提供想要的代码片段。
对于这些重复的内容,从 import 到生命周期方法到整个组件,都可以通过 VSCode 的自定义 Snippet 功能实现快速输入。目前运营后台的表格和弹窗都是通过 Snippet 快速生成的。
本文介绍了 VSCode 中自定义 Snippet 的基本姿势,期望能让更多人能够通过这种方式提升编码效率。
配置文件
VSCode 的 Snippet 存储在一系列 json 文件中,一般通过「CMD + Shift + P」快捷键打开 Command Palette 快速搜索配置项,从而进行代码片段的管理。
分类
选择「首选项: 配置用户代码片段」后,将显示以下创建和管理代码片段的入口
可以看到,配置文件主要分为几种:
全局代码代码段
:本地所有语言有效,文件名格式为<name>.code-snippet
。可通过 scope 配置指定生效文件类型。项目级别代码段
:本项目所有语言有效,存储在项目的.vscode 文件夹中,VSCode 1.28.2 版本后支持。文件名格式为<name>.code-snippet
。可通过 scope 配置指定生效文件类型。特定语言代码段
:本地特定语言有效,文件名格式为<languageId>.json
。
数据结构
代码段的配置包含几个部分:
scope
:生效范围。由一系列语言标识符组成,不提供或留空表示对所有文件生效。用于在全局和项目代码片段配置中限制生效范围,减少使用时的干扰。prefix
:前缀。输入前缀后编辑器将进行提示,选择对应项或者按 tab 将插入代码段,可提供数组。body
:代码段的具体定义。可以是字符串或者一个数组。数组中的每一项代表代码的每一行。description
:编辑器提示时显示的描述。
一个最基本的例子如下:
// globalOrProjectLevel.code-snippet
{
"Print to console": {
"scope": "javascript,typescript",
"prefix": "log",
"body": ["console.log('$1');", "$2"],
"description": "Log output to console"
}
}
其中 Print to console
仅用于标识配置。
语法
Tab Stops
在 Snippet 中,可以通过 ${number}
形式定义光标位置,如 $1 代表初始光标位置,$2
代表在 $1
处输入完成后按 tab 光标将会到达的位置。同样序号的 TabStop 还能存在多个,他们将同时产生多个光标。
特殊的,$0
代表光标最终的位置。
Placeholders
Placeholder 是带默认值的 Tab Stop,通过类似 ${1:text}
的形式定义。Placeholder 的文本将显示在 TabStop 处,并默认选中,可以快捷的编辑或者干脆按 tab 使用默认值。
{
"For_Loop": {
"prefix": "for",
"scope": "javascript,typescript",
"body": ["for (const ${2:element} of ${1:array}) {", "\t$0", "}"],
"description": "For Loop"
}
}
Choice
有时候我们想提供几个选项,而不是一个默认值,此时可以使用 Choice。Choice 的格式为 ${1|one,two,three|}
,在光标处于该位置时,编辑器将提供对应的选项供使用者选择。
Comment
Snippet 中还支持根据文件类型插入注释符号。可以使用 BLOCK_COMMENT_START
和 BLOCK_COMMENT_END
插入多行注释或用 LINE_COMMENT 加入单行注释。以下是官方给的例子,在 js 文件中将插入 /* Hello World */
而在 HTML 文件中将插入 <!-- Hello World -->
。
{
"hello": {
"scope": "javascript,html",
"prefix": "hello",
"body": "$BLOCK_COMMENT_START Hello World $BLOCK_COMMENT_END"
}
}
Variables
Snippet 中支持通过 $varName
或者 ${varName:defaultText}
的形式使用内置的一些变量,包括当前选择的内容、当前文件路径、时间、剪切板内容等。
可用的变量列表可参考官方文档中的相应章节。
Variables Transforms
内置变量的格式很可能不是我们想要的,或者我们只需要提取出其中一部分,此时可以使用对变量进行一定的转换。 格式分为变量名、正则表达式、替换字符串、正则选项几部分:
${TM_FILENAME/(.*)\\..+$/$1/}
| | | |
| | | |-> 正则的选项,此处没有
| | |
| | |-> 指代匹配中的第一个group
| |
| |
| |-> 匹配除了后缀之前的内容
|
|
|-> 内置变量,将解析为当前文件名
上面官方文档中的一个例子,将 foo.txt
变为 foo
插入到代码中。
此外我常用的转换还有:
//显示当前文件所在文件夹名,适用于在ComponentName/index.ts内插入ComponentName
${TM_DIRECTORY/.*\\/(.+)$/${1}/}
// 文件名转换,用于将文件名从kebab-case转化为PascalCase
// declare-file.d.ts => DeclareFile
${TM_FILENAME_BASE/(-|^)(\\w+)(\\..+)?/${2:/capitalize}/g}
Placeholder Transforms
Placeholder Transforms 和变量的转换一致,不过读取的不是内置变量而是你的输入。 此外,标号相同的 TabStop 都可以有不同转换方式。如下面的 snippet 中,在光标处于 $1 处时输入 foo 将在两行分别打出 Foo 和 foo。
{
"test": {
"prefix": "test",
"description": "测试Placeholder Transforms",
"body": ["${1/^(.+)$/${1:/capitalize}/}", "${1/^(.+)$/$1/}"]
}
}
进阶
总结
优点
- 大幅减少重复劳动
- 实现便捷,共享方便(项目级别的 Snippet 可以直接加入到 git 仓库中分享给其他开发者)
缺点
- 换行很多字符需要转义,难以手动处理。(不过可以使用我之前写的简单处理脚本来进行换行和其他字符的转义)
- JSON 格式后续维护不是很方便。