从零开始配置vim(27)——代码片段
我们之前介绍过缩写相关的内容,缩写是可以自动帮我们将缩写的单词展开成一段完整的话。但是代码本身是结构话的,仅仅使用缩写来配置是无法完成自动生成代码这个步骤的。好在我们大量的插件来进行配置。本篇我们将要来讨论如何使用相关插件来完成代码片段自动完成的功能
vsnip 插件
我们之前在配置自动补全的时候已经下载了 vim-vsnip这个插件。
vsnip是一个非常强大的插件,它支持我们使用与 VS Code 类似的方式来扩展定义我们自己的代码片段,同时它也内置了不同语言版本的代码片段。在之前的配置中我们还加了另外一个 friendly-snippets
。它提供了丰富的已定义好的可以直接使用的代码片段,加快了我们的编码效率。在前面介绍补全的时候我们已经安装并配置了它们。它们的效果如下图所示:
自定义代码片段
虽说这些插件预定义了大量的代码片段。但是他们都是通用型的代码片段,总有那么些时候无法满足我们的需求。一个明显的例子就是不同的公司有不同的代码和注释的风格。这个时候就需要我们自定义了。本篇也准备将重点放在如何自定义代码片段上。如果小伙伴们已经有了在 vscode
上自定义代码片段的经历,那么请跳过本篇以节省各位的时间。
入门
下面我们以 C
为例来说明如何自定义代码片段来满足我们的需求。其他语言只是填入的内容不同,在定义上并没有什么大的差别。这里我采用 C
语言一个原因是静态类型的语言更方便的演示其中的各项功能,另一个原因就是我对C/C++
比较熟悉
首先我们要找到代码片段的配置文件所在位置,这个位置保存在变量 g:vsnip_snippet_dir
中。我们可以通过 :echo g:vsnip_snippets_dir
找到它。
当然也有更简单的办法,我们可以执行 :VsnipOpen
来打开该语言对应的配置文件,如果该文件不存在,命令将创建一个以对应语言名称命名的 json
文件,例如这里它会创建一个 c.json
的文件
这里我们先来写一个最简单的代码片段——自动生成一个 Hello World
的 C
程序
"example": {
"prefix": ["hello"],
"body": [
"#include <stdio.h>",
"int main(int argc, char* argv[])",
"{",
"\tprintf(\"hello world\");",
"\treturn 0;",
"}"
],
"description": "create a hello world"
}
我们来试试效果,输入 hello
将自动生成一段代码
现在我们来看看这些字段都代表什么含义
example
:它代表该代码段的名称,也可以认为它是一个id,用于标识一个代码片段prefix
:用于触发该代码片段,这里也就是我们输入 hello将会触发补全,当选择对应项的时候会调用该代码片段进行补全body
:补全时自动生成的代码。它可以是一个字符串或者字符数组。虽然它本身也支持\r\n
来进行换行。但是我更倾向于使用字符数组的形式,每一行是数组中的一个字符串。description
:描述信息
使用占位符
如果我们仅仅只能生成像上述 hello world
那样给写死的代码的话,那么它也没什么太大的用处。有时候我们的代码需要生成一个模板,关键地方应该由我们自己进行填充。这个时候可以用到占位符,例如我们使用下面的来生成一个函数
"ifunc": {
"prefix": ["ifunc"],
"body": [
"int $1()",
"{",
"\treturn 0;",
"}"
],
"description": "create a function return int"
}
}
占位符内容可选
我们现在已经可以生成函数了,但是有一个问题摆在我们的面前。函数的返回类型多种多样,如果我们每一个类型都定义一个片段,例如 返回 int
的定义一个 ifunc
,返回 float
的定义一个 ffunc
。显得多此一举了,代码的重复度太高了。这个时候我们我们给占位符一些预选项以供我们选择。它的语法格式如下
${1|sel1, sel2, sel3,...|}
前面的1代表是第一个占位符。如果是后面的占位符需要提供选项,那么就可以依次类推
例如我们将上述生成函数的代码片段改为
"func": {
"prefix": ["func"],
"body": [
"${1|int,float,double,char,short,*|} $2()",
"{",
"\treturn $3;",
"}"
],
"description": "create a function"
}
}
占位符间跳转
生成函数的代码片段中有3个占位符,其中第一个是可以选的,第二个第三个需要我们手动填写。按照习惯,我们一般使用 <Tab>
键来进行跳转,但是这个时候我们发现它没有用。vsip
有自己的命令来跳转到占位符,因此为了保持使用习惯不变,我们需要定义快捷键
vim.cmd[[imap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']]
vim.cmd[[smap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']]
vim.cmd[[imap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']]
vim.cmd[[smap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']]
由于该插件是使用 vimscript
脚本写的,它还没有提供 lua
的接口,因此这里我也就使用 vimscript
的方式来定义快捷键。
使用变量
使用语法 $name
或者 ${name: default}
可以插入一个变量。如果未设置变量,则会插入其默认值或空字符串。当变量未知(未定义其名称)时,会将插入的变量名称转换为占位符。这里的变量一般是环境变量或者是 vim 自带的一些变量。我们直接拿来用但是在不同的环境下得到的结果是不一样的。
我们可以使用变量来丰富一些信息。例如我们使用下面的代码片段来生成注释
"doc-header": {
"prefix": ["doc-header"],
"body": [
"/**",
"* @file ${TM_FILENAME}",
"* @bref ${1}",
"* @details ${2}",
"* @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE}",
"* @commit history:",
"* \t v${3}: ${4}",
"*/${0}"
],
"description": "create a doc header"
}
这样我们就生成了一个 .cpp
文件开头的注释了。其中用到了 TM_FILENAME
来表示文件名,使用 CURRENT_YEAR
CURRENT_MONTH
CURRENT_DATE
来分别获取到年月日。它的效果如下:
这里的 ${0}
一般用来截断 Tab键的跳转,也就说遇到 ${0}
之后 <Tab>
键就不再起到跳转到下一个占位符位置这个作用了。
具体有哪些变量可以使用,可以参考 visual studio code
官方给出的文档
本篇主要谈论了该如何定义自己的代码片段。如果想要更完整的内容可以参考 Visual Studio Code
官方的文档。我们也可以从Visual Studio Code
相关代码片段中 Copy 部分来进行使用。