这篇我们来谈论vim一个相当重要的东西——自动命令。
从编程的角度来看,自动命令有点类似于事件响应,或者回调函数之类。当外部发生某些事件的时候,自动执行事先定义好的一组命令。
定义一个自动命令的格式如下
autocmd type pattern cmd
-
autocmd
: 自动命令以autocmd
关键字开始,它的作用类似与js
中定义函数时使用的function
关键字 -
type
: 触发该命令的事件类型 -
pattern
: 事件的过滤,根据不同的事件类型有不同的含义 -
cmd
: 将要执行的命令
说了这么多,我们以一个例子来说明:
当我们使用 vim
打开一个不存在的文件,如果直接退出,我们会发现它并没有被保存。因为打开一个不存在的文件时 vim
并没有真的在磁盘中创建这么一个文件,它仅仅新开了一个缓冲区,当执行写命令的时候才真正会创建文件。我们使用 q!
退出的话,它是不会往文件中写的
我们来试试修改这一行为,打开新文件的时候就创建,也就是打开一个新文件的时候就执行写操作。打开新文件的事件使用 BufNewFile
来表示。这里我们不限定文件格式,因此 pattern
这块就使用 *
来表示,执行的命令是 :w
来写入。
:autocmd BufNewFile * w
自动命令的事件类型
自动命令可以监听的事件类型主要有以下几种
- 开始编辑一个当前并不存在的文件,也就是我们上面介绍的一种情况。这种情况使用
BufNewFile
来表示 - 开始编辑一个已存在的文件。这种情况使用
BufReadPre
或者BufReadPost
来表示。他们分别表示文件内容被加载前和被加载后 - 改变一个缓冲区的
filetype
选项的时候,它与vim
的文件类型配合使用。这种情况使用FileType
来表示 - 文件被写回磁盘。这种情况用
BufWritePre
和BufWritePost
。他们的含义与上面BufReadPre
和BufReadPost
类似。 - 进入和退出插入模式,进入插入模式时使用
InsertEnter
,退出插入模式时使用InsertLevel
。 - Vim 完成启动所有初始化操作之后可以使用
VimEnter
来表示 - 输入可以使
vim
退出的命令,可以使用ExitPre
来表示。如果输入:quit
,在决定是否退出之前,可以使用QuitPre
。
这些只是 vim
庞大事件中的一小部分,想要了解其他的事件,可以使用 :help autocmd-events
同时绑定多个事件
我们可以同时绑定多个事件,事件之间使用逗号作为分割即可,例如我们想要在写入或者加载HTML文件之前对其进行自动排版,那么就可以使用如下代码
:autocmd BufReadPost,BufWritePre *.html normal gg=G
自动命令它是在事件触发时,执行命令。相当于我们在命令模式下手动输入命令。在之前介绍命令模式的时候说到过,要在命令模式下使用普通模式的操作可以使用 normal。gg表示将光标移动到第一行,=G表示从当前光标到最后一行执行自动排版的操作。
我们在 html
文件中输入
hello!
执行 :w之后我们发现它自动排版了。或者我们也可以使用 :edit打开一个排版混乱的HTML文件,会发现vim自动为它排版了。
或者我们再举一个例子,我们在之前设置了自动换行功能,因为在一般的编程语言中,一行代码写太长确实不是一个好习惯。但是像 markdown
或者 html
这种用来写文档的标记语言,难免会出现很长的文本,这个时候再设置自动换行就有点不合适了,我们可以使用下面的语句来针对这两种文档来消除
:autocmd BufNewFile, BufReadPost *.html setlocal nowrap
这里我们使用 BufNewFile和 BufReadPost,因为我们需要对新创建的和已存在的 html文件都启用该设置。
FileType 事件
这种类型的事件可以说是我们最常用的,通过这个事件配合 setlocal
可以很方便的针对不同的编程语言做不同的设置
例如我们将上述取消 HTML
自动换行的代码做一个改写,改写成使用 FileType
:autocmd FileType html setlocal nowrap
又或者我们可以根据不同的语言,定义一个快捷键快速添加注释
:autocmd FileType python nnoremap c I#
:autocmd FileType javascript nnoremap c I//
在输入完命令之后,我们新打开一个 js或者 python文件,就可以直接测试
我们也可以结合之前介绍过的本地缩写的定义,针对不同的文件类型定义不同的缩写,例如
:autocmd FileType c iabbrev main int main(int argc, char* argv[])
:autocmd FileType python iabbrev main if __name__ == "__main__":
针对不同语言快速填充main函数。
我们结合FileType
事件和缩写似乎可以针对不同语言定义出对应的代码片段,这个也就是有的编辑器提供的 code snippet
的功能,结合插件我们可以使用 vim
定义出更加强大的代码片段
自动命令组
已经讨论了自动命令的这么多东西了,各位小伙伴可能已经掌握了甚至已经开始迫不及待想往配置文件里面添加内容了。但是在 lua中该怎么使用自动命令呢?先别着急,自动命令相关内容还没讨论完,先容我卖个关子,我们在后面来讨论如何在 lua中添加自动命令吧。
我们暂时不讲如何在 lua
中添加自动命令,这里我们先尝试启用 init.vim
文件。我们在里面加入一行
autocmd BufWrite * sleep 200m
然后我们保存并使用 :source $MYVIMRC
启用
好像也没什么变化是不是。别着急,我们多次执行 :source $MYVIMRC
。然后再执行保存操作。是不是发现vim越来越慢了。这是怎么回事呢?
我们每次执行 :source $MYVIMRC
的时候,vim
不会丢弃原有的设置,只会重头再来读取并加载新的设置,有点像文件的追加。在多次加载配置文件的时候,vim
已经创建了多个自动命令了。随着加载次数的增多,vim
中保存了多个休眠的自动命令了。所以 vim
会变得越来越卡。
你可能会说,谁没事闲着一直加载配置文件玩啊,而且也没有人会写延时吧。但是想想这个场景是不是像我们在反复修改并测试配置的时候会做的操作。虽然我们不会做延时,但是加载配置或者加载插件也是要时间的,插件有的也会执行自动命令,如果数量大起来了,自然会影响vim的启动速度。
我们该如何处理这个问题呢?
vim中给出的解决办法是将自动命令放到一个用户命名的组中,组成一个自动命令组。
使用关键字 augroup
来创建一个自动命令组。例如我们可以创建下面的自动命令组
:augroup testgrp
: autocmd BufWrite * echom "hello1"
: autocmd BufWrite * echom "hello2"
:augroup END
直接在命令行输入这么些东西太麻烦了,我们还是在 init.vim
中输入。启用之后我们再来执行保存操作接着,执行 :message
命令来查看日志,发现有两条打印
我们再来修改一下配置文件或者在命令行输入添加下面一句
:augroup testgrp
: autocmd BufWrite * echom "hello3"
:augroup END
我们先退出vim,再打开,然后执行保存操作,再看看日志里面有几条输出呢?
答案可能会另各位百思不得其解,它会打印三条。并不是各位想象的那样,同名的组发生覆盖。这里它也会发生追加现象,将同一组的多个命令组合在一起。既然用不用组都会追加,那么我要它有何用,我还多写了这么多代码。别着急,我们慢慢往下看。
分组一个是为了划分模块,第二个原因就是我们可以使用 autocmd!
来清除同一组之前的命令。
例如我们将上述命令修改为
:augroup testgrp
: autocmd!
: autocmd BufWrite * echom "hello3"
:augroup END
这个时候再次测试,我们发现它只会打印一条语句了。
但是这里如果我们将它放在不同组的时候,情况就又发生变化了
:augroup testgrp
: autocmd BufWrite * echom "hello1"
: autocmd BufWrite * echom "hello2"
:augroup END
:augroup testgrp1
: autocmd!
: autocmd BufWrite * echom "hello3"
:augroup END
我们发现在保存之后,它还是会打印3条语句
autocmd!
只会清除同一组之前的所有命令,而不同组的命令它不会生效。这就给我们按模块划分命名提供了便利。
改进自动加载配置文件的操作
之前我们通过绑定快捷键
来自动重新加载配置,学习了自动命令之后我们可以进一步的偷懒了,利用自动命令,我们可以做到只要配置文件被保存了,就自动加载。
:augroup NVIMRC
: autocmd!
: autocmd BufWritePost init.vim source %
:augroup END
这里我们先使用 autocmd!清除之前的加载操作。后面的自动命令中,我们使用 BufWritePost在配置文件保存到磁盘之后再来执行加载操作,保证从磁盘中加载到的配置文件与我们修改过后的保持一致。
自动命名在我们实现 vim
自动化编程的一个重要工具。甚至 vim
自身也有很多功能依靠它来实现,比如文件类型检测。文件类型检测我们将在下一章进行讨论。