Vim 简介
最近有兴趣折腾了一下 Vim,不免对这个上古神器的强大感到震撼。这篇文章简要记录下基本操作,只能说是冰山一角。
认识 Vim 基本模式
Vim 有三种基本模式:普通模式、插入模式和命令行模式。普通模式是打开 Vim 之后的默认模式,也是进行模式转换的桥梁。在普通模式下,可以进行常规的浏览、替换、删除、粘贴等命令;在普通模式下按 i
即可切换到插入模式,这种模式与常规的文本编辑器相同,可以自由添加、修改、删除文本内容;按 Esc
可以从插入模式切换回普通模式,再次键入 :
会切换到命令行模式。这种模式支持各种命令的使用,在我看来也是 Vim 的强大之处所在。
Vim 命令对大小写敏感,因此在使用 Vim 时务必确保大写锁定处于关闭状态。通常情况下,大家会建议将键盘的 Caps Lock
键重新映射为 Esc
,对于 Manjaro
系统,可以通过系统设置中的键盘的高级选项进行修改,非常方便。
当本文提及大写的命令如 A
时,实际上是指 Shift
+ a
的组合键。
退出 Vim
网络上流传有这么一个段子:“问:如何产生随机字符串?答:让新手退出 Vim。”
退出 Vim 首先要确保处于普通模式,如果不是很确定,可以多按几次 Esc
。然后输入 :quit
即可退出 Vim。这个命令可以这样来看,首先键入的 :
使 Vim 进入到命令行模式,而 quit
就是执行的命令。
可以猜想,如果只是写入文档而不退出(一般编辑器下的"保存"按钮),应当执行 :write
;如果想看某个命令的帮助文档,应该会涉及 :help
命令,实际上的确如此。在普通模式下输入 :help quit
就可以看到相应的帮助文档,其中 :q[uit]
的中括号部分表示可以省略。因此 :quit
可以简写为 :q
。
如果文档进行了改动,直接运行 :q
会抛出警告,这时需要写入后退出,或者不保存退出。汇总这些相关的命令,如下:
:q
— 退出 Vim (关闭文档);:w
— 写入(保存)文档但不退出;:wq
— 保存并关闭当前文档;:q!
— 忽略所有更改并关闭当前文档。
移动光标
移动光标需要在普通模式下进行,用于浏览文档或为编辑做准备。
基本移动
普通模式下,光标移动可以通过以下按键实现:
h
— 左移一个字符;j
— 下移一行;k
— 上已一行;l
— 右移一个字符;0
— 移动到本行开头;$
— 移动到本行末尾;gg
— 移动到文档首行开头;G
— 移动到文档最后一行开头。
Vim 的命令在执行前可以指定重复次数,例如下移15行可以使用 15j
来完成。特别地,在 G
之前指定数字可以跳转到指定行。为了在 Vim 中显示行号,可以在普通模式下执行 :set number
。
wrap
设置被启用(默认启用)时,过长的文字会在屏幕内自动换行,但这并不是真正的换行。Vim 默认的 j
命令是对实际行进行操作,遇到这种情况时难免会出乎我们的期望,因此可以使用 gj
和 gk
进行屏幕行的跳转。为了默认使用屏幕行的跳转,我们会将 j
与 gj
的功能互换,可以在配置文件 .vimrc
中添加以下内容:
nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j
基于单词的移动
比普通模式更快地,Vim 可以基于单词进行移动,主要命令如下:
w
— 移动到下一个单词开头(word);e
— 移动到当前单词结尾(end);b
— 向前移动到单词开头(backward);ge
— 向前移动到单词结尾;W
— 移动到下一个字符串开头;E
— 移动到下一个字符串结尾;B
— 向前移动到字符串开头;gE
— 向前移动到字符串结尾。
对比可以发现,小写命令针对单词,而大写命令针对的是字符串,两者的区别在于对特殊字符的处理方式:单词认为 '
是单词之间的分割,而字符串则严格以空格作为分割。例如 let's go
这个短语会被 Vim 解读成 4 个单词或 2个字符串。
基于查找的移动
Vim 可以使用 f
对行内的单个字符进行查找,结合 ;
向后继续查找,当跳转过头时使用 ,
向前查找。同样,也可以使用 /
对字符串进行查找,使用 n
或 N
分别向后、向前继续查找。
我们用下面的一个例子来对比各种移动光标的方法:
The quick brown fox jumps over the lazy dog.
假设现在光标处于这段话的开头,现在希望将光标跳转到 over
的开头,有这么几种做法:
wwwww
— 所见即所得,只要不觉得麻烦就可以达到效果;5w
— 与上一个命令等价,虽然按键少了,但需要一定的计算;fo;;
— 首先fo
指定了对字符o
的查找,光标跳转到brown
的中间,然后继续查找两次达成目标;fvh
— 对v
进行查找,然后向左移动一个字符;/over<Enter>
— 对字符串over
进行查找并跳转(其中<Enter>
表示按回车键)。
对比这几种方法,第一种最直观,但是当句子特别长时跳转很慢;第二种虽然看起来简单,但数数的功夫容易得不偿失,也很少使用;第三种方法应该说是最常用也是最容易被人接受的,当句子较长时能够获得比较快的跳转速度;第四种是第三种的高阶用法,查找不常出现的字符以提高跳转速度;最后一种单次使用时输入较为复杂,通常结合替换使用。
其他跳转方法
%
可以用于配对符号之间的跳转,例如在 (
处按 %
可以跳转到相应的 )
,这个功能特别适合对代码进行检查。
m
和 `
构成标记(mark)——跳转对。例如, ma
将当前位置标记为 a
,普通模式下使用 `a
就可以跳转到刚刚标记的位置。需要说明的是,标记只能是单个字符,并且区分大小写;单个文档可以存在多个标记。
选定范围
类比于英语中动词可以分为及物动词和不及物动词,Vim 中有些命令可以直接执行,例如 j
就是下移一行;也有些命令需要指定作用范围,这里我们以删除命令 d
(delete)为例进行说明。
为了执行删除操作,按下 d
之后还应当给定删除范围。前面介绍跳转的时候说过 w
是从当前位置到下一个单词开头,确切地表明了范围。因此 dw
就是从当前位置删除到下一个单词的开头。类似地, d2w
就是从当前位置删除到后面第三个单词的开头(删除两个单词)、 d$
从当前位置删除到本行结尾。
如果光标处于某个想删除的单词的中间,而使用 dw
删除时,光标之前部分不会被删除。为此,Vim 还提供了 a
和 i
这两个修饰词(可以理解为 around 和 inside)来进一步修饰范围, a
可以将选区扩展到光标两边,包括限定词; i
将选区扩展到两边,但是不包括限定词。例如, daw
将删除整个单词以及后面的一个空格,不管光标处于单词的哪个位置。使用 a
或 i
进行修饰时,除了使用 w
设定范围外,还可以使用配对符号进行扩展。例如,当光标处于某一对括号内部任意位置,使用 da)
可以删除整个括号内的内容,并连同括号一起删除。
特别地,Vim 的一些命令重复按两次表示对当前行进行操作,如 dd
为删除当前行。
除了使用命令划定范围外,与普通编辑器相同,可以先选定范围再执行操作。按 v
进入 Vim 的可视模式(visual mode),然后再利用前面介绍的各种跳转命令,可以发现光标遍历的位置高亮,这时候再执行 d
即可删除高亮选区。特别地, V
直接对整行进行选取。
组合键 Ctrl
+ v
在 Vim 中不再是粘贴,在普通模式下使用该组合键可以进入 Vim 的块选取模式,对应通常编辑器的列编辑功能,能够同时对多行进行相同的修改。
编辑文档
在普通模式进行少量修改
在普通模式下可以对文档进行部分删除和简单的替换工作,常用有:
x
— 删除当前字符;r
— 使用另一个字符替换当前字符(replace),例如re
是将光标下的字符替换为e
;d
— 删除操作,在上面已经讨论,不再赘述;.
— 重复上一次操作。
从插入模式编辑文档
编辑文档最主要的方式是进入编辑模式,像普通编辑器一样编辑文档。Vim 提供以下基本的方式进入插入模式:
i
— 从当前光标之前进入插入模式(insert);I
— 从当前行(或可选模式下的选区)之前进入插入模式;a
— 从当前光标之后进入插入模式(append);A
— 从当前行(或可选模式下的选区)之后进入插入模式;o
— 向下插入新行并进入插入模式;O
— 向上插入新行并进入插入模式;s
— 删除当前字符并进入插入模式;S
— 删除当前行并进入插入模式。
Vim 还可以使用 c
命令进行更改(change),例如 caw
表示删除当前的单词并进入插入模式。这种方法在修改程序时非常有效,例如修改字符串变量 sys_name = "Windows 10"
时,将光标移至双引号内部任意位置,使用 ci"
即可删除字符串变量的值并进行修改。
搜索与替换
前面介绍了 /
进行搜索,而替换则需要结合命令行 :s
实现(substitute),例如将 old
替换为 new
,有以下几种不同的方式:
:s/old/new
— 从光标之后对当前行进行搜索,将第一个old
替换为new
;:s/old/new/g
— 从光标之后对当前行进行搜索,将所有old
替换为new
;:%s/old/new/g
— 对全文进行搜索,将所有old
替换为new
;:%s/old/new/gc
— 对全文进行搜索,将所有old
替换为new
,并在执行前进行确认。
进行简单的计算
在普通模式使用组合键 Ctrl
+ a
可以对光标下的数字加一,而使用 Ctrl
+ x
则进行减一操作。当光标不处于数字上,Vim 会自动从光标之后搜索当前行的第一个数字,执行相应操作。同样地,这种方式可以指定次数运行,例如 10<Ctrl>a
是将当前数字加 10 。
应当注意,Ctrl
+ a
和 Ctrl
+ x
都是针对单词的操作,因此小数将以小数点分界,作为两个整数来对待。
如果数字以 0 开头,Vim 默认将其解释为八进制,因此对 007 进行 <Ctrl>a
命令后,得到的是 010 而不是 008。为了只使用十进制,可以在配置文件 .vimrc
中添加 set nrformats=
进行设置。
在插入模式中,可以使用 Ctrl
+ r
+ =
触发计算功能,输入计算表达式后,按 Enter
即可将计算结果输出到当前位置。例如 <Ctrl>r=1+1<Enter>
将会输入 2 。
寄存器简介
Vim 提供多种寄存器,用于储存复制或删除的内容。寄存器可以通过 "
进行引用,有以下几种常见寄存器:
[a-z]
— 有名寄存器,用于各种自定义内容;"
— 无名寄存器,不指定寄存器时默认使用该寄存器;0
— 复制专用寄存器(书里这么写的,我也不知道为啥要起这个名字,实际上复制不一定非要用这个);+
— 系统剪切板,用于系统级的复制粘贴;_
— 黑洞寄存器,所有在这里的东西都丢失,彻彻底底地删除;=
— 表达式寄存器,可以用于计算;%
— 当前文件名。
还有一些其他寄存器,这里不多赘述。
复制与粘贴
以复制粘贴这个经典的例子来介绍寄存器的使用。
复制可以使用 y
来实现(yank),而粘贴使用 p
完成(paste)。复制时需要指定范围,可以用限定词来指定,如 yaw
为复制当前单词;也可以用可视模式进行选择。p
默认将内容粘贴在光标之后(Vim 会自动判断是否换行),也可用 P
粘贴在之前,或使用可视模式进行选择替换。特别地, yy
将对当前行进行复制。
上面这种直接复制的方法没有指定寄存器,默认使用无名寄存器 "
,即 yy
命令等价于 ""yy
(第一个 "
表示使用寄存器,第二个 "
表示使用无名寄存器)。使用无名寄存器容易导致复制的内容丢失,我们看下面一个例子:
1) 使用 `yy` 命令复制本行;
2) 使用 `dd` 命令删除本行;
3)尝试在此使用 `p` 命令将第一行内容粘贴到本行之下。
如果按照上面的提示做,会发现最后粘贴的是第二行的内容。这是因为第二行使用 dd
删除的内容并没有真正的删除,而是以剪切的方式储存在寄存器内,而前面的 yy
和这里的 dd
均没有显式指定寄存器,所有都使用的是无名寄存器。无名寄存器第一次复制的内容被第二次删除的内容覆盖,因此粘贴时读取的无名寄存器是第二行内容。
为此,可以改变操作顺序,如先删除,后复制。也可以使用显式指定其他寄存器以避免内容覆盖,例如可以使用 "0yy
和 "0p
进行复制粘贴;或者可以将删除内容放置到其他寄存器如 "_dd
。
可以同时使用多个寄存器,例如复制帐号和密码时,可以使用 "ay
将帐号复制到寄存器 a
,而使用 “by
将密码复制到寄存器 b
。粘贴分别使用 "ap
和 "bp
即可。
上述的粘贴是在普通模式进行,为了在插入模式直接读取寄存器内容,可以使用 Ctrl
+ r
组合键,指定寄存器后即可将内容粘贴到正文。
忘记寄存器内容时,可以使用 :reg
命令进行查看。
宏的录制与回放
寄存器中还可以保存命令(宏),并在特定的时候执行(回放)。普通模式下按 q
并指定寄存器即可开启宏的录制,再次按 q
即停止录制。在使用寄存器内保存的宏时,使用 @
和寄存器名即可。
例如,如果要在 Markdown 中使用 Enter 这样的键盘样式,需要输入指令 <kbd>Enter</kbd>
,非常麻烦。因此可以录制一个宏,自动在单词两边分别添加 <kbd>
和</kbd>
。假如我将这个宏录制到寄存器 k
,具体步骤如下:
- 普通模式下将光标移动到需要添加键盘样式的单词处;
- 键入
qk
开始录制宏,并指定将宏录制到有名寄存器k
; - 使用
ciw
删除当前单词并保留两边的空格,进入插入模式,输入前缀<kbd>
; - 在插入模式中使用
<Ctrl>r"
读取无名寄存器的内容,即上一步删除的单词,然后输入后缀</kbd>
; - 按
Esc
返回普通模式,按q
停止录制。
如此做,只需要在另一个需要添加键盘样式的单词处使用 @k
即可调用宏。使用 @
回放宏的时候,也可以像普通命令一样指定次数。一个经典的例子就是在各行前面添加行号,需要结合一定的脚本,使用以下方式实现:
- 在命令行运行
:let i=1
设定行号初值,并将光标移动到需要编号的那一行; - 使用
qq
开始录制宏,并将其录制到寄存器q
; - 使用
I\<Ctrl>r=i\<Enter>)
将变量i
的值插入到行的最前面,并添加)
进行修饰; - 按
Esc
回到普通模式,运行:let i+=1`` 进行递归,然后按
j` 换行; - 按
q
停止宏录制,然后使用22@q>
将宏重复 22 次(2
与@
在同一个键位,用起来方便而已)。
当行数不足以满足指定的重复次数时,Vim 会自动停止回放宏,因此无需担心指定次数不准确的问题。
参考文献
- Neil D. Vim实用技巧. 杨源, 车文隆, 译. 人民邮电出版社, 2014.