Neovim 中的 runtime 详解

437 阅读5分钟

Vim Runtime 介绍

vim runtime 就是 vim 可以执行 vim scripts 的目录,换句话说,如果你的 vim script 在这个目录中,那么打开 vim 时,它们就会自动被执行(或者手动执行)。

查看帮助信息

:h runtime

如果我们编写了一个 xxx.vim 或者 plugin,那么只要它在 runtime 目录中,vim 就可以执行它,无需再提供完整路径。

打开 vim 后,如果想手动执行某个 script,可以使用 runtime 命令。它是 Ex 模式中的命令,语法如下:

:ru[ntime][!] [where] {file} ..

在 Ex Mode 中使用 runtime 命令来执行一个 vim script 或 .lua,如下:

:runtime syntax/c.vim
:runtime syntax/c.lua
Note:

当执行上述 :runtime syntax/c.vim 时,所有在 runtimepath 中的 syntax/.c.vim 都会被搜索,第一个找到的 script 会被 source。

当执行 :runtime! 时,所有找到的 script 都会被依次执行;

另外,start 目录虽然不在 runtimepath 中,但是也会被搜索(并执行?),因为它在 packpath 中(后面再介绍 packpath)。

简单总结就是:

vim 执行 script 时,会搜索 runtimepath 中的目录,以及 packpath 中的 start/ 目录。

关于 packpath,主要包含以下一些值:

  • START   search only under "start" in 'packpath'
  • OPT     search only under "opt" in 'packpath'
  • PACK    search under "start" and "opt" in 'packpath'
  • ALL     first use 'runtimepath', then search under "start" and "opt" in 'packpath'

:ru[ntime][!] [where] {file} ..

When [where] is omitted, first 'runtimepath' is searched, then directories under "start" in 'packpath'are searched.
When {file} contains wildcards it is expanded to all matching files.  
Example:
    :runtime! plugin/**/*.vim

This is what Vim uses to load the plugin files when starting up.  
This similar command:
    :runtime plugin/**/*.vim

也就是说,所有 plugin/** 目录下的 .vim 都会被 source,这也是 Vim 加载 plugins 的原理。

总结:

只要一个目录被加入 runtimepath,那么 vim 会自动搜索这个目录下的 autoload/, plugin/ 等子目录,只要 script 在这些子目录中,就可以被执行。这个规则可以递归。

比如,通常我们会把 ~/.vim 加入 runtimepath,因此 ~/.vim/ 目录下的这些上述子目录(autoload/, plugin/ 等)肯定会被自动搜索,并且只要 .vim.lua 在这些子目录中就可以被执行。

当我们在 plugin/ 中安装了第三方插件,比如手动的方式 git clone http://xxx/hello.git 到这个目录中,那么 hello/ 这个插件目录会被自动添加到 runtimepath 中,并且其跟目录中的脚本会被执行,同时相应的子目录(autoload/, plugin/ 等)中脚本也会被执行。

当我们使用 vim-plug 来管理插件时,我们一般需要手动指定一个用于存放插件的目录,比如我指定 ~/.vim/plugged/,那么 vim-plug 会把该目录加入到 runtimepath 中了,也就相当于把它变成了 vim 可以自动搜索并执行脚本的目录了。所有的规则跟原默认那些目录都一样了。

~/.vim/
   └─ autoload/
   └─ plugin/
   └─ load.vim
   ···
   └─ plugged/
      └─ ack.vim/
      └─ any-jump.vim/
      └─ fzf.vim/
      └─ UltiSnips/
      └─ molokai/
      └─ vim-fish/
         └─ autoload/
         └─ ftdetect/
            └─ fish.vim
         └─ ftplugin/
            └─ fish.vim
		 └─ indent/
         └─ syntax/
         └─ UltiSnips/
         └─ bin/
            └─ man.fish
         └─ compiler/
            └─ fish.vim
NeoVim 的 runtime

对于 nvim 而言,也是一样,不过 nvim 默认的 runtimepath 是 ~/.config/nvim,由于 nvim 支持 lua,所以 lua/ 子目录会在搜索路径中,以便可以执行 .lua 脚本。执行 :h lua-require 查看详细。

Lua 中的 runtime

lua 主要会搜索 package.path 和 package.cpath 两个路径中的 .lua 脚本。而这两个的值会根据 runtimepath 的值而来,并且添加 /lua/?.lua/lua/?/init.lua 两个搜索路径,这两个 .lua 文件会自动执行。

简单来说就是:

只要 runtimepath 下有个子目录叫 /lua/,比如 runtimepath/lua/ ,那么该目录下的 .lua 都可以被自动执行。

另外,runtimepath/lua/?/init.lua 也会被执行。

package.path 和 package.cpath 还可以从 $LUA_CPATH$LUA_INIT 得来。

注: package.cpath 主要是用来搜索和读取 /lua/?.so 文件

When Lua starts, it initialises package.path and package.cpath with values of LUA_PATH and LUA_CPATH environment variables. Setting up these environment variables will be one clean way to set paths. Appending LUA_PATH's value with a double semi-colon will make Lua append the default path to the specified path.

Using bash on Linux, you can set the paths by adding these lines to the end of ~/.bashrc file. For example:

## final ;; ensure that default path will be appended by Lua

export LUA_PATH=";;"

export LUA_CPATH="./?.so;/usr/local/lib/lua/5.3/?.so;

                /usr/local/share/lua/5.3/?.so;"

Example

假如 runtimepath=/foo/bar,/xxx;yyy/baz,/abc

那么 lua Engine 可以读取:

- /foo/bar/lua/?.so
- /foo/bar/lua/a?d/j/g.elf
- /abc/lua/?.so
- /abc/lua/a?d/j/g.elf
    
/foo/bar,/xxx;yyy/baz,/abc ('runtimepath')

× ./?.so;/def/ghi/a?d/j/g.elf;/def/?.so (package.cpath)

= /foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so  

理解了 Lua Engine 搜索的执行 .lua 的原理之后,我们该如何在 nvim 配置环境来组织我们的 .lua 脚本

先查看 nvim 的目录环境:
:echo stdpath("config")
~/.config/nvim/

nvim 使用 stdpath 来保存配置文件信息,这个概念跟 vim 中的 runtimepath 有点不同。runtimepath 是执行 .vim 脚本的的环境目录。而 stdpath 是 nvim 的配置文件目录,可以执行 lua 脚本,也可以执行 .vim 脚本。所以,实际上,二者的功能上是有重复的,因为 nvim 的配置目录中其实也是脚本,区别在于:nvim 的 stdpath 中更多的是 .lua 类型的脚本。此外,nvim 兼容 vim,所以 nvim 除了会去执行 stdpath 中的 .lua.vim 脚本,也会去执行在 runtimepath 中的 .vim 脚本。

总结:

nvim 使用 stdpath 作为 runtime,有别与 vim 使用 runtimepath 作为 runtime。nvim 全面使用 lua 来进行配置,因此 stdpath 更注重 .lua 脚本。为了兼容 vim,nvim 也会读取 vim 中的 runtimepath 中的脚本。但是,需要注意的是,nvim 并不需要 runtimepath,也不需要 vimrc。

可见,nvim 使用 ~/.config/nvim/ 作为 runtimepath,因此我们只要在这个目录下创建 lua/ 子目录,并把要执行的 .lua 放在这个 lua/ 子目录下即可。

全文完!

如果你喜欢我的文章,请关注我的微信公众号 deliverit