我们可以在 .vimrc
中对 Vim 进行配置,但有时我们还需要更 动态
或更 即时
的配置,Vim 脚本可以实现这样的需求,它能让我们完成复杂的任务。其实在 Vim 的配置文件中设置 Vim 选项,就已经在编写 Vim 脚本了,因为所有 Vim 命令和选项都是 Vim 脚本的有效输入。
脚本极简入门
下面通过一个简单示例,来说明 Vim 脚本。Vim 多种自定义配色方案,可以使用 Vim
的 colorscheme
命令选择某种配色方案,例如 colorscheme desert
。将该配置放入 Vim 的配置文件,即可在每次启动 Vim 时设置相应的配色方案。但是如果我们想根据时间早晚而选择不同的配色方案,该如何实现呢?
Vim 提供类似于 C 语言的 if/else
条件语句,而 Vim 的内置函数 strftime()
可以返回时间信息。因此将如下代码放入 .vimrc
文件之后,Vim 在每次启动时都会根据当前的时间自动设置配色方案。
1 | " choose color scheme |
上述代码中还使用了一个 Vim 脚本命令:echo
。echo
将消息显示在 Vim 命令行状态窗口里(或者以对话框的形式出现,取决于它在启动序列中的位置)。在使用 echo
显示字符串时,需要用双引号括起字符串,否则 echo 认为其为表达式或函数。
变量
可以使用变量对上述代码进行改进:并不需要在每个条件分支里都调用 strftime()
函数,相反,我们可以只调用一次并将结果保存到 Vim 变量里。Vim 有一套定义变量作用域的惯例,依赖于变量名称的前缀。前缀包括:
- b:在单一 Vim 缓冲区里被辨识的变量
- w:在单一 Vim 窗口里被辨识的变量
- t:在单一 Vim 分页里被辨识的变量
- g:全局变量,能在任何地方被辨识
- l:在函数内被辨识的变量(局部变量)
- s:在来源的 Vim 脚本里被辨识的变量
- a:函数的参数
- v:Vim 变量,由 Vim控制,也是全局变量
如果在定义 Vim 变量时没有指定作用域,则
- 当变量定义在函数外时,其默认为全局变量
- 定义在函数内时,其默认为局部变量
使用 let
命令指派变量的值:let var = "value"
。对上述代码进行修改,得到如下代码:
1 | " choose color scheme |
但该脚本执行会出现如下错误:
1 | E185: Cannot find color scheme 'colorScheme' |
即执行 colorscheme
命令时,没有找到 colorScheme
这种配色方案配色方案。也就说 Vim 将 colorScheme
当成配色方案,而不是将 colorScheme
的值当成配色方案。
execute命令
需要将上述语句改成 execute "colorscheme " colorScheme
。execute
的行为可以这样理解:
- 对于单纯的词(不加引号),
execute
命令将该词视为变量或表达式,并用变量的值代替该变量 - 用引号括起来的字符串,
execute
命令直接将它视为字面字符串,不进行变量替换
定义函数
上述代码只会在 Vim 启动时执行,如果想在 Vim 会话期间的任何时刻执行上述代码,可以创建包含该脚本代码的函数。Vim 使用如下形式定义函数,注意:Vim 的用户自定义函数名必须以大写字母开头。
1 | function MyFunction(arg1, arg2, arg3......) |
如果我们不调用该函数,其中的代码并不会执行。在 Vim 中使用 call 语句来调用函数,例如 call MyFunction()
。
使用全局变量转变 Vim 脚本
如果我们想让改变颜色模式的函数被自动调用,一种方法是将该调用放入 statusline 中。由于状态行会自动更新计算,因此其中的函数也会被自动调用,但是它的缺点也很明显:调用次数过于频繁,而且每次调用都会重新设置配色方案,即使设置前后的方案完全相同。
我们使用全局变量 colors_name
来优化该函数,比较 colors_name
变量与 colorScheme
,只当有两者不同时,才重新设置配色方案。colorscheme 命令会设置它自身的变量 colors_name
(可以通过 echo colors_name
查看该变量的值),以下是修改后的脚本文件:
1 | function SetColor() |
上述代码有一个需要注意的事项,必须要先定义 colors_name
变量,否则脚本文件会认为没有这个变量。我们可以执行colorscheme defalut
将颜色模式设置为默认值,这样 colors_name
变量自动定义好了(也可以通过 let 命令直接定义 g:colors_name
变量)。
数组
利用 Vim 的数组可以进一步改进上述脚本。Vim 中的数组为 方括号中由逗号分隔的一串值
,使用下标运算来访问数组中的某个元素,下标从 0 开始。
1 | function SetColor() |
通过脚本动态配置文件类型
通常编辑新文件时,Vim 根据文件的扩展名来判断并设置 filetype。但并非所有文件都会提供扩展名,事实上,Vim 具有良好的训练,只需要判读文件的内容,也可以辨识出文件类型,但是前提是文件已经提供了足够的内容。接下来我们将编写一个 Vim 脚本,来动态配置文件类型。
自动命令
自动命令包括任何合法的 Vim 命令。Vim 使用事件执行命令,Vim 事件包括:
- BufNewFile:在 Vim 开始编辑新文件时触发相关联的命令
- BufReadPre:在 Vim 移向新缓冲区前触发相关联的命令
- BufRead, BufReadPost:在编辑新文件时触发相关联的命令,但需要在读入文件后
- BufWrite, BufWirtePre:把缓冲区的内容写入文件前触发相关联的命令
- FileType:在设置 filetype 后触发相关联的命令
- vimResized:在改变 Vim 窗口尺寸后触发相关联的命令
- winEnter, winLeave:分别在进入、离开 Vim 窗口时触发相关联的命令
- CursorMoved, CursorMovedI:分别在光标进入正常模式、插入模式时触发的命令
Vim 事件还有很多,任何一个事件都可以定义在事件发生时执行的 autocmd
,其格式如下:
1 | autocmd [group] event pattern [nested] command |
- group:可选的命令组
- event:触发命令的事件
- pattern:匹配文件名的模式,用于找出应执行命令的文件
- nested:如果出现,表示这个自动命令能放在其它自动命令中
- command:当事件发生时执行的 Vim 命令、函数或用户自定义脚本
例如设计如下自动命令,用于检测文件类型:
1 | autocmd CursorMovedI * call CheckFileType() |
- 触发命令的事件为
CursorMovedI
,即当处于插入模式时触发命令 - 匹配文件名的模式为:
*
,代表辨识任何新打开的文件类型 - 执行的命令为
call CheckFileType()
,CheckFileType()
是我们自己编写的用于检测文件类型的函数
在 CheckFileType
函数中,需要检查 filetype
选项的值,在 Vim
脚本中,通过在选项名称前加入前缀(&)字符,即可获得选项值。以下是 CheckFileType
函数的简易定义:
1 | function CheckFileType() |
Vim 命令 filetype detect
是个保存在 $VIMRUNTIME
目录下的 Vim 脚本,它检查许多标准,试着为每个文件指派一个类型。
缓冲区变量
如果我们想在插入模式中将光标移动 20 次之后才开始文件类型的检测,此时需要定义一个缓冲区变量(因为编辑会话中的各个缓冲区应该相互独立)。修改后的函数定义如下:
1 | function CheckFileType() |
自动命令与组
vim提供自动命令组的表示法,其语法为:
1 | augroup group_name |
现在使用如下语句将前面的 autocmd
定义到自动命令组中:
1 | augroup newFileDetection |
为了高效地实现我们的函数,我们希望函数完成功能后,解除对它的引用。Vim 可以在事件处理函数中删除自动命令:
1 | autocmd! [group] [event] [pattern] |
- 感叹号接在
autocmd
后,表示与组、事件或模式相关联的命令将被删除
这样,我们通过如下语句就可删除与 newFileDetection
组相关联的命令:
1 | autocmd! newFileDetection |
如果想要确认自动命令的定义与删除,可在 Vim 中使用如下命令进行查询:autocomd newFileDetection
。
如果自动命令组中已经不包含自动命令,就可以使用 augroup! groupname
删除自动命令组。注意删除 自动命令组
不等于删除 已关联的自动命令
,如果在 自动命令组
中存在 自动命令
的情况下删除 自动命令组
,则 Vim 在每次引用到这些自动命令时都将出现错误状态。
其他注意事项
- Vim 提供不少扩展组件以及其它脚本语言的编程接口,其中包括 Perl,Python 和 Ruby 这三种最受欢迎的脚本语言
- 除了 Vim 命令,Vim 也提供大量内置函数,具体的这些内置函数的说明可以查看 Vim 内置的帮助文件
usr_41.txt
这里只是讨论了 Vim 脚本的皮毛,有更多的资源可以让我们更深入学习 Vim 脚本:
- 在 Vim 主页可以找到大量 Vim 脚本
- Vim 的运行时目录中也有大量的 Vim 脚本,所有后缀是
.vim
的文件都是脚本 - Vim 内置的说明文档也是无价之宝,可以参考如下主题:
- help autocmd
- help scripts
- help variables
- help functions
- help usr_41.txt