Visual Studio Code 中的代码片段

代码片段是模板,使得输入重复的代码模式(如循环或条件语句)更加容易。

在 Visual Studio Code 中,代码片段会出现在 IntelliSense 中(⌃Space (Windows, Linux Ctrl+Space))与其他建议混合在一起,以及在一个专门的代码片段选择器中(命令面板中的插入代码片段)。还支持 Tab 键自动补全:通过 "editor.tabCompletion": "on" 启用它,输入一个代码片段前缀(触发文本),然后按 Tab 键插入代码片段。

片段语法遵循TextMate片段语法,但不支持“插值shell代码”和使用\u;两者均不受支持。

ajax片段

内置代码片段

VS Code 内置了多种语言的代码片段,例如:JavaScript、TypeScript、Markdown 和 PHP。

内置JavaScript代码片段

您可以通过在命令面板中运行插入代码片段命令来查看某种语言的可用代码片段,以获取当前文件语言的代码片段列表。然而,请记住,此列表还包括您定义的用户代码片段,以及您安装的任何扩展提供的代码片段。

从市场安装代码片段

扩展市场中,许多VS Code Marketplace上的扩展包含代码片段。您可以在扩展视图(⇧⌘X (Windows, Linux Ctrl+Shift+X))中使用@category:"snippets"过滤器搜索包含代码片段的扩展。

搜索带有代码片段的扩展

如果您找到想要使用的扩展,请安装它,然后重新启动VS Code,新的代码片段将可用。

创建你自己的代码片段

您可以轻松定义自己的代码片段,无需任何扩展。要创建或编辑自己的代码片段,请在文件 > 首选项下选择配置用户代码片段,然后选择代码片段应出现的语言(通过语言标识符),或者选择新建全局代码片段文件选项,如果代码片段应适用于所有语言。VS Code 会为您管理和刷新底层的代码片段文件。

片段下拉菜单

代码片段文件是用JSON编写的,支持C风格的注释,并且可以定义无限数量的代码片段。代码片段支持大多数TextMate语法以实现动态行为,根据插入上下文智能格式化空格,并允许轻松的多行编辑。

下面是一个JavaScript的for循环代码片段示例:

// in file 'Code/User/snippets/javascript.json'
{
  "For Loop": {
    "prefix": ["for", "for-const"],
    "body": ["for (const ${2:element} of ${1:array}) {", "\t$0", "}"],
    "description": "A for loop."
  }
}

在上面的例子中:

  • "For Loop" 是代码片段的名称。如果没有提供 description,它将通过 IntelliSense 显示。
  • prefix 定义一个或多个触发词,用于在 IntelliSense 中显示代码片段。前缀会进行子字符串匹配,因此在这种情况下,"fc" 可以匹配 "for-const"。
  • body 是一行或多行内容,在插入时将作为多行连接。换行符和嵌入的制表符将根据插入代码段的上下文进行格式化。
  • description 是 IntelliSense 显示的代码片段的可选描述。

此外,上面示例的body有三个占位符(按遍历顺序列出):${1:array}${2:element}$0。您可以使用Tab快速跳转到下一个占位符,此时您可以编辑占位符或跳转到下一个占位符。冒号:后的字符串(如果有)是默认文本,例如${2:element}中的element。占位符的遍历顺序按数字升序排列,从1开始;0是一个可选的特殊情况,始终排在最后,并在指定位置退出片段模式。

文件模板片段

如果代码片段旨在填充或替换文件内容,您可以在代码片段的定义中添加isFileTemplate属性。当您在新文件或现有文件中运行Snippets: Populate File from Snippet命令时,文件模板代码片段将显示在下拉列表中。

代码片段范围

代码片段是有作用域的,因此只会建议相关的代码片段。代码片段可以通过以下任一方式进行作用域限定:

  1. 代码片段所针对的语言(可能是所有语言)
  2. 片段所属的项目(可能是所有项目)

语言片段范围

每个代码片段根据其定义的位置,可以限定为一种、几种或所有(“全局”)语言:

  1. 一个语言片段文件
  2. 一个全局代码片段文件

单语言用户定义的代码片段定义在特定语言的代码片段文件中(例如javascript.json),您可以通过语言标识符通过代码片段:配置用户代码片段访问。只有在编辑定义该代码片段的语言时,才能访问该代码片段。

多语言和全局用户定义的代码片段都定义在“全局”代码片段文件中(文件后缀为.code-snippets的JSON文件),也可以通过代码片段:配置用户代码片段访问。在全局代码片段文件中,代码片段定义可能有一个额外的scope属性,该属性接受一个或多个语言标识符,这使得代码片段仅对指定的语言可用。如果没有给出scope属性,则全局代码片段在所有语言中都可用。

大多数用户定义的代码片段都限定于单一语言,因此它们是在特定语言的代码片段文件中定义的。

项目片段范围

