Vim 快速编辑

2021-01-20 ⏳5.1分钟(2.0千字)

Vim在启动之后会进入所谓的Normal模式。在Normal模式下,用户无法直接输入文本。我想,这可能是Vim最令人困惑的地方了。

关于这个Normal模式,我在Vim 快速移动一文中也有介绍:

对于一段代码,跟它的整个生命周期相比,编码的时间可能连百分之一都不到。我们大部分时间都是阅读或者修改已经写好的代码。Vim的作者有鉴于这种事实,引入了所谓的 Normal 模式。在 Normal 模式下,你不能输入内容,但是可以高效地移动和修改。

因为输入内容用的相对较少,所以Vim要求在输入前需要先切换到Insert模式。一旦进入Insert模式,大家就可以正常输入内容。完成输入后需要按Esc返回Normal模式。

那么如何进入Insert模式呢?

最常用的就是按字母i,显然是单词insert的第一个字母。 大家可以试一下,按i切入Insert模式后,光标会停在当前字母的前面。也就是说,如果你的光标停在了insert的s上,那按i之后,光标会停在n和s之间,新输入的内容也会实插入到s之前。

如果Vim只支持i,我们就会遇到一些麻烦。比如,我们想给一行的结尾加一个分号。我们只能是先按$跳到最后一个字母,再按i切换到Insert模式,再按把光标移动到最后一个字母的后面,再输入;,再按Esc回到Normal模式。

这么小的改动居然得用方向键。能忍吗。为此,Vim还提供了字母a,大家可以理解成append,也就是追加的意思。同样是切换到Insert模式,a会让光标停在当前字母后面。所以,前面的改动可以依次键入a;Esc就可以了。

所以,在一行的开头插入内容需按^i,在结尾插入内容需按aEscNormalVimiIa,然后输入内容,最后按Esc返回Normal模式。因为这两种操作比较常用,Vim给他们单独分配了快捷键,^i对应I,a对应A。怎么样,是不是很有规律。

除了简单的插入内容,我们还经常需要新起一行输入内内容。到现在你可以依次按A,跳到行尾并切换到插入模式,再按回车新起一行,然后输入你想要的内容。按两次有点麻烦,Vim为大家提供了o来实现这个效果。这个o好像没有对应的单词。大家不妨理解成open new line😜

o是在当前行下面新起一行。细心的朋友肯定会问如何在当前行之上新起一行呢?如果我们只会o,那我们只能先按一下k稳到上一行,然后再按o。可如果光标就在第一行呢?我们只能先按I再按回车了。同样因为常用,Vim为I(表示回车)提单独分配了O(大写的o,你懂得)这个键位。

好了,光切换到插入模式,我们已经学了四种办法,如下表

快捷键 等效键位 功能
i 之前追加
a i→ 之后追加
I ^i 行首插入
A $a 行尾插入
o I 下起一行
O A 上起一行

每当完成修改后,一定要返回Normal模式。有同学喜欢「一键到底」,这不是一个好习惯。为什么呢?一个原因是在Insert模式下,我们无发像Normal那样灵活移动;另一个原因是不能灵活的撤销之前的改动。

跟其他编织器一样,Vim也提供undo和redo功能(分别对应u和r),但是这些操作是以「原子改动」为单位的。什么是「原子改动」呢?简单来说,从Normal模式切换到Insert模式、输入改动内容、再返回Normal模式,这一整个过程叫做一次「原子改动」。如果大家一直在Insert模式下输入内容,不管输入多少,只要回到Normal模式再按一下u,就会全部撤销。非常不灵活。

所以,Vim鼓励大家多想少改,一次只改一点,步步为营。每改完一点,就按一次Esc返回Normal模式。大家可以跟 git 类比一下,把每次切到Insert模式、改动、返回Normal模式当成一次 commit。这样撤销的时候会非常灵活。

在极少数情况下,我在希望在Insert模式快速移动光标。简单的做法是使用方向键,但更地道的方法是使用ctrl+o。在Insert模式下按ctrl+o vim会临时切换到Normal式,然后可以按wf^$等实现快速移动。Vim命令执行完之后会自动切换到Insert模式。也算是一种小优化。

以上都是说先切换到Insert模式再进行修改的。切来切去多麻烦。有没有更快的方法呢?有!

首先你可以直接在Normal模式执行删除操作。删除的主要命令是d(可以理解为delete)。单纯一个d不能完成删除操作,必须还要再提供一个范围。这个范围主要使用移动操作表示(如果你对移动不大熟悉可以先看我的素人Vim之快速移动一文),而且是从光标所在字符开始。比如,如果想向右删除一个字符,可以按dl,如果想删三个字符可以按d3l,给大家整理一个表格

命令 范围 功能
dl l 向右删除一个字符
d3l 3l 向右删除三个字符
de e 向右删除到单词的结尾
df; f; 向右删除到第一个分号
d$ $ 向右删除到当前行的结尾
d^ ^ 向左删除到当前行的开头
dG G 从当前行删除到最后一行
dgg gg 从当前行删除到第一行

命令+范围,大家要记住这个范式。Vim还有很多其他功能也遵守这样的范式。

为了进一步简化删除操作,Vim还提供了一组简化的快捷键

功能 快捷键
dl x
dh X
d$ D
删除整行 dd

到这里,删除已经学的差不多了。记住d+范围可以解决大部分问题。

我们为什么要删除呢?除了极少数纯删除操作外,我们一般都是为了修改而执行删除操作的。删是为了更好的改。先删后改,是不有点麻烦呢?能不能删改一体化?能!

简单来说就是把刚才的d换成c(可以理解成change)就可以了。也是命令+范围,c会删除相同范围的内容并切换到Insert模式,删除之后可以立即输入新内容。因为c跟d是完全对应的,我就不在这里重复罗列了。但c还有一些简化操作,不得不提

功能 快捷键 功能
cl s 修改当前字符
cc S 修改整行

还有一种情况是我们只想改一个数字,比如把0改成1我们可以按s1Esc,需要按一次Esc返回Normal模式。Vim对这种情况也提供了一个快捷键叫r(可以理解成replace),原来的s1Esc可以简化成r1。

最后说一下复制跟粘贴。

复制使用y(yank),同样遵循命令+范围的范式,跟dc完全对应(复制整行用yy),不作赘述。

粘贴则使用p(paste),比较简单。与之对应的是P,也是粘贴,但会粘贴到光标的左侧和上方,大家可以试一下。

最后给大家整一个表格,到此,基本把编辑相关的操作讲完了。

命令 助记词 整行操作
d delete dd
c change cc
y yank yy
p page
u undo
ctrl+r redo