前言:vim依靠海量的插件库可以打造成一款神器的编辑器,但是网络上的教程都是几乎一模一样的,教我们怎么安装插件管理器,然后怎么安装插件,千篇一律,很少涉及到更本质的东西,本文会从几个重要的目录入手,把Vim的插件管理与加载详细的说明。
在使用Vundle或者是vim-plug插件管理工具的时候,当我们安装完之后,我们通过用户目录之下的.vimrc配置文件下载相应的插件,然后再用户目录之下的.vim文件夹之下就有了相应的插件所对应的文件夹,每一个文件夹下面或者是子文件夹之下都存在一个 xxxx.vim的文件,它是什么呢?
另一个问题就是,Vim仅从〜/ .vimrc文件中读取其所有设置和脚本吗?
先来回答两个问题:
(1)xxxx.vim 文件的本质就是脚本,因此在启动vim的时候会加载这些脚本文件,正式这些脚本的存在,才让我们可以自定义vim;
(2)〜/ .vimrc文件只是我们习惯性地用户配置方式,其实vim的配置还来自于很多其他的地方。
前面说了,Vim的结构非常整洁,.vimrc只是多个配置文件中的其中一个而已。其实,你可以让Vim告诉你究竟加载了哪些脚本。试试看:启动终端,直接打开vim,或者是编辑计算机上的任意一个文件,如vim test.py。加载后,运行如下命令:
2.1 查看vim加载时加载了那些插件(执行了哪些脚本)
:scriptnames
得到如下结果:
1: /etc/vimrc
2: /usr/share/vim/vim74/syntax/syntax.vim
3: /usr/share/vim/vim74/syntax/synload.vim
4: /usr/share/vim/vim74/syntax/syncolor.vim
5: /usr/share/vim/vim74/filetype.vim
6: /usr/share/vim/vim74/ftplugin.vim
7: ~/.vimrc
8: ~/.vim/autoload/plug.vim
9: /usr/share/vim/vim74/ftoff.vim
10: /usr/share/vim/vim74/indent.vim
11: ~/.vim/plugged/nerdtree/plugin/NERD_tree.vim
12: ~/.vim/plugged/nerdtree/autoload/nerdtree.vim
13: ~/.vim/plugged/nerdtree/lib/nerdtree/path.vim
14: ~/.vim/plugged/nerdtree/lib/nerdtree/menu_controller.vim
15: ~/.vim/plugged/nerdtree/lib/nerdtree/menu_item.vim
16: ~/.vim/plugged/nerdtree/lib/nerdtree/key_map.vim
17: ~/.vim/plugged/nerdtree/lib/nerdtree/bookmark.vim
18: ~/.vim/plugged/nerdtree/lib/nerdtree/tree_file_node.vim
19: ~/.vim/plugged/nerdtree/lib/nerdtree/tree_dir_node.vim
20: ~/.vim/plugged/nerdtree/lib/nerdtree/opener.vim
21: ~/.vim/plugged/nerdtree/lib/nerdtree/creator.vim
22: ~/.vim/plugged/nerdtree/lib/nerdtree/flag_set.vim
23: ~/.vim/plugged/nerdtree/lib/nerdtree/nerdtree.vim
24: ~/.vim/plugged/nerdtree/lib/nerdtree/ui.vim
25: ~/.vim/plugged/nerdtree/lib/nerdtree/event.vim
26: ~/.vim/plugged/nerdtree/lib/nerdtree/notifier.vim
27: ~/.vim/plugged/nerdtree/autoload/nerdtree/ui_glue.vim
28: ~/.vim/plugged/nerdtree/nerdtree_plugin/exec_menuitem.vim
29: ~/.vim/plugged/nerdtree/nerdtree_plugin/fs_menu.vim
30: ~/.vim/plugged/nerdtree/nerdtree_plugin/vcs.vim
31: ~/.vim/plugged/python-mode/plugin/pymode.vim
32: ~/.vim/plugged/python-mode/autoload/pymode.vim
33: /usr/share/vim/vim74/plugin/getscriptPlugin.vim
34: /usr/share/vim/vim74/plugin/gzip.vim
35: /usr/share/vim/vim74/plugin/matchparen.vim
36: /usr/share/vim/vim74/plugin/netrwPlugin.vim
37: /usr/share/vim/vim74/plugin/rrhelper.vim
38: /usr/share/vim/vim74/plugin/spellfile.vim
39: /usr/share/vim/vim74/plugin/tarPlugin.vim
40: /usr/share/vim/vim74/plugin/tohtml.vim
41: /usr/share/vim/vim74/plugin/vimballPlugin.vim
42: /usr/share/vim/vim74/plugin/zipPlugin.vim
清单比你预期的要长吗?我们发现加载了很多来自不同地方(即不同文件夹之下)的脚本xxx.vim文件(因为我这里安装了一些插件,有的可能没有这么多,这个数目是不一定的)。而且我们发现,不仅仅加载了我们自己安装的那些来自于.vim文件夹下面的插件,还有很多其他地方的插件,这是哪里来的呢?后面会说到。
这其实也就是vim在启动的时候会加载脚本,脚本越多就越耗时间。如果你安装了大量插件的话,那么编辑器需要做大量工作。你可以通过以下命令检查是什么导致编辑器的速度变慢,然后再看看它创建的start.log:
2.2 查看每一个插件的耗时
首先新建一个文件夹,然后在文件夹之下新建一个test.py文件,我的如下:
/home/zoe/Documents/python_test/test1.py
在该文件夹之下通过vim打开test1.py,使用下面的命令
vim --startuptime start.log test1.py(这里替换成自己需要打开的文件名)
这个时候就会打开我们的test1.py,与此同时,会在python_test文件夹之下出现了一个start.log的日志文件,打开查看如下:
times in msec
clock self+sourced self: sourced script
clock elapsed: other lines
000.011 000.011: --- VIM STARTING ---
000.146 000.135: Allocated generic buffers
000.224 000.078: locale set
000.229 000.005: window checked
000.612 000.383: inits 1
000.685 000.073: parsing arguments
000.686 000.001: expanding arguments
000.708 000.022: shell init
000.978 000.270: Termcap init
000.999 000.021: inits 2
001.067 000.068: init highlight
001.562 000.122 000.122: sourcing /usr/share/vim/vim74/syntax/syncolor.vim
001.654 000.268 000.146: sourcing /usr/share/vim/vim74/syntax/synload.vim
017.395 015.690 015.690: sourcing /usr/share/vim/vim74/filetype.vim
.
.
中间省略了
.
.
1157.594 000.048 000.048: sourcing /home/zoe/.vim/plugged/python-mode/autoload/pymode/tools/signs.vim
1157.772 000.060 000.060: sourcing /home/zoe/.vim/plugged/python-mode/autoload/pymode/tools/loclist.vim
1157.840 000.354 000.246: sourcing /home/zoe/.vim/plugged/python-mode/autoload/pymode/lint.vim
1158.458 1108.490: first screen update
1158.459 000.001: --- VIM STARTED ---
我们发现,打开这个test1.py文件,并加载这所有的插件花费了1158.458 ms(毫秒)。
为了比较起见,下面我们看看如果没有这些配置,如果不加载这些插件要多少时间呢,Vim的启动速度有多快:
执行下面的命令打开test1.py
vim --clean --startuptime clean.log test1.py(这里替换成自己需要打开的文件名)
打开之后同样在python_test文件夹之下会出现一个clean.log的日志文件,里面内容如下:
times in msec
clock self+sourced self: sourced script
clock elapsed: other lines
000.000 000.000: --- VIM STARTING ---
000.000 000.000: Allocated generic buffers
001.000 001.000: locale set
001.000 000.000: clipboard setup
001.000 000.000: window checked
003.000 002.000: inits 1
006.000 003.000: parsing arguments
007.000 001.000: expanding arguments
052.000 045.000: shell init
052.000 000.000: Termcap init
052.000 000.000: inits 2
052.000 000.000: init highlight
067.000 001.000 001.000: sourcing D:\Program Files (x86)\Vim\vim81\syntax\syncolor.vim
067.000 002.000 001.000: sourcing D:\Program Files (x86)\Vim\vim81\syntax\synload.vim
073.000 005.000 005.000: sourcing D:\Program Files (x86)\Vim\vim81\filetype.vim
.
.
.
.
256.000 000.000: BufEnter autocommands
256.000 000.000: editing files in windows
256.000 000.000: VimEnter autocommands
256.000 000.000: before starting main loop
263.000 007.000: first screen update
263.000 000.000: --- VIM STARTED ---
我们发现,加载的内容少了很多,时间少了很多,总共只花费了263毫秒。
注意:
(1)--clean这个参数的含义如下:
--clean 'nocompatible', Vim defaults, no plugins, no viminfo,
即仅仅使用默认的vim设置,不加载插件,也不启用其它的vimrc的配置等。
(2)另外,vim8.0版本可以使用--clean参数,vim7.4版本似乎没有这么参数,所以还是更新一下vim到新版本。
为了确定启动时或加载缓冲区时会运行哪些脚本,Vim会遍历“runtimepath”。该设置是一组以逗号分隔的目录列表,各个目录的结构都是一致的。Vim会检查每个目录的结构,找到需要运行的脚本,并按照目录在列表中的顺序一一处理。本文会以window上的vim和Linux上面的vim作为比较来说明:
运行以下命令就可以检查系统上的runtimepath:
3.1 遍历搜索路径runtimepath
启动一个vim之后,执行
:set runtimepath
(1)在window下面得到的结果如下:
runtimepath=
~/vimfiles,
~/vimfiles/after,
D:\Program Files (x86)\Vim\vim81, # 这后面三个其实就是vim的安装目录之下的目录
D:\Program Files (x86)\Vim/vimfiles,
D:\Program Files (x86)\Vim/vimfiles/after
# 顺序我适当的调整了一下,当前用户目录之下的 vimfiles 类似于Linux下面的 .vim 文件夹,需要自己手动创建,否则是没有的。另外我window上面还没有安装其他的任何自定义插件,所以这里可能和后面的Linux的结果有所差别
(2)在Linux下面得到的结果:
runtimepath=
~/.vim, # 前面这5个是自己安装的插件python-mode和nerdtree
~/.vim/after,
~/.vim/plugged/nerdtree/,
~/.vim/plugged/python-mode/,
~/.vim/plugged/python-mode/after,
/usr/share/vim/vim74, #这后面三个同样是vim的安装路径之下的三个目录,这里是vim7.4版本
/usr/share/vim/vimfiles,
/usr/share/vim/vimfiles/after,
# 这里的顺序有所调整
比较发现,其实不管是Linux还是windows上面的vim其实运行时搜索路径(runtimepath)都是一样的结构。
3.2 runtimepath包含的目录简介
runtimepath默认包含以下目录。并非所有这些都必须出现在文件系统中,但如果存在就会被使用。
~/.vim
主目录,保存个人偏好的文件。
/usr/local/share/vim/vimfiles
系统范围的Vim目录,保存由系统管理员决定的文件。
/usr/local/share/vim/vim81
即$VIMRUNTIME,保存与Vim一起分发的文件。
/usr/local/share/vim/vimfiles/after
系统范围Vim目录中的“after”目录。系统管理员可以利用该目录来覆盖默认设置,或添加新的设置。
~/.vim/after
主目录中的“after”目录。可以利用该目录用个人偏好覆盖默认设置或系统设置,或添加新的设置。
这些目录会按照顺序处理,所以要说“after”目录有什么特别的话,那就是它位于列表末尾。实际上“after”并没有什么特别之处。
在处理每个目录时,Vim都会查找具有特定名称的子文件夹。如果想了解更多这方面的信息,
请参阅:help runtimepath。下面我们只挑部分进行说明。
plugin/
编辑任何类型的文件都会自动加载的Vim脚本文件,称为“全局插件”。
autoload/
(不要与“插件”相混淆。)自动加载中的脚本包含仅在其他脚本请求时加载的函数。
ftdetect/
用于检测文件类型的脚本。可以根据文件扩展名、位置或内部文件内容决定文件类型。
ftplugin/
编辑已知类型的文件时执行的脚本。
compiler/
定义如何运行各种编译器或格式化工具,以及如何解析其输出。可以在多个ftplugins之间共享。且不会自动执行,必须通过 :compiler 调用。
pack/
Vim 8原生软件包的目录,它采用了“Pathogen”格式的包管理。原生的包管理系统不需要任何第三方代码。
注意:
(1)上面的那些runtimepath文件夹下面可能包含上面的文件夹中的一个或者是几个,不一定全部包含,每一个文件夹的含义已经有所说明了;
(2)最后,通用的编辑器设置都会放到~/.vimrc中。你可以通过它来设置用于覆盖特定文件类型的默认值。有关.vimrc设置的全面讲解,请运行 :options。
在Vim中,插件只是脚本,必须放在runtimepath中的正确位置才能执行。从概念上讲,插件的安装非常简单:只需下载文件。问题在于,很难删除或更新某些插件,因为它们的子目录加入到了runtimepath中,很难判断哪个插件负责哪些文件。
为了满足这种需求,网上出现了很多插件管理器。最早在2003年就出现了Vim.org插件仓库。然而,直到2008年左右,插件管理器的概念才真正流行起来。
这些工具在Vim的runtimepath中添加了单独的查检目录,并会为插件文档编译帮助标签。大多数插件管理器还可以从网上安装和更新插件代码,有的还支持并行更新,或者显示彩色的进度条。
以下是按时间顺序整理的插件管理器。我按照每个插件最早和最新版本进行了排序,如果找不到官方的发行版本,则根据最早和最后的提交日期排序。
2006年3月- 2014年7月:Vimball(分发格式和关联的Vim命令)
2008年10月- 2015年12月:Pathogen(由于原生vim包被弃用)
2009年8月- 2009年12月:Vimana
2009年12月- 2014年12月:VAM
2010年8月 - 2010年12月:Jolt
2010年10月 - 2012年12月:tplugin
2010年10月 - 2014年2月:Vundle(在NeoBundle破解代码后停止使用)
2012年3月 - 2018年3月:vim-flavor
2012年4月 - 2016年3月:NeoBundle(被弃用,建议使用dein)
2013年1月 - 2017年8月:infect
2013年2月 - 2016年8月:vimogen
2013年10月 - 2015年1月:vim-unbundle
2013年12月 - 2015年7月:Vizardry
2014年2月 - 2018年10月:vim-plug
2015年1月 - 2015年10月:enabler
2015年8月 - 2016年4月:Vizardry 2
2016年1月 - 2018年6月:dein.vim
2016年9月 - 至今:原生Vim 8
2017年2月 - 2018年9月:minpac
2018年3月 - 2018年3月:autopac
2017年2月 - 2018年6月:pack
2017年3月 - 2017年9月:vim-pck
2017年9月 - 2017年9月:vim8-pack
2017年9月 - 2019年5月:volt
2018年9月 - 2019年2月:vim-packager
2019年2月 - 2019年2月:plugpac.vim
首先要注意,这些工具五花八门,其次通常每个工具在活跃大约四年后就会过时。
最稳定的管理插件的方法是使用Vim 8的内置功能,该功能不需要第三方代码。下面让我们具体来看看这种方法。
首先在用户目录之下创建一个pack目录,然后在pack目录中创建两个子目录opt和start。注意这里,这里的自定义名称是我们要对插件进行的一个分类,比如自动补全的、导航的、调试的等等,方便管理,当然我就统统放在一个目录之下也行,只是不方便管理。
mkdir -p ~/.vim/pack/自定义名称/{opt,start}
注意事项:
(1)注意占位符 自定义名称。这个名称完全取决于你。我们用它对包进行分类。大多数人会把所有的插件都扔进一个无意义的类别中,这样完全没问题。你可以选择自己喜欢的名称,在本文中我选择使用 foobar。理论上,你也可以创建多个类别,比如~/.vim/pack/navigation, ~/.vim/pack/linting等,当然我也可以都放在foobar 这个目录之下。
(2)Vim不会检测类别之间的重复,如果存在重复,则会加载两次。
(3)“start”目录中的包会自动加载。而对于“opt”目录中的包,只有通过:packadd命令特别请求,Vim才会加载。opt中适合保存不常用的软件包,以及为保持Vim的快速启动不必要运行的脚本。请注意,:packadd没有相反的命令卸载包。
在下述示例子中,我们将添加“ctrlp”模糊查找插件到opt目录。下载最新版本的命令如下:
curl -L https://github.com/kien/ctrlp.vim/archive/1.79.tar.gz \
| tar zx -C ~/.vim/pack/foobar/opt #解压到刚创建的opt文件夹之下
该命令创建了 ~/.vim/pack/foobar/opt/ctrlp.vim-1.79 文件夹,现在这个包可以使用了。我们再次回到vim中,为这个新包创建一个帮助标签的索引:
:helptags ~/.vim/pack/foobar/opt/ctrlp.vim-1.79/doc
该命令会在刚安装的ctrlp-1.79目录包的doc目录中创建了一个名叫”tags“的文件,这样Vim的内部帮助系统就可以使用它的内容了。(或者你也可以在包加载之后运行一次:helptags ALL,该命令会处理runtimepath下的所有文档。)
在需要使用包时,只需加载它(Tab自动补齐也可以用于插件名,所以不需要输入全名):
下面在启动vim之后,选择性的加载opt目录之下的插件
:packadd ctrlp.vim-1.79
packadd会把包的根目录放到runtimepath中,然后运行它的plugin和ftdetect脚本。在加载ctrlp之后,就可以按Ctrl-P来弹出模糊文件查找了。