Vim 是个博大精深的编辑器,虽然各种编辑器层出不穷(说实话 VS 也挺好用的) 但还是改不了这个习惯。

分享一下 Vim 的使用经验吧,自己也当做收集和整理

概览

  1. 模式 vim 有三种模式,普通(normal)/输入(insert)/可视(visal),通过 ESC 键来切换到 normal 状态,i(当然还有 I, a, A, o …) 和 v(当然还有 V, ctr-v) 分别进入 insert 和 visal 状态。normal 状态用来移动光标,输入指令等,insert 状态用来数据,visal 状态用来选择

  2. 动词、名词与量词 我个人习惯用这种划分方式来理解 vim 的各种操作。
    1. 动词,是一个操作,比如 y 就是复制,p 就是粘贴,d 是删除之类的,动词的 数量非常多,基本上能覆盖到你所有文本编辑中会遇到的操作。
    2. 名词,是一个位置,比如 h,j,k,l 在我的理解中,其实是名词,他们代表一个位置,比如 j 代表的是当前光标位置下一行的位置,而不是理解为移动光标到下一行这么一个动作。 因为名词是动词的宾语,比如 y 是复制,拿它须要一个复制的对象,一个名词,那你输入 yj,成功复制了当前行和下一行,这里 j 的身份,是一个名词。就像你用 y12G 就是复制当前行到第 12 行,这个 12G 或者 gg(到文件首行) 或者 G(到文件末尾), 或者 $(到行尾),这些都是一样的性质,表示一个位置。 按照这种理解,vim 中的名词也非常多,包含了各种各样的粒度,单个字符的(h,l), 一个单词(w,b,W,B),一句话((,)),一段话({,}),一个文件(gg,G)。还有其他的比如 % 是匹配的各种括号,nG 是指定行, 是指定标记位置等等,f 是当前行匹配字符,等等等等, 只有你想不到,没有做不到。 另外一种我作为名词看待的,是寄存器,在 normal 模式下输入 :reg 就可以看到自己 当前的寄存器状况,默认拷贝的时候是会把东西存到寄存器 “” 和 “0 (对,寄存器都是双引号标识的), 当然也可以指定,比如 “ay 就会把内容复制到 “a 寄存器里面。 另外还有很多默认的寄存器 “:, “/, “+(这个在 mac 下面会有点问题) 等等。 vim 的寄存器还可以储存操作记录,也就是所谓的宏,不过比较复杂就后面再讲吧
    3. 量词,顾名思义,表示数量,通常跟动词和名词配合,比如想要复制 10 次相同内容就用 10p, 向上移动 5 行就用 5k。
  3. 命令 命令是在 normal 模式下可以使用的一些指令,用 : 来开始输入指令(或者说进入命令模式), 输入完成后用回车,确认执行,比如新手使用 vim 最急需的一个 :q 命令,就是退出命令。 当然 vim 的命令也很多,常用的比如保存 :w(保存),:vs(垂直分屏), :sp(水平分屏), :s(正则替换) 等。

熟悉了常用的一些动词名词,就可以像使用普通的文本编辑器一样使用 vim 了。但仅仅是这样, 还算不上一个合格的 IDE,最多就是一个不用鼠标的优势而已。

环境配置

想要舒服地写码,当然少不了配置一下环境了,调整到一个适合自己的状态,效率才高嘛。 在 vim 里面,环境配置可以直接在 normal 模式下使用 :set 指令做到。 但每次都要配置一下也太反人类了,开发者肯定没有这么傻。vim 的配置文件是 vimrc, 具体的位和文件名可能会因为系统有一点点差别,但总的来说可以理解为把平时要写的 vim 配置命令一行一条的写在一起, 然后 vim 在启动的时候会先把这些命令跑一遍,跟 bashrc 之类的东西差不多一个意思。