你也可以有一个全局的代码片段文件(带有.code-snippets文件后缀的JSON),该文件适用于你的项目。项目文件夹的代码片段是通过Snippets: Configure User Snippets下拉菜单中的New Snippets file for ''...选项创建的,并位于项目根目录下的.vscode文件夹中。项目代码片段文件对于与在该项目中工作的所有用户共享代码片段非常有用。项目文件夹的代码片段类似于全局代码片段,并且可以通过scope属性限定为特定语言。

片段语法

代码片段的body可以使用特殊结构来控制光标和插入的文本。以下是支持的功能及其语法:

制表位

使用制表位,您可以使编辑器光标在代码片段内移动。使用 $1, $2 来指定光标位置。数字表示制表位被访问的顺序,而 $0 表示最终的光标位置。相同的制表位的多次出现是链接的,并且会同步更新。

占位符

占位符是带有值的制表位,例如 ${1:foo}。占位符文本将被插入并选中,以便可以轻松更改。占位符可以嵌套,例如 ${1:另一个 ${2:占位符}}

选择

占位符可以有选择作为值。语法是用管道字符括起来的值逗号分隔枚举,例如 ${1|one,two,three|}。当插入代码片段并选择占位符时,选择将提示用户选择其中一个值。

变量

使用 $name${name:default},您可以插入变量的值。当变量未设置时,将插入其默认值或空字符串。当变量未知(即其名称未定义)时,将插入变量的名称并将其转换为占位符。

以下变量可以使用:

  • TM_SELECTED_TEXT 当前选中的文本或空字符串
  • TM_CURRENT_LINE 当前行的内容
  • TM_CURRENT_WORD 光标下单词的内容或空字符串
  • TM_LINE_INDEX 基于零索引的行号
  • TM_LINE_NUMBER 基于一的索引行号
  • TM_FILENAME 当前文档的文件名
  • TM_FILENAME_BASE 当前文档的文件名,不包括扩展名
  • TM_DIRECTORY 当前文档的目录
  • TM_FILEPATH 当前文档的完整文件路径
  • RELATIVE_FILEPATH 当前文档的相对(相对于打开的工作区或文件夹)文件路径
  • CLIPBOARD 剪贴板的内容
  • WORKSPACE_NAME 打开的工作区或文件夹的名称
  • WORKSPACE_FOLDER 打开的workspace或folder的路径
  • CURSOR_INDEX 基于零索引的光标编号
  • CURSOR_NUMBER 基于一的索引的游标编号

插入当前日期和时间:

  • CURRENT_YEAR 当前年份
  • CURRENT_YEAR_SHORT 当前年份的最后两位数字
  • CURRENT_MONTH 月份为两位数(例如 '02')
  • CURRENT_MONTH_NAME 月份的全名(例如 'July')
  • CURRENT_MONTH_NAME_SHORT 月份的缩写名称(例如 'Jul')
  • CURRENT_DATE 月份中的日期,以两位数表示(例如 '08')
  • CURRENT_DAY_NAME 星期几的名称(例如 '星期一')
  • CURRENT_DAY_NAME_SHORT 星期几的简称(例如 'Mon')
  • CURRENT_HOUR 当前小时,采用24小时制格式
  • CURRENT_MINUTE 当前分钟,显示为两位数
  • CURRENT_SECOND 当前秒数,显示为两位数
  • CURRENT_SECONDS_UNIX 自Unix纪元以来的秒数
  • CURRENT_TIMEZONE_OFFSET 当前的UTC时区偏移量,格式为+HH:MM-HH:MM(例如-07:00)。

用于插入随机值:

  • RANDOM 6个随机的十进制数字
  • RANDOM_HEX 6个随机的Base-16数字
  • UUID 一个版本4的UUID

用于插入行或块注释,遵循当前语言:

  • BLOCK_COMMENT_START 示例输出:在 PHP 中为 /* 或在 HTML 中为 <!--</code>
  • BLOCK_COMMENT_END 示例输出:在 PHP 中 */ 或在 HTML 中 -->
  • LINE_COMMENT 示例输出:在 PHP 中 //

下面的代码片段在 JavaScript 文件中插入 /* Hello World */,在 HTML 文件中插入

{
  "hello": {
    "scope": "javascript,html",
    "prefix": "hello",
    "body": "$BLOCK_COMMENT_START Hello World $BLOCK_COMMENT_END"
  }
}

变量转换

转换允许你在插入变量之前修改其值。转换的定义由三部分组成:

  1. 一个正则表达式,它与变量的值匹配,或者在无法解析变量时与空字符串匹配。
  2. 一个“格式字符串”,允许引用正则表达式中的匹配组。格式字符串允许条件插入和简单修改。
  3. 传递给正则表达式的选项。

以下示例插入当前文件的名称,但不包括其扩展名,因此从 foo.txt 生成 foo

${TM_FILENAME/(.*)\\..+$/$1/}
  |           |         |  |
  |           |         |  |-> no options
  |           |         |
  |           |         |-> references the contents of the first
  |           |             capture group
  |           |
  |           |-> regex to capture everything before
  |               the final `.suffix`
  |
  |-> resolves to the filename

