Skip to main content

Ch22. Vimrc

在先前的章节中,您学习了如何使用Vim。在本章,您将学习如何组织和配置Vimrc。

Vim如何找到Vimrc

对于Vimrc,常见的理解是在根目录下添加一个 .vimrc 点文件(根据您使用的操作系统,文件路径名可能不同)。

实际上,Vim在多个地方查找vimrc文件。下面是Vim检查的路径:

  • $VIMINIT
  • $HOME/.vimrc
  • $HOME/.vim/vimrc
  • $EXINIT
  • $HOME/.exrc
  • $VIMRUNTIME/default.vim

当您启动Vim时,它将在上面列出的6个位置按顺序检查vimrc文件,第一个被找到的vimrc文件将被加载,而其余的将被忽略。

首先,Vim将查找环境变量 $VIMINIT。如果没有找到,Vim将检查 $HOME/.vimrc。如果还没找到,VIm就检查 $HOME/.vim/vimrc。如果Vim找到了vimrc文件,它就停止查找,并使用 $HOME/.vim/vimrc

关于第一个位置,$VIMINIT 是一个环境变量。默认情况下它是未定义的。如果您想将 ~/dotfiles/testvimrc 作为 $VIMINTI 的值,您可以创建一个包含那个vimrc路径的环境变量。当您运行 export VIMINIT='let $MYVIMRC="$HOME/dotfiles/testvimrc" | source $MYVIMRC'后,VIm将使用 ~/dotfiles/testvimrc 作为您的vimrc文件。

第二个位置,$HOME/.vimrc 是很多Vim用户习惯使用的路径。$HOME 大部分情况下是您的根目录(~)。如果您有一个 ~/.vimrc 文件,Vim将使用它作为您的vimrc文件。

第三个,$HOME/.vim/vimrc,位于 ~/.vim 目录中。您可能已经有了一个 ~/.vim 目录用于存放插件、自定义脚本、或视图文件。注意这里的vimrc文件名没有“点”($HOME/.vim/.vimrc 不会被识别,但 $HOME/.vim/vimrc能被识别)。

第四个,$EXINIT 工作方式与 $VIMINIT 类似。

第五个,$HOME/.exrc 工作方式与 $HOME/.vimrc 类似。

第六个,$VIMRUNTIME/defaults.vim 是Vim编译时自带的默认vimrc文件。在我的电脑中,我是使用Homebrew安装的Vim8.2,所以我的路径是(/usr/local/share/vim/vim82)。如果Vim在前5个位置都没有找到vimrc文件,它将使用这个Vim自带的vimrc文件。

在本章剩余部分,我将假设vimrc使用的路径是 ~/.vimrc

应该把什么放在Vimrc中?

我刚开始配置Vimrc时,曾问过一个问题,“我究竟该把什么放在Vimrc文件中?”。

答案是,“任何您想放的东西”。 直接复制粘贴别人的vimrc文件的确是一个诱惑,但您应当抵制这个诱惑。如果您仍然坚持使用别人的vimrc文件,确保您知道这个vimrc干了什么,为什么他/她要用这些设置?以及他/她如何使用这些设置?还有最重要的是,这个vimrc文件是否符合你的实际需要?别人使用并不代表您也要使用。

Vimrc基础内容

简单地说,一个vimrc是以下内容的集合:

  • 插件
  • 设置
  • 自定义函数
  • 自定义命令
  • 键盘映射

当然还有一些上面没有提到的内容,但总体说,已经涵盖了绝大部分使用场景。

插件

在前面的章节中,我曾提到很多不同的插件,比如fzf.vim, vim-mundo, 还有 vim-fugitive.

十年前,管理插件插件是一个噩梦。但随着很多现代插件管理器的开发,现在安装插件可以在几秒内完成。我现在正在使用vim-plug作为我的插件管理器,所以我在本节中将使用它。相关概念和其他流行的插件管理器应该是类似的。我强烈建议您多试试几个插件管理器,比如:

除了上面列出的,还有很多插件管理器,可以随便看看。要想安装 vim-plug,如果您使用的是Unix,运行:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

