Vim 快速编辑

2021-01-20 ⏳5.3分钟(2.1千字) 🕸️

我在Vim 快速移动一文中相对系统地总结了 Vim 的移动操作。本文补上编辑部分。目的只有一个,那就是帮助初学者尽快熟悉 Vim,形成战斗力。

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

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

✏️Note

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

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

那么如何进入 Insert 模式呢?

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

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

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

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

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

o是在当前行下面新起一行。细心的朋友肯定会问如何在当前行之上新起一行呢?如果我们只会o,那我们只能先按一下k移到上一行,然后再按o。可如果光标就在第一行呢?我们只能先按I再按回车了。同样因为常用,Vim 为I<cr>(表示回车) 单独分配了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不能完成删除操作,必须还要再提供一个范围。这个范围主要使用移动操作表示,而且是从光标所在字符开始。比如,如果想向右删除一个字符,可以按 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 模式,删除之后可以立即输入新内容。因为cd是完全对应的,我就不在这里重复罗列了。但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

有志于掌握 Vim 的读者应该依照我的移动和编辑编辑两篇文章,多加练习,假以时日,必成大器🐶