七阶子博客: 杂文 | 游戏 | 戏剧 | 白蛇 | 文艺 | 编程 | 近期
请输入标题关键字或 yyyymmdd 格式的日期

    使 SpaceVim 与个人 Vim 配置共存

    前言

    众所周知,Vim 是需要精心配置才能发挥最大作用的编辑器。而 SpaceVim 就是一个超豪华的配置套装。我偶有试用过这套配置,体验下来,感觉有以下几个优点:

    1. 收集与筛选了许多优秀插件,可节省个人盲目搜寻的痛苦与纠结。
    2. 将一些协作的插件及相关配置模块化,适用于不同的编辑目的或工程,可在启动时有选择地载入不同模块;支持各种常见编程语言,并且开发活跃还在扩展模块。
    3. 统一与调配快捷键资源,并且有延迟提示,便于辅助记忆快捷键。
    4. 该项目对于愿意自己折腾 vim 配置或插件开发者也是很好的参考。
    5. 界面很漂亮,接近 IDE 目标。(不过这是我个人最不关心的一点)

    但是,它也有几个缺点与局限很难调和:

    1. 对于 vim 初学者,SpaceVim 显得过于庞杂,与 vim 的简约轻便原则有点相悖。
    2. 对于 vim 熟练者,使用习惯很难迁就与迁移,与 vim 的个性灵活原则有点违和。
    3. 还有一点,倒不是太关键,模块化还不够彻底,只能启动时配置,使用中不便动态增加模块扩展功能。

    所以,虽然我几次试用,也未能成为深度用户,还是更习惯于自己的山寨配置,敝帚自珍。后来,我想了个折衷方案,让 SpaceVim 与自己的配置共存,看情况试用,互为参照,顺便也能关注其更新进度。

    于是写这篇文章的初衷就是阐述一下如何让两套或多套复杂配置共存,若有其他同道有类似需求也可以作为参考教程。截止写本文时,SpaceVim 的版本大约是 v0.9。另外,我当前使用的是 Linux 系统,Windows 系统没有软链接概念,恐怕很难用上这里的方法。

    手动安装 SpaceVim

    首先,在安装时自是不能采用它提供的一键安装脚本。虽然它会备份你的 ~/.vim 个人配置目录,然后在卸载时会恢复该目录。但是如果通过反复安装与卸载来切换使用 SpaceVim 与个人配置,那就显得太滑稽了。

    $ cd ~
    $ git clone https://github.com/SpaceVim/SpaceVim
    $ mkdir ~/.vim/SpaceVim.d
    $ ln -s .vim/SpaceVim.d .SpaceVim.d
    

    按 SpaceVim 项目的官方默认做法,是安装到 ~/.SpaceVim 目录中,它自己的配置又读取自 ~/.SpaceVim.d 。这都是以 . 开头的隐藏目录。

    只不过我在执行 git clone 命令时,不想麻烦多敲额外路径参数,就让它默认安装到SpaceVim 中了。如果实在想吻合目录,clone 时可附加参数 -C .SpaceVim

    我将 ~/.SpaceVim.d 软连接至 ~/.vim 的一个子目录,是因为我自己的 ~/.vim目录也是一个 git 仓库,保存个人 Vim 配置。而使用 SpaceVim 的自定义配置也算是个人 vim 配置的一部分了,就统一放在起以便管理。一开始我还直接软链接至 ~/.vim,后来想想还是额外建个 SpaceVim.d 子目录放 SpaceVim 的个人配置比较好。

    支持多套配置的统一 vimrc 入口

    我的入口 vimrc 只是一个配置分发脚本。该脚本可以是 ~/.vimrc ,但放在~/.vim/vimrc 明显是更好的选择。核心部分可总结为如下两行:

    let $VIMHOME = $HOME . '/.vim'
    execute 'source ' . $VIMHOME . '/' . v:progname . '.vim'
    

    关键是 v:progname 这个变量,它代表着从 shell 启动 vim 时的程序命令名字。对于大多数用户,大多数情况下,你可能就用如下命令启动 vim 的:

    $ vim
    

    那么 v:progname 的值就是字符串 "vim"。但是如果你通过指向 vim 的链接文件(无论硬链接还是软链接)启动 vim 后,v:progname 的值都是那个链接文件名,而不再是 "vim" 了。在一些 Linux 发行版本中,vi 就是一个指向 vim 的软链接,所以用即使用 vi 命令打开的也不是古老的 vi 了,而也是 vim ,此时就有 v:progname == "vi"。(不过如果 vi 是 vim 的 alias ,用别名的方式启动 vim,那 v:progname 的值仍是 "vim"

    如果你使用过 vim 源码编译过 vim ,并且使用 --prefix=$HOME 选项安装到个人家目录,使用 ls -l ~/bin 就能发现还为你自动安装了许多 vim 的软链接,如:

    ex -> vim
    rview -> vim
    rvim -> vim
    view -> vim
    vimdiff -> vim
    

    这其中的设计思想是,vim 不仅可以用于普通文本编辑,也可以当作其他工具启用。譬如view 就是当作一个只读的文本查看器来使;如果使用我上面那两行 vimrc ,这就会被分发加载 ~/.vim/view.vim 这个 vim 脚本,作为本次启动 view 的实际 vimrc 。当然了,我其实也不用 view 的,查看器更喜欢用 less 命令。

    我个人将 vim 打造为代码编辑器的配置命名为 vc ,那只是自己为 vim 额外创建的软链接,实际配置的 vimrc 就将是 vc.vim 。此外,我也放了一个最简化的配置名为vi.vim 以供 vi 命令使用;一份比较中庸默认的配置 vim.vim 也就是给 vim 启用的。

    按此思想,就可以为 SpaceVim 也创建一个启动 vimrc 。当然这个名字可能有点长,敲命令略繁,我就简名为了 spvim 了:

    $ cd bin
    $ ln -s vim spvim
    $ cd .vim
    $ touch spvim.vim
    

    定制 SpaceVim 启动 vimrc

    然后就是具体编辑 spvim.vim 文件作为 SpaceVim 的启动脚本,核心也只是两行:

    let $SPACEVIM = $HOME . '/SpaceVim'
    execute 'source ' . $SPACEVIM . '/init.vim'
    

    定义一个名为 $SpaceVim 的环境变量,保存之前 git clone 安装的路径,加载该项目根目录下的 init.vim 就启动了 SpaceVim 。在当前的 SpaceVim 版本,在启动时会读取 ~/.SpaceVim.d/init.toml 配置文件。而在之前安装时我已将该目录链接至~/.vim/SpaceVim.d 目录了,所以只要将 init.toml 扔在那个目录就可以了。于是spvim.viminit.toml 都在 ~/.vim 目录下,可以像自己原来的 vim 配置一样存入 git 仓库。

    自 SpaceVim 将在 .SpaceVim.d 目录下的自定义配置(主要是全局变量配置与模块配置)格式从 .vim 格式改为 .toml 后,有些小伙伴不知道在哪里可以写 viml 代码定制某些选项了。按官方文档建议是,在 .SpaceVim.d/init.toml 配置中加两行:

    bootstrap_before = "path#to#autoload_function"
    bootstrap_after = "path#to#autoload_function"
    

    定义两个自动加载函数,让 SpaceVim 在加载其自身主体功能的前后分别调用相应的函数。用户可以将这两个函数写在 ~/.vim/autoload~/.SpaceVim.d/autoload 目录下。这个配置实现略为复杂,不够直接,只在启动时执行一次代码包装为函数是不必要的;当然自动加载函数有个副作用可能更重要,可以搜寻加载包含目标函数的脚本。

    但如果按这里使用 spvim.vim 启动配置的话,添加额外个人配置就很简单了,只要继续向这个文件复制粘贴就行了。当然,应该加在原来那两行之前,还是之后,是要考虑其不同影响意义的。譬如,我想令 dein 用 ssh 协议下载插件,就应该加在 SpaceVim 初始化之前,我想取消相对行号就应该加在之后。

    let g:dein#types#git#default_protocol = 'ssh'
    let $SPACEVIM = $HOME . '/SpaceVim'
    execute 'source ' . $SPACEVIM . '/init.vim'
    set norelativenumber
    

    尽管当作普通 vimrc 自由写入任意合法 viml 代码,如用 source 加载其他脚本,用call 调用其他函数。如果想要复用自己原来的某些公用配置脚本,还可利用$SpaceVim 环境变量来判断是否使用了 SpaceVim 。当然理论上就用 v:progname 判断也是可以的,但是为这么个豪华的配置定义一个环境变量,会显得更正式地尊重。

    最大的挑战可以来源于若个人配置之前使用与 SpaceVim 不同的插件管理方案(比如我自己就只用 vim8 自带的 packadd),如果重复加载相同的插件会怎么样?其实这个问题也没有一开始想象的那么严重。现代的优秀插件,都普遍使用自动加载函数的机制,它只会使用已经加载的函数定义,或者在 &rtp 路径中第一个找到的脚本。所以,最好的结果可能是就没影响。当然了,如果能利用 $SpaceVim 分支判断避免就更好,哪怕只为效率。

    脚本源码参考

    本文以上介绍的代码片断,可以在个人 github 上找到完整源码参考。其实我的 vimrc 也不是直接放在 ~/.vim 目录下了,而是将多入口的与多个 vimrc 单独放在 start子目录下,~/.vim/vimrc 是指向 start/main.vim 的软链接。并且,在通过v:progname 启动命令查找 vimrc 脚本文件时,还进行一定程度的模糊查找。

    1. main.vim vim 统一入口 vimrc
    2. spvim.vim SpaceVim 启动 vimrc
    3. SpaceVim.d/init.toml这基本就是改自 SpaceVim 的默认 toml 配置了。