前文已提及,vim 脚本主要用 :source
命令加载,然而很多情况下又不需要手动执行
该命令。只要将脚本放在特定的目录下,vim 就有个机制能自动搜寻并加载。
首先要知道有 &runtimepath
(常简写为 &rtp
)这个选项。它与系统的环境变量
$PATH
有点类似,就是一组有序的目录名称,用于 Vim 在许多不同情况下搜寻 *.vim
脚本文件的。你可以在命令行输入 :echo &rtp
查看当前运行的 vim 有哪些“运行时目
录”,一般都会包含 ~/.vim
这个目录。
vimrc
,运行时需要加载的脚本,一般都是从
&rtp
目录列表中搜索的。&rtp
目录下的 plugin/
搜索 *.vim
文件,并加载所有
找到的脚本文件。需要注意的是在 plugin/
子目录下的所有脚本也会自动加载。除
非你先在 vimrc 中用选项禁用加载插件这个行为。&filetype
被识别时,Vim 会从所有 &rtp
目录下的 ftplugin/
子目录中搜索以文件类型开始的脚本文件,然后加载执行。比如编辑一个 cpp
文件
时,ftplugin/
目录下的 cpp.vim
cpp_*.vim
cpp/*.vim
都会被加载。所以,我们自己写的脚本,如果想让它在 vim 启动时自动生效,就扔到
~/.vim/plugin/
目录下,想只针对某种文件类型生效,就扔到 ~/.vim/ftplugin/
目录下。
目前主流的第三方插件,也会遵循这种子目录规范,然后安装时一般会将整个目录添加到
&rpt
中,以便让 Vim 找到对应的脚本。
Vim 一直有个追求的目标是启动快。当插件越来越多时,vim 启动时要解析大量的脚本文
件,就会被拖慢了。这时就出现了一个 autoload
自动加载函数的机制,这个巧妙的方
法可算是 VimL 发展的一个里程碑吧。而在这之前,须由用户在 plugin/*.vim
的复杂
脚本中用极具巧妙的编程技巧,才好实现延时加载。
虽然还没有讲到 VimL 的函数,但也可以在这里解释自动加载函数的原理与过程,毕竟这 不需要涉及到函数的具体实现。
例如,有一个 ~/.vim/autoload/foo.vim
脚本(或在其他任一个 &rtp
目录下的
autoload/
子目录也行),该脚本内定义一个函数 foo#bar()
,其中 #
之前的部
分必须与脚本文件名 foo.vim
相同。将有以下故事发生:
foo.vim
文件,也不知道它里面可能定义了什么复杂
的脚本内容。foo#bar()
第一次被调用时,比如从命令行中执行 :call foo#bar()
,vim 发
现 foo#bar
这个函数未定义,就会试图从这个函数名分析出它可能定义于 foo.vim
文件中。然后就从 &rtp
目录列表中,依次寻找其中 autoload/
子目录的
foo.vim
文件。将所找到的第一个 foo.vim
脚本加载,并停止继续寻找。如果在
所有 &rtp
目录下都找不到,那就是个错误了。:source
)完 foo.vim
,再次响应 :call foo#bar()
的函数调用,就
能正常执行了。foo.vim
文件中其实并没有定义 foo#bar()
这个函数,比如手误把函数名写
错了,写成了 foo#Bar()
,则 vim 在二次尝试执行 :call foo#bar()
时依然报错
说“函数未定义”。:call foo#bar()
,由于文件已加载,该函数是已定义的了,vim
就不需要再次寻找 foo.vim
文件了,直接执行就是。foo.vim
文件中还定义了一个 foo#bar2()
函数,由于之前是加载整个文件
,foo#bar2()
也是个已定义函数,也就可以直接调用到 :call foo#bar2()
。foo.vim
文件中根本不存在函数,如 :call foo#nobar()
。即
使之前已经加载过 foo.vim
一次,由于这个 foo#nobar
函数未定义,vim 会再次
从 &rtp
目录找到这个 foo.vim
文件再加载一次,然后再尝试 :call foo#nobar()
依然出错报错。各种细节过程可能很复杂,但总体思想还是很简单,就是延时加载,只有在必要时才额外 加载脚本。从用户使用角度,只要注意几点:
foo#bar()
必须与文件名 foo.vim
完全一致(大小写也最好一致)。如果
脚本是在 autoload
的深层子目录下,那函数名也必须是相对于 autoload
的路径
名,把路径分隔符 /
替换为 #
就是。即在 autoload/path/to/foo.vim
文件中
定义的函数名应该是 path#to#foo#bar()
。#
函数,并在首次使用时触发
相关脚本的加载。#
函数是全局作用域的,也可以认为各层 #
是完整的命名空间,当然从任何地方
访问时都须使用路径全名,即使从相同的脚本内访问也须用全名。#
命名,如 g:path#to#foo#varname
也能触发相应脚本文件的
自动(延时)加载,不过一般没有函数应用那么广泛。#
自动加载函数中,有时要注意不同 &rtp
目录下
同名文件的屏蔽效应。利用 VimL 的这个自动加载机制,还有效地避免了全局变量(函数)名的冲突问题,因为 函数名包含了路径名,而一般文件系统下是不会有重名文件的。唯一的问题是,这个函数 名有点长。