vscode使用技巧

配置

设置隐藏文件

用于在资源管理器隐藏固定后缀的文件,配置方式:

  1. 首选项->设置
  2. 搜索files.exclude,选择工作区设置
  3. 添加模式:**/*.pyc

Creating your own snippets

既然你点开了这个页面,那就说明要么你不知道 VSCode 上已有拓展「C/C++ Snippets」,要么你对这个拓展不甚满意。对于后者,本文将为你介绍如何在 VSCode 上设置 snippets,并为你提供一套可以直接用的 C 语言 snippets。

1. snippet 简介

snippet[ˈsnɪpɪt],或者说「code snippet」,也即代码段,指的是能够帮助输入重复代码模式,比如循环或条件语句,的模板。通过 snippet ,我们仅仅输入一小段字符串,就可以在代码段引擎的帮助下,生成预定义的模板代码,接着我们还可以通过在预定义的光标位置之间跳转,来快速补全模板。

当然,看图更易懂。下图将 aja 补全为 JQuery 的 ajax() 方法,并通过光标的跳转,快速补全了待填键值对: image

2. snippet 配置流程

进入 snippet 设置文件,这里提供了三种方法: 通过快捷键「Ctrl + Shift + P」打开命令窗口(All Command Window),输入「snippet」,点选「首选项:配置用户代码段片段」; 点击界面最左侧竖栏(也即活动栏)最下方的齿轮按钮,在弹出来的菜单中点选「用户代码片段」; 按下「Alt」键切换菜单栏,通过文件 > 首选项 > 用户代码片段; 填写 snippets

3. snippet 详细介绍

3.1 引子

设置文件头部的一个块注释给出了设置 snippet 的格式,了解过「json」就不会对此感到奇怪。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Place your snippets for C here. Each snippet is defined under a snippet name and has a prefix, body and 
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
"Print to console": {
"prefix": "log",,
"body": [
"console.log('$1');",
"$2"
],
"description": "Log output to console"
}

上例定义了一个名为「Print to console」的 snippet,其功能为:在输入 log 并确认后,可将原文本替换为console.log(‘’);

3.2 语法结构

然而引子中只是最简单的功能,而 VSCode 的代码段引擎所能做的远不止这些。本文将以官方教程1为本,对其进行详实地阐述。

snippet 由三部分组成:

  1. prefix:前缀,定义了 snippets 从 IntelliSense 中呼出的关键字;
  2. body: 主体,即模板的主体内容,其中每个字符串表示一行;
  3. description:说明,会在 IntelliSense 候选栏中出现。未定义的情况下直接显示对象名,上例中将会显示 Print to console。 Snippet 的三个部分目前只有主体部分支持丰富的特性。接下来整篇文章都是拿来介绍主体部分的。主体部分的介绍将分为两部分:较为直接的基础用法,及结合正则表达式的高级用法。

3.3 Body - 基本用法

主体部分可以使用特殊语法结构,来控制光标和要插入的文本,其支持的基本结构如下:

  1. Tabstops:制表符 用「Tabstops」可以让编辑器的指针在 snippet 内跳转。使用 $1,$2 等指定光标位置。这些数字指定了光标跳转的顺序。特别地,$0表示最终光标位置。相同序号的「Tabstops」被链接在一起,将会同步更新,比如下列用于生成头文件封装的 snippet 被替换到编辑器上时,光标就将同时出现在所有$1位置。

    1
    2
    3
    "#ifndef $1"
    "#define $1"
    "#end // $1"
  2. Placeholders:占位符 「Placeholder」是带有默认值的「Tabstops」,如${1:foo}。「placeholder」文本将被插入「Tabstops」位置,并在跳转时被全选,以方便修改。占位符还可以嵌套,例如${1:another ${2:placeholder}}。 比如,结构体的 snippet 主体可以这样写:

    1
    struct ${1:name_t} {\n\t$2\n};

作为「Placeholder」的name_t一方面可以提供默认的结构名称,另一方面可以作为输入的提示。

  1. Choice:可选项 「Choice」是提供可选值的「Placeholder」。其语法为一系列用逗号隔开,并最终被两个竖线圈起来的枚举值,比如 ${1|one,two,three|}。当光标跳转到该位置的时候,用户将会被提供多个值(one 或 two 或 three)以供选择。

  2. Variables:变量 使用$name或${name:default}可以插入变量的值。当变量未赋值时(如),将插入其缺省值或空字符串。 当varibale未知(即,其名称未定义)时,将插入变量的名称,并将其转换为「Placeholder」。可以使用的「Variable」如下:

  • TM_SELECTED_TEXT:当前选定的文本或空字符串;

  • 注:选定后通过在命令窗口点选「插入代码片段」插入。

  • TM_CURRENT_LINE:当前行的内容;

  • TM_CURRENT_WORD:光标所处单词或空字符串

  • 注:所谓光标一般为文本输入处那条闪来闪去的竖线,该项可定制。单词使用 VSCode 选词(Word Wrap)器选择。你最好只用它选择英文单词,因为这个选择器明显没有针对宽字符优化过,它甚至无法识别宽字符的标点符号。

  • 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: 当前秒数。 注:这些都是变量名,不是宏,在实际使用的时要加上 $ 符。

3.4 Body - 高级语法

3.4.1 变量转换

变量转换可将变量的值格式化处理后插入预定的位置。

3.4.1.1 语法结构

我们可以通过 ${var_name/regular_expression/format_string/options} 插入格式化后的代码段。显然,「variable transformations」由 4 部分构成:

  1. var_name:变量名;
  2. regular_expression:正则表达式;
  3. format_string:格式串;
  4. options:正则表达式匹配选项。 其中正则表达式的写法和匹配选项部分不在本篇博文的讲解范围之内,具体内容请分别参考 javascript 有关 RegExp(pattern [, flags]) 构造函数中的 pattern 及 flags 参数项的说明2。

本文只对 format_string 部分进行详细介绍。

3.4.1.2 format_string 部分

根据其 EBNF 范式,我们可以知道 format_string 其实是 format 或 text 的线性组合:

  1. text:也即没有任何作用的普通文本,你甚至可以使用汉字;
  2. format:格式串,分为 7 种:
  • $sn:表示插入匹配项;
  • ${sn}:同 $sn;
  • ${sn:/upcase} 或 ${sn:/downcase} 或 ${sn:/capitalize}:表示将匹配项变更为「所有字母均大写/所有字母均小写/首字母大写其余小写」后,插入;
  • ${sn:+if}:表示当匹配成功时,并且捕捉括号捕捉特定序号的捕捉项成功时,在捕捉项位置插入「if」所述语句;
  • ${sn:?if:else}:表示当匹配成功,并且捕捉括号捕捉特定序号的捕捉项成功时,在捕捉项位置插入「if」所述语句;否则当匹配成功,但当捕捉括号捕捉特定序号的捕捉项失败时,在捕捉项位置插入「else」所述语句;
  • ${sn:-else}:表示当匹配成功,但当捕捉括号捕捉特定序号的捕捉项失败时,在捕捉项位置插入「else」所述语句;
  • ${sn:else}:同 ${sn:-else}。 format 的后三条理解起来可能比较困难。这里我们以倒数第三条为例进行说明。假设我们有一个「make.c」文件,我们有这么一条 snippet: “body”: “${TM_FILENAME/make.c(pp|++)?/${1:?c++:clang}/}”。整个模式串匹配成功,但是捕捉括号捕捉后缀名中的 pp 或 ++ 失败,因此判断条件在捕捉括号的位置插入捕捉失败时应插入的字符串,也即「clang」。

    注: 其中 sn 表示捕捉项的序号 其中 if 表示捕捉项捕捉成功时替换的文本 其中 else 表示捕捉项捕失败时替换的文本

3.4.1.3 案例分析

下面笔者再介绍一个简单的例子,帮助大家理解「variable transformations」。

假设有一个名为「make.c」的文件中,并且我们已经定义如下 snippet。

1
2
3
4
"#ifndef HEADER … #define … #endif":{
"prefix": "defheader",
"body": "#ifndef ${1:${TM_FILENAME/(.*)\\.C$/${1:/upcase}_H/i}} \n#define $1 \n${2:header content}\n#endif\t// $1"
}

其中最复杂的模式为:${1:${TM_FILENAME/(.*)\.C$/${1:/upcase}_H/i}},我们将之拆解为如下五部分:

  1. ${1:…}:嵌套的 placeholder;
  2. ${TM_FILENAM/…/…/.}:「variable transformations」中的「var_name」,表示带后缀的文件名;
  3. ${…/(.*)\.C$/…/.}:「variable transformations」中的「regular_expression」,表示匹配任意以「.C」为后缀的字符串;
  4. ${…/…/${1:/upcase}_H/.}}:「variable transformations」中的「options」,表示将第一个捕捉项替换为大写的,并添加「_H」的后缀;
  5. ${…/…/…/i}:「variable transformations」中的「options」,表示无视大小写。
3.4.2 占位符转换
3.4.2.1 语法结构

我们可以通过 ${int/regular_expression/format_string/options} 插入格式化后的代码段。显然,与变量转换,「placeholder transformations」也由 4 部分构成:

  1. int:占位符相应光标序号;
  2. regular_expression:正则表达式;
  3. format_string:格式串;
  4. options:正则表达式匹配选项。 上述全部内容我们都在前文介绍过了,因此此处不做赘述。我们唯一需要关注的是转换触发的时机:占位符转换将在进行占位符跳转(假设 1→2)的时候自动适用到当前占位符(1)。
3.4.2.2 案例分析

假设我们已经这样的 Snippet:

1
2
3
4
"HelloWorld": {
"prefix": "say_hello",
"body": "${1} ${2} -> ${1/Hello/Hallo/} ${2/World/Welt/}"
}

那么我们在两个制表位同时键入 Hello 并跳转的时候,第一个制表位依然保持 Hello 不变,而第二个制表位(占位符)被替换为 Hallo。键入 Welt 亦然。

3.5 语法定义

官网给出了 snippet 的 EBNF 范式的正则文法,注意,作普通字符使用时,$ , } 和 \ 可使用 \(反斜杠)转义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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' '}'
| '${' 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 ::= .*

4. 一些建议

默认情况下 snippet 在 IntelliSense 中的显示优先级并不高,而且在 IntelliSense 中选择相应 snippet 需要按「enter」键,这对于手指短的人来说并不是什么很好的体验。

所幸,VSCode 意识到了这一点,并为我们提供了改进的方式。我们可以在 VSCode 的用户设置(「Ctrl+P」在输入框中写「user settings」后点选)中,检索代码段,然后根据提示修改代码段的相关设置。

我们可以设置在 IntelliSense 中优先显示代码段,并可以通过「TAB」补全。

修改后设置文件中会多出这两行:

1
2
"editor.snippetSuggestions": "top",
"editor.tabCompletion": true

参考:https://code.visualstudio.com/docs/editor/userdefinedsnippets https://blog.csdn.net/maokelong95/article/details/54379046?utm_source=blogxgwz0

坚持原创技术分享,您的支持将鼓励我继续创作!