要添加新的插件,将您的插件名(比如,Plug 'github-username/repository-name') 放置在 call plug#begin()call plug#end() 之间的行中. 所以,如果您想安装 emmet-vimnerdtree,将下面的片段放到您的vimrc中:

call plug#begin('~/.vim/plugged')
Plug 'mattn/emmet-vim'
Plug 'preservim/nerdtree'
call plug#end()

然后保存修改,加载当前vimrc (:source %), 然后运行 :PlugInstall 安装插件。

如果以后您想删除不使用的插件,您只需将插件名从 call 代码块之间移除,保存并加载,然后运行 :PlugClean 命令将它从机器上删除。

Vim 8 有自己的内置包管理器。您可以查阅 :h packages 了解更多信息。在后面一章中,我将向您展示如何使用它。

设置

在任意一个vimrc文件中都可以看到大量的 set 选项。 如果您在命令行模式中运行 set 命令,它只是暂时的。当您关闭Vim,设置就会丢失。比如,为了避免您每次运行Vim时都必须在命令行模式运行 :set relativenumber number 命令,您可以将这个命令添加在vimrc中:

set relativenumber number

有一些设置需要您赋予一个值,比如 set tabstop=2。想了解一个设置可以接收什么类型的值,可以查看帮助页。

您也可以使用 let 来代替 set(确保在选项前添加一个 &号)。使用 let ,您可以使用表达式进行赋值。比如,要想仅当某个路径存在时,才将该路径赋予 'dictionary' 选项:

let s:english_dict = "/usr/share/dict/words"

if filereadable(s:english_dict)
let &dictionary=s:english_dict
endif

在后面的章节中您将了解关于Vimscript赋值和条件的知识。

要查看Vim中所有可用的选项,查阅 :h E355

自定义函数

Vimrc是一个很好的用来放置自定义函数的地方。在后面的章节中您将学习如何写您自己的Vimscript函数。

自定义命令

您可以使用 command 创建一个自定义命令行命令。

比如,创建一个用于显示今天日期的基本命令 GimmeDate

:command! GimmeDate echo call("strftime", ["%F"])

当您运行 :GimmeDate 时,Vim将显示一个类似 "2021-01-1"的日期。

要创建一个可以接收输入的基本命令,您可以使用 <args> 。如果您想向 GimmeDate 传递一个时间/日期格式参数:

:command! GimmeDate echo call("strftime", [<args>])

:GimmeDate "%F"
" 2020-01-01

:GimmeDate "%H:%M"
" 11:30

如果您想限定参数的数目,您可以使用 -nargs 标志。-nargs=0 表示没有参数,-nargs=1 表示传递1个参数,-nargs=+ 表示至少1个参数,-nargs=* 表示传递任意数量的参数,-nargs=? 表示传递0个或1个参数。如果您想传递n个参数,使用 -nargs=n(这里 n 是一个任意整数)。

<args> 有两个变体:<f-args><q-args> 。前者用来向Vimscript函数传递参数,后者用来将用户输入自动转换为字符串。

使用 args:

:command! -nargs=1 Hello echo "Hello " . <args>
:Hello "Iggy"
" returns 'Hello Iggy'

:Hello Iggy
" Undefined variable error

使用 q-args:

:command! -nargs=1 Hello echo "Hello " . <q-args>
:Hello Iggy
" returns 'Hello Iggy'

使用 f-args:

:function! PrintHello(person1, person2)
: echo "Hello " . a:person1 . " and " . a:person2
:endfunction

:command! -nargs=* Hello call PrintHello(<f-args>)

:Hello Iggy1 Iggy2
" returns "Hello Iggy1 and Iggy2"

当您学了关于Vimscript函数的章节后,上面的函数将更有意义。

查阅 :h command:args 了解更多关于command和args的信息。

键盘映射

如果您发现您重复地执行一些相同的复杂操作,那么为这些复杂操作建立一个键盘映射将会很有用:

比如,在我的vimrc文件中有2个键盘映射:

nnoremap <silent> <C-f> :GFiles<CR>

nnoremap <Leader>tn :call ToggleNumber()<CR>

在第一个中,我将 Ctrl-F 映射到 fzf.vim 插件的 :Gfiles 命令(快速搜索Git文件)上。在第二个中,我将 <leader>tn 映射到调用一个自定义函数 ToggleNumber (切换 norelativenumberrelativenumber 选项)。Ctrl-f 映射覆盖了Vim的原生的页面滚动。如果发生冲突,您的映射将会覆盖Vim的设置。因为从几乎从来不用Vim原生的页面滚动功能,所以我认为可以安全地覆盖它。

另外,在 <Leader>tn 中的 "leader" 键到底是什么?

Vim有一个leader键用来辅助键盘映射。比如,我将 <leader>tn 映射为运行 ToggleNumber() 函数。如果没有leader键,我可能会用 tn,但Vim中的 t 已经用做其他功能("till"搜索导航命令)了。有了leader键,我现在先按定义好的leader键作为开头,然后按 tn,而不用干扰已经存在的命令。您可以设置leader键作为您映射的连续按键的第一个按键。默认Vim使用反斜杠作为leader键(所以 <Leader>tn 会变成 "反斜杠-t-n")。

我个人喜欢使用空格 <Space> 作为leader键,代替默认的反斜杠。要想改变您的leader键,将下面的文本添加到您的vimrc中:

let mapleader = "\<space>"

上面的 nnoremap 命令可以分解为三个部分:

  • n 表示普通模式。
  • nore 表示禁止递归。
  • map 是键盘映射命令。

如果不想使用 nnoremap,您至少也得使用 nmap (nmap <silent> <C-f> :Gfiles<CR>)。但是,最好还是使用禁止递归的版本,这样是为了避免键盘映射时潜在的无限循环风险。

如果您进行键盘映射时不使用禁止递归,下面例子演示了会发生什么。假设您想给 B 添加一个键盘映射,用来在一行的末尾添加一个分号,然后跳回前一个词组(回想一下,B 是Vim普通模式的一个导航命令,用来跳回前一个词组)。

nmap B A;<esc>B

当您按下 B ...哦豁,Vim开始失控了,开始无止尽的添加;(用 Ctrl-c终止)。为什么会发生这样的情况?因为在键盘映射 A;<esc>B中,这个 B不再是Vim原生的导航命令,它已经被映射到您刚才创建的键盘映射中了。这是您实际上执行的操作序列:

A;<esc>A;<esc>A;<esc>A;esc>...

要解决这个问题,您需要指定键盘映射禁止递归:

nnoremap B A;<esc>B

现在再按一下 B 试试。这一次它成功地在行尾添加了一个 ;,然后跳回到前一个词组。这个映射中的 B 就表示Vim原生的 B了。

Vim针对不同的模式有不同的键盘映射命令。如果您想创建一个插入模式下的键盘映射 jk,用来退出插入模式:

inoremap jk <esc>

其他模式的键盘映射命令有:map(普通、可视、选择、以及操作符等待模式), vmap(可视、选择), smap(选择), xmap(可视), omap(操作符等待模式), map!(插入、命令行), lmap(插入,命令行,Lang-arg模式), cmap(命令行), 还有tmap(终端任务)。在这里我不会详细的讲解它们,要了解更多信息,查阅 :h map.txt

创建最直观、最一致、最易于记忆的键盘映射。

组织管理Vimrc

一段时候键,您的vimrc文件就会变大且复杂得难以阅读。有两种方法让您的vimrc文件保持整洁:

  • 将您的vimrc文件划分为几个文件
  • 折叠您的vimrc文件

划分您的vimrc

您可以使用Vim的 :source 命令将您的vimrc文件划分为多个文件。这个命令可以根据给定的文件参数,读取文件中的命令行命令。

让我们在 ~/.vim 下创建一个子文件夹,取名为 /settings~/.vim/settings)。名字可以取为任意您喜欢的名字。

然后你在这个文件夹下创建4个文件:

  • 第三方插件 (~/.vim/settings/plugins.vim).
  • 通用设置 (~/.vim/settings/configs.vim).
  • 自定义函数 (~/.vim/settings/functions.vim).
  • 键盘映射 (~/.vim/settings/mappings.vim) .

~/.vimrc 里面添加:

source $HOME/.vim/settings/plugins.vim
source $HOME/.vim/settings/configs.vim
source $HOME/.vim/settings/functions.vim
source $HOME/.vim/settings/mappings.vim

~/.vim/settings/plugins.vim 里面:

call plug#begin('~/.vim/plugged')
Plug 'mattn/emmet-vim'
Plug 'preservim/nerdtree'
call plug#end()

~/.vim/settings/configs.vim 里面:

set nocompatible
set relativenumber
set number

~/.vim/settings/functions.vim 里面:

function! ToggleNumber()
if(&relativenumber == 1)
set norelativenumber
else
set relativenumber
endif
endfunc

~/.vim/settings/mappings.vim 里面:

inoremap jk <esc>
nnoremap <silent> <C-f> :GFiles<CR>
nnoremap <Leader>tn :call ToggleNumber()<CR>

这样您的vimrc文件依然能够正常工作,但现在它只有4行了。

使用这样的设置,您可以轻易知道到哪去修改配置。如果您要添加一些键盘映射,就将它们添加在 /mappings.vim 文件中。以后,当您的vimrc变大时,您总是可以新建几个子文件来缩小它的大小。比如,如果您想为主题配色创建相关设置,您可以添加 ~/.vim/settings/themes.vim

保持单独的一个Vimrc文件

如果您倾向于保持一个单独的vimrc文件,以使它更加便于携带,您可以使用标志折叠让它保持有序。在vimrc文件的顶部添加一下内容:

" setup folds {{{
augroup filetype_vim
autocmd!
autocmd FileType vim setlocal foldmethod=marker
augroup END
" }}}

Vim能够检测当前buffer所属的文件类型 (:set filetype?). 如果发现属于 vim 类型,您可以使用标志折叠。回想一个标志折叠的用法,它使用 {{{}}} 来指明折叠的开始和结束。

添加 {{{}}} 标志将您的vimrc文件其他部分折叠起来。(别忘了使用 " 对标志进行注释):

" setup folds {{{
augroup filetype_vim
autocmd!
autocmd FileType vim setlocal foldmethod=marker
augroup END
" }}}

" plugins {{{
call plug#begin('~/.vim/plugged')
Plug 'mattn/emmet-vim'
Plug 'preservim/nerdtree'
call plug#end()
" }}}

" configs {{{
set nocompatible
set relativenumber
set number
" }}}

" functions {{{
function! ToggleNumber()
if(&relativenumber == 1)
set norelativenumber
else
set relativenumber
endif
endfunc
" }}}

" mappings {{{
inoremap jk <esc>
nnoremap <silent> <C-f> :GFiles<CR>
nnoremap <Leader>tn :call ToggleNumber()<CR>
" }}}

您的vimrc文件将会看起来类似下面:

+-- 6 lines: setup folds -----

+-- 6 lines: plugins ---------

+-- 5 lines: configs ---------

+-- 9 lines: functions -------

+-- 5 lines: mappings --------

启动Vim时加载/不加载Vimrc和插件

如果您要启动Vim时,既不加载Vimrc,也不加载插件,运行:

vim -u NONE

如果您要启动Vim时,不加载Vimrc,但加载插件,运行:

vim -u NORC

如果您要启动Vim时,加载Vimrc,但不加载插件,运行

vim --noplugin

如果您要Vim启动加载一个 其他的 vimrc, 比如 ~/.vimrc-backup, 运行:

vim -u ~/.vimrc-backup

聪明地配置Vimrc

Vimrc是定制Vim时的一个重要组件,学习构建您的Vimrc最好是首先阅读他人的vimrc文件,然后逐渐地建立自己的。最好的vimrc并不是谁谁谁使用的,而是最适合您的工作需要和编辑风格的。