占位符-转换

类似于变量转换,占位符的转换允许在移动到下一个制表位时更改插入的占位符文本。 插入的文本与正则表达式匹配,并且根据选项,匹配项被替换为指定的替换格式文本。 每个占位符的出现都可以使用第一个占位符的值独立定义其自己的转换。 占位符转换的格式与变量转换的格式相同。

转换示例

示例显示在双引号内,因为它们会出现在代码片段主体中,以说明需要对某些字符进行双重转义的必要性。示例转换及结果输出适用于文件名 example-123.456-TEST.js

Example Output Explanation
"${TM_FILENAME/[\\.]/_/}" example-123_456-TEST.js Replace the first . with _
"${TM_FILENAME/[\\.-]/_/g}" example_123_456_TEST_js Replace each . or - with _
"${TM_FILENAME/(.*)/${1:/upcase}/}" EXAMPLE-123.456-TEST.JS Change to all uppercase
"${TM_FILENAME/[^0-9a-z]//gi}" example123456TESTjs Remove non-alphanumeric characters

语法

以下是代码片段的EBNF(扩展巴科斯-瑙尔形式)。使用\(反斜杠),你可以转义$}\。在选择元素中,反斜杠还可以转义逗号和竖线字符。只有需要转义的字符才能被转义,因此在这些结构中不应转义$,并且在选择结构内部也不应转义$}

any         ::= tabstop | placeholder | choice | variable | text
tabstop     ::= '$' int
                | '${' int '}'
                | '${' int  transform '}'
placeholder ::= '${' int ':' any '}'
choice      ::= '${' int '|' text (',' text)* '|}'
variable    ::= '$' var | '${' var '}'
                | '${' var ':' any '}'
                | '${' var transform '}'
transform   ::= '/' regex '/' (format | text)+ '/' options
format      ::= '$' int | '${' int '}'
                | '${' int ':' '/upcase' | '/downcase' | '/capitalize' | '/camelcase' | '/pascalcase' '}'
                | '${' int ':+' if '}'
                | '${' int ':?' if ':' else '}'
                | '${' int ':-' else '}' | '${' int ':' else '}'
regex       ::= JavaScript Regular Expression value (ctor-string)
options     ::= JavaScript Regular Expression option (ctor-options)
var         ::= [_a-zA-Z] [_a-zA-Z0-9]*
int         ::= [0-9]+
text        ::= .*
if          ::= text
else        ::= text

使用TextMate片段

你也可以在VS Code中使用现有的TextMate代码片段(.tmSnippets)。更多信息请参阅我们扩展API部分中的使用TextMate代码片段主题。

为代码片段分配快捷键

您可以创建自定义的键绑定来插入特定的代码片段。打开keybindings.json首选项:打开键盘快捷键文件),该文件定义了所有的键绑定,并添加一个键绑定,传递"snippet"作为额外参数:

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

快捷键将调用插入代码片段命令,但不会提示您选择代码片段,而是直接插入提供的代码片段。您可以像往常一样定义自定义快捷键,包括键盘快捷键、命令ID以及可选的when子句上下文,用于确定何时启用键盘快捷键。

此外,您可以使用langIdname参数来引用现有的代码片段,而不是使用snippet参数值来内联定义您的代码片段。langId参数选择由name表示的代码片段插入的语言,例如下面的示例选择了可用于csharp文件的myFavSnippet

{
  "key": "cmd+k 1",
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "langId": "csharp",
    "name": "myFavSnippet"
  }
}

下一步

  • Command Line - VS Code 拥有丰富的命令行界面,可以打开或比较文件以及安装扩展。
  • Extension API - 了解扩展VS Code的其他方法。
  • Snippet Guide - 您可以打包代码片段以在VS Code中使用。

常见问题

如果我想使用来自.tmSnippet文件的现有TextMate片段怎么办?

您可以轻松打包TextMate片段文件以在VS Code中使用。请参阅我们扩展API文档中的使用TextMate片段

如何在粘贴的脚本中放置一个变量?

要在粘贴的脚本中使用变量,你需要转义$variable名称中的'$',以便它不会被片段扩展阶段解析。

"VariableSnippet":{
    "prefix": "_Var",
    "body": "\\$MyVar = 2",
    "description": "A basic snippet that places a variable into script with the $ prefix"
  }

这将导致粘贴的代码片段如下:

$MyVar = 2

我可以从IntelliSense中移除代码片段吗?

是的,您可以通过在插入代码片段命令下拉列表中选择代码片段项右侧的从IntelliSense隐藏按钮来隐藏特定的代码片段,使其不在IntelliSense(完成列表)中显示。

在插入代码片段下拉菜单中的隐藏IntelliSense按钮

您仍然可以使用插入代码片段命令选择代码片段,但隐藏的代码片段不会在IntelliSense中显示。