个人认为必不可少的一些配置包括编码设置,行号,语法高亮,tab 键设置,自动缩进, 高亮搜索,折叠设置,比如下面这些就是我 vimrc 中的部分基本配置

    "设置编码
    set encoding=utf-8
    "显示行号
    set nu
    "语法高亮
    syntax on
    "不做备份
    set nobackup
    "tab长度
    set tabstop=4
    set sw=4
    "space替代tab
    set expandtab
    "自动缩进
    set ai 
    "显示命令
    set showcmd
    "高亮显示搜索
    set hlsearch
    "80列提示
    set cc=80
    "按缩进折叠
    set fdm=indent

当然如果用来写码的话,这依然还是不够的。IDE 的重要功能之一就是减少我们的输入量, 也就是传说中的自动补全,联想输入之类的,vim 虽然也有(ctr-n)来自动补全, 但是是要求先输入过一遍,所以就要请出强大的 map 命令了。map 其实就是配置文件中的宏, 可以把特定的输入转为其他的输入。

比如作为一个前端,我的 vimrc 里面有这样的配置

    let mapleader = ";"
    inoremap <leader>html <html><CR><head></head><CR><body><body><CR></html><esc>ki
    inoremap <leader>link <link rel="stylesheet" type="text/css" href=""><esc>hi

let mapleader 这一句是设定 map 的启动键,当然这并不是必须的,直接

    inoremap ;link <link rel="stylesheet" type="text/css" href=""><esc>hi

也是可以的,当然写成变量更好一些啦。 下面一句 map 的意思就是当输入 ;html 的时候,转换成后面的输入,其中 还切回了普通模式, **k** 移动到 body 标签里面,再用 **i** 切回 insert 模式。

第二句其实也差不多,是针对 link 标签的一个 map。而 map 命令中还加了两个参数,就是 inoremap 中的 i 和 nore,i 表示这个 map 只在 insert 模式生效(当然可以猜到 v 和 n 代表啥了,其实还有 c 是命令行模式),nore 表示不进行递归。

递归 map 是什么意思呢,就是说如果我为了输入引号方便,把 “ map 到 ““,递归的话, 新输入的两个双引号就又会变成 4 个,4 个变成 8 个,就没完没了了。vim 默认递归 map, 所以如果可能出问题的 map 就不要递归了。

当然 map 的用处远远不止这些,比如说我使用了一个 nerdtree 的 vim 插件打开插件很麻烦那可以加一个命令的 map

    noremap to :NERDTreeToggle<CR>

如果我觉得平时默认复制东西在 “0 寄存器容易被 dd 操作覆盖掉,那我可以加个复制用的 map

    vnoremap <leader>y "ay

文件类型判断也可以

    noremap <F5> <ESC>:set filetype=html<ENTER>
    noremap <F6> <ESC>:set filetype=css<ENTER>
    noremap <F7> <ESC>:set filetype=javascript<ENTER>

总之就是写码的时候有任何要手打的不爽的东西就全丢进去就好了。

使用技巧

我最喜欢 vim 的一点就是它足够简单,没错,虽然很难用好,但是其实它很简单。 vim 提供的功能都非常基础,并不提供什么特别厉害的定制的功能,甚至连特定语言的 map 都要自己做,写个 for 循环并不会打完 fo 就出来个下来列表问你是要 for in 还是 for i++ 。但这些东西,只要你需要,通过他提供的能力,都能够做到。

要用好 vim 确实须要很长的学习时间,单纯地背指令表并不能体验到用它编码的乐趣。 用vim,我感觉乐趣是为自己量身定做一个编辑器。须要把一个单词变成一个 HTML 标签, 没问题,短短一行 map 就可以做到,写 css 伪类元素的时候老是忘记加 content 和 dispaly, 想方便地规范老代码的缩进,都可以做到,可能须要花一点点心思,但完成得越多, 就会感觉自己越离不开它。

所以最重要的技巧,就是要够懒,懒得做任何重复的输入工作,找到方法,实现它。

文件以外

做文本编辑的时候,特别是写码,有很多功能需求是来自文本以外的。对此, vim 也提供了很多好用的功能。

最常用的一个是切换到命令行,把 vim 放到后台运行。在 vim 中使用 :sh 命令就可以了, 会切到一个 bash 环境,想要退出的时候 exit 一下就又回了。

还有一个系统剪贴板的功能,就是之前提到过的 “+ 寄存器,这个寄存器就是指向的系统剪贴板。 不过在 mac 上还要费点功夫,装 xcode 再装 macvim 之类的,才能用上。

还有引入其他文件的 :r 命令,改变当前文件名的 :f 命令 等等

当这些都不能满足你的跨文件需求的时候,可能腰考虑使用一下 vim 的插件了,比如上面提到过的 NERDTree 插件,其实就是在你的 vim 侧边加一个目录树。但 vim 的插件又是另一个大坑了。

在我自己看来,vim 的稍微复杂一些的操作,都是宏。之前说过 vim 是一个高度定制化, 越用越爽的编辑器,而这些定制操作,就都是宏,复杂的操作都是能通过基本操作组合而成的, 而在 vim 里面非常方便就可以做到,比如上面提到的 map。

当然提到 vim 的宏的时候,大家所指的比较多的应该是指 q 的 recoding 功能。在 normal 模式下 q 接你想要记录的寄存器名称,比如 qa 就会进入 recoding 状态,这个状态下会记录你的所有操作, 直到你再按一下 q 结束录制,然后你用 :reg 打开你的寄存器列表就可以看到自己被记录下来的操作了。 使用的时候也很简单,比如 qa 录制的,那用 @a 就可以执行了。所以单从功能上来说, 这是一个几乎万能的指令了,毕竟所有操作都能做对吧。

举个简单点的例子,比如我们有一个很长的 <li> 列表,类似这样

    <li class="a"></li>
    <li class="bc"></li>
    <li class="defaw"></li>
    <li></li>

我们想在每一个标签的内部加一个数字,还是要递增的,大概要的效果是这样

    <li class="a">2</li>
    <li class="bc">3</li>
    <li class="defaw">4</li>
    <li>5</li>

这种并不是特别常用的功能,可能不适合放在 vimrc 里面,那我们可以写这么一个宏到寄存器 “a 里面

   0f>lvt<yj0f>p(ctr-a)0
   // 0 是跳到行首,f> 是移动到第一个 > 符号的位置,v 进入选择模式,t< 是移动到
   // 下一个 < 的前一位,也就是选中标签中间的内容,y 是复制,j 移动到下一行,0
   // 移动到行首,接着 f> 跳到 > 处,p 粘贴,(ctr-a) 不需要这么输入的,直接在键盘
   // 按 control + a 是能记录下来的,这是把数字 +1 的组合键,然后跳回行首

然后在第一行里面填 2,或者任何你想要的起始数字,然后输入 3@a (也就是把 a 里面的操作重复 3 遍),就搞定了。复杂的功 能都是简单功能的组合, 只要习惯了这点,没什么是做不到的。

标记

如果说宏是 vim 的自定义动词的话,那标记就是 vim 的自定义名词。标记是在 normal 模式下使用 m 指令接寄存器名称实现的,比如在一个位置使用 ma 就会在当前光标所在位置打上标记。 想要跳转到标记所在行就须要 ‘a,如果须要准确的定位到标记的位置,就使用 `a。 标记平时写码的时候非常方便,在文件中须要编辑的地方打上标记,就可以在多个地点随意切换了。

但标记最强大的还不止这样,还记得刚刚讲过的宏么,宏加上标记来使用简直是如虎添翼, 再奇葩的定位要求,也可以轻松做到。

:q

vim 的魅力远远不止上面所说的这些,感兴趣的朋友还是须要自己去体会一下。 虽然有这么一个说法,用 vim 的程序员节省下来的时间都被用来安利别人用 vim 了,而且通常没什么效果。 但还是要坚持安利啊,毕竟这就是 vim 的副作用。