02-到底什么是模式编辑?
第 2 章 到底什么是模式编辑?
正如启动面板上的字母所提示的,LazyVim 是以键盘为中心的。尽可能多的操作都可以在不将手移动到鼠标的情况下完成。当然,仍然可以使用鼠标。你可以点击编辑器中的任何位置,与弹出的按钮和对话框交互,使用滚轮或触摸板手势滚动,以及通过拖动边框来调整编辑器窗格的大小。但是,你也可以使用键盘完成所有这些事情,而且通常更有效率。
更重要的是,大多数操作最多只需按住两个键,通常只需一个键。你将极少需要将手扭曲成痛苦(且危险)的姿势来按 Control + Shift + Alt + <某个键>
。
Vim 是如何做到这一点的?通过模式编辑。
2.1. 模式编辑简介
LazyVim 中的“模式”仅仅意味着不同的按键根据当前激活的模式有不同的含义。例如,当你启动编辑器时,你处于“启动面板模式”,该模式下每个按键最常见的解释都直接列在启动面板上。这种在特定模式下快捷键的可发现性是 LazyVim 中一个常见的主题,也是对 Neovim 本身不透明的默认行为的一大改进。
译者注:
- 模式 (Mode): 编辑器的一种状态,决定了键盘输入的行为。
- 快捷键 (Keybinding): 将特定的按键或组合键映射到某个编辑器命令或动作。
- 可发现性 (Discoverability): 指用户能够容易地找到和学习软件功能(如此处的快捷键)的程度。
为了理解我的意思,请按空格键进入“Space 模式”。Space 模式是 LazyVim 的一个概念;它在原生的 Neovim 安装中不存在(尽管如果你想要 Space 模式但不想用 LazyVim,可以安装各种插件来重现效果)。
译者注: Space 模式通常将空格键作为一个“前缀键”或“引导键”(Leader Key),请记住Leader Key这个单词,按下空格后再按其他键可以触发各种命令,通常会弹出一个菜单显示可用的后续按键及其功能,极大地提高了命令的可发现性。
进入 Space 模式会在屏幕底部弹出一个菜单。它看起来会像这样(我的菜单包含一些自定义设置,所以你的可能不完全相同):
图 4. Space 模式
这是一个大菜单。现在需要关注的重点是 f
键,我们将用它来理解模式编辑。
如果你处于启动面板模式并按下 f
键,你将使用一个我们稍后会讨论的选择器插件打开 Find file
(查找文件)对话框。然而,既然你现在处于 Space 模式,如果你按下 f
键,它将转而打开 file/find
(文件/查找)的 Space 模式子菜单。
这就是模式编辑的核心含义:特定按键的行为取决于当前模式。正如 Space 模式菜单底部的提示行所示,你可以按 Escape
键退出 Space 模式并返回到启动面板。去试试吧。
现在你回到了启动面板模式,可以按 n
键创建一个新的空缓冲区。
译者注: 缓冲区 (Buffer) 在 Vim/Neovim 中指内存里文件内容的副本。你编辑的是缓冲区,然后可以将缓冲区的修改保存回文件。一个缓冲区不一定对应一个磁盘上的文件(例如用
n
创建的新缓冲区)。缓冲区不保存就不会写入文件,电脑突然关机,数据就消失了。
密切注意该缓冲区左下角,你会看到 INSERT
字样:
图 5. 插入模式 (Insert Mode)
还记得我说 Space 模式是 LazyVim 的概念吗?插入模式则是一个源自 Vi 的概念,其后继者 Vim、Neovim,以及现在的 LazyVim 都继承了它。在插入模式下,绝大多数按键的作用都和你期望在任何编辑器中的一样:它们插入文本。所以你可以像平常一样进行盲打输入。
在插入模式下,你可以使用 Control
和 Alt
键访问一些键盘快捷键。例如,你可以按 Control-r
进入“寄存器”迷你模式,它会弹出一个你可以从中粘贴内容的“寄存器”列表。我们将在第 8 章详细介绍寄存器。现在,只需知道在插入模式下,按 Control-r
后跟加号键(即 Shift-=
)将粘贴剪贴板中的文本就足够了。
译者注: 寄存器 (Register) 是 Vim 中用于存储文本块(例如复制或删除的文本)的命名空间。系统剪贴板通常映射到
+
寄存器。别急,后面会学到的。
然而,你会更频繁地切换到普通模式 (Normal Mode) 来执行任何非文本录入的操作,包括粘贴文本。普通模式是另一个主要的 Vim 模式,自 Vi 时代就已存在。
要从插入模式进入普通模式,请按 Escape
键。光标将从竖线变为方块,左下角的指示器将变为蓝色的 NORMAL
:
图 6. 普通模式 (Normal Mode)
在普通模式下,按各种键盘字符不会像在插入模式下那样插入文本。例如,按 p
不会将字面上的 p
字符插入文档,而是会从系统剪贴板粘贴文本。
Vim 和 Neovim 的可发现性不是很好,但它们通常是容易记住的。尽可能地,执行某个操作的键盘快捷键会以对该操作有意义的字母开头。你可能会认为 p
代表“paste”(粘贴),但实际上这个概念比剪贴板这个抽象概念存在的时间更长。如果你觉得更容易理解,可以把它想成“粘贴”,但在 Vim 的术语中,它实际上代表“put”(放置),我们在本书中将一直使用这个词。
译者注: Vim 中
p
(put after cursor) 和P
(put before cursor) 命令用于粘贴(放置)之前被y
(yank/复制) 或d
(delete/剪切) 的文本,这些文本默认存放在匿名寄存器"
中。如果想粘贴系统剪贴板内容,通常需要指定+
寄存器,例如"+p
或"+P
。不过 LazyVim 可能有配置使得p
/P
默认就操作于系统剪贴板。
作为对比,在插入模式下弹出寄存器列表的 Control-r
键,在普通模式下不会弹出寄存器列表。相反,Control-r
的意思是“redo”(重做,即撤销一次撤销操作)。要从普通模式进入寄存器迷你模式,你需要按 "
(双引号,即 <Shift>-单引号
)键。
如果这听起来令人困惑,别担心。你的大脑和肌肉记忆会比你预期的更快适应,并且你会一直明白普通模式下的行为与插入模式下是不同的。
我几乎从不在插入模式下使用非文本录入命令。我发现切换回普通模式,然后从普通模式执行命令更容易。这样做通常不会需要更多的按键次数,而且我不必同时按住多个键。
退出插入模式并返回普通模式的通用键是 Escape
。这就引出了一个重点:你会经常使用这个键,但是将手从主行(home row)移动到左上角的 Escape 键再移回来有点低效。
译者注: 主行 (Home Row) 是标准打字指法中,手指静止时放置的一行键(在 QWERTY 键盘上是 ASDF JKL;)。
对于这种情况,你可以选择以下几种常见的解决方案:
- 如果你有一个可定制的键盘,你可以把
Escape
键放在一个更容易触及的位置。我就是这么做的。我用的是 Kinesis Advantage 360,我重新映射了按键,让 escape 位于这个公认怪异键盘的“拇指键区”。它和Enter
、Space
、Backspace
一样容易按到,这些也是我非常频繁使用的键。 - 你的操作系统可能也能够重新映射按键。很多模式编辑用户用
Escape
键替换了无用的Capslock
键。(对于非模式编辑范式,将Capslock
重新映射为常用的Control
键可能更舒服,尤其是在笔记本键盘上)。 - Neovim 本身也能够重新映射按键。我们稍后会讨论如何在 LazyVim 中做到这一点。一种常见的模式是将一系列在插入文本时不太可能连续输入的罕见按键映射到 escape 键。所以你可以设置将插入模式下的
jk
、jj
或;;
之类的组合映射为切换到普通模式。我试过这个,但不太喜欢,因为它在你按下第一个字符时会引入一个时间延迟,Neovim 需要等待看你是否要输入一个命令还是继续插入文本,但你可能会喜欢。 Control-C
组合键也可以用来退出插入模式,无需重新映射。我不喜欢这个,因为它是两个按键,而且在我的 Dvorak 键盘上,Control-C
比在 Qwerty 键盘上更难按到(Qwerty 键盘的 C 在Control
键附近的底行)。
译者注: Dvorak 是一种键盘布局,旨在通过将最常用的字母放在主行上来提高打字速度和舒适度,与 QWERTY 布局不同。我用的就是这个,然后键盘支持驱动改键,大写锁定改成Esc。大写锁定键纯粹的祖宗之法不可变,厂商都不想换位置。
也可以暂时不用去更改它;先开始习惯使用 Escape
键现在的位置,看看你是否觉得它烦人。
一旦你进入了普通模式,你显然希望在某个时候回到插入模式来输入文本!有几种不同的方法可以做到这一点。以下是几种最常用的:
i
键总是在当前光标位置之前插入文本。这意味着你可以(非常笨拙地)通过重复按 i <Escape> i <Escape>
来向左移动光标。当你按 i
时,你在当前位置之前插入文本,然后 Escape
会让你在那个新的“之前”的位置退出插入模式。
通常,你想在当前光标位置之后进入插入模式。要做到这一点,请改用 a
键(助记符:i 代表 Insert Before(之前插入),a 代表 Append(追加),尽管我通常把它想成 After(之后))。
你会发现,在导航文档时,你需要经常在这两者之间切换,因为我们稍后将介绍的各种导航命令通常会把你放在需要插入位置的正前方或正后方。所以记住它们两者很重要。
另外两个非常常见的操作是在当前行的最开始或最末尾插入。你可以使用导航命令移动到行首或行尾,然后使用 i
和 a
,但使用 I
和 A
命令更简单(区别在于它们是大写的,所以你需要配合 Shift
键使用它们)。
2.1.1. 关于快捷键助记符的说明
像这样相关的快捷键被分配给同一个键的小写和大写版本是非常普遍的。你通常会发现小写版本表示“做某事”,而大写版本则根据情况表示“做同样的事情,但范围更大”或“做相反的事情”。在这种情况下,i
和 a
表示“在光标前或后插入一个字符”,而 I
和 A
则表示“在光标前或后插入,但范围更大(即在行首或行尾)”。
为了说明“做相反的事情”的情况,考虑 o
和(Shift 后的)O
键,这是另外两种进入插入模式的方式。
o
键用于在当前行下方的新行上进入插入模式。对于“做相反的事情”的场景,Shift 后的 O
表示“在当前行上方创建新行并在其上进入插入模式”。
助记符“Open a new line above/below”(在上方/下方打开新行)可以帮助你记住这个不那么容易记住的
o
命令。
最后一个需要两个按键的有用命令是 gi
。即单击并释放 g
后跟 i
。它的意思是“Go to the last place you entered Insert mode, and enter Insert mode again”(转到你上次进入插入模式的地方,并再次进入插入模式)。在这种情况下,g
键实际上是切换到一个我称之为“转到”(Go To) 模式的新迷你模式,你可以通过在普通模式下按 g
键并等待窗口底部弹出菜单来查看“转到”模式下可用的完整命令列表:
图 7. 转到 (Go To) 模式
我们稍后会介绍其中的大部分,但请注意 i
键在那里标记为“Move to the last insertion and INSERT”(移动到上次插入点并进入插入模式)。所以如果你忘记了如何去往上次的插入点,你可以进入“转到”模式并扫描菜单再次找到 i
。
反复尝试所有这些命令(a
, i
, o
, A
, I
, O
, 和 gi
),输入一些文本,然后按 Escape
返回普通模式。然后再试一次。使用鼠标在文本中移动光标(我们很快就会讲到键盘导航,我保证),然后再次尝试使用这些命令,看看它们在新的位置如何表现。
要非常熟练地在普通模式和插入模式之间切换。你可能认为你会把大部分时间花在插入模式,但事实是代码被编辑的频率远高于全新编写的频率,你会不断地在它们之间交替。
2.2. 可视模式 (Visual Mode)
LazyVim 从其祖先那里继承的另一个主要模式是“可视”模式。可视模式用于选择文本。通常,你可以进入可视模式,然后使用许多与普通模式下相同的导航键来移动光标。由于我们还没有介绍那些导航按键,我将把对可视模式的详细讨论推迟到第 8 章,届时我们将具备必要的基础。
译者注: 可视模式有几种子模式:
v
(Visual Mode): 字符级选择。V
(Visual Line Mode): 行级选择。Ctrl-v
(Visual Block Mode): 块级(矩形)选择。 选择文本后,可以执行各种操作,如d
(删除/剪切),y
(复制),c
(修改) 等。
2.3. 命令模式 (Command Mode)
命令模式与我们见过的其他模式不同,后者大多是子菜单或是编辑器级别的“主要”模式。你可以通过 :
(即 Shift-<分号>
)命令从普通模式进入命令模式。在 LazyVim 中,这会弹出一个小部件,你可以在其中输入所谓的“Ex 命令”。这个名字来源于 vi
的前身 ex
,它(除了作为 Vim 的一部分外)几十年来都未曾真正被使用过。
译者注: Ex 命令是 Vim 中以
:
开头的命令,用于执行各种文件操作、设置选项、搜索替换等。例如:w
(保存),:q
(退出),:e <文件>
(编辑文件),:set number
(显示行号) 等。
本质上,你可以向这个小部件输入各种各样的命令,并期望触发某些操作。实际上,它比其他任何东西都更类似于 Sublime Text 和 VS Code 的命令面板(Command Palette),尽管体验相当不同。
你已经在第 1 章学过一个 ex 命令了!还记得 <Escape><冒号>q!<Enter>
这个退出编辑器的命令吗?你现在知道 Escape
是为了从你所在的任何模式进入普通模式。冒号用于切换到命令模式,q
是 quit(退出)的缩写(如果你不觉得需要节省按键,可以输入完整的单词 quit
)。感叹号表示“不保存就退出”,而 Enter
则表示“提交 ex 命令”。
再举个例子,让我们看看 write
ex 命令。输入 :
后跟 write myfile.txt
,像这样:
图 8. Write (写入) 命令
按 Enter
确认并执行该命令,这将以输入的名称保存文件。
大多数命令可以缩短为其最短的唯一公共前缀。你通常输入 :w myfile.txt
而不是 :write myfile.txt
。最流行的命令甚至有特殊的组合命令,所以 :wq
会保存并退出,尽管你可能更喜欢 :x
,因为它甚至更短。
译者注:
:x
与:wq
类似,但它只在文件被修改过的情况下才写入并退出,否则仅退出。这可以避免不必要地更新文件的修改时间。
命令模式有点奇怪。它有点像插入模式,因为你可以向其中输入文本,并且一些在插入模式下有效的快捷键在命令模式下也有效(包括 Control-r
从寄存器粘贴)。但其他快捷键在命令模式下的工作方式不同。最重要的是 Tab
键,它会对命令进行某种“补全”。例如,输入 :q<Tab>
会弹出一个像这样的菜单:
图 9. 命令模式下的 Tab 补全
这个补全菜单的导航方式不直观。你可能需要收藏这一节或做些笔记,直到你习惯它为止!
首先,如果你想选择菜单中的不同条目,你肯定会认为可以使用箭头键,对吧?嗯,你可以试试,欸,怎么窗口消失了,其实你需要使用 Left
和 Right
来将光标 Up
和 Down
移动。我知道!WTF,对吧?
译者注: 这通常是因为这个补全菜单最初是设计为水平显示的,后来 UI 库将其改为垂直显示,但底层的按键映射没有相应更新,祖宗之法不可以变。
这主要是因为这个菜单在 LazyVim 中看起来与原始 Vim 不同,但按键没有被重新映射。因此,我建议使用 Tab
和 Shift-Tab
来选择菜单中的不同条目。这更容易记住,对肌肉记忆也更友好:按一次 Tab 显示菜单,再按 Tab 在菜单中的其他条目间循环。
其次,关于确认这些菜单条目中的某一个,存在一些细微差别。在上面的例子中,你可以直接按 Enter
来确认选择并执行它。然而,通常情况下,你希望确认选择然后继续编辑命令。一个极好的例子是 :e
或 :edit
命令。
这个命令用于打开文件系统上的文件,但你必须输入文件的完整路径。例如,如果你有以下目录结构:
列表 5. 嵌套目录结构
.
└── foo
├── bar
└── baz
│ └── fizz.txt
…而你打开了 Neovim,你必须输入以下内容才能打开 fizz.txt
文件:
列表 6. Edit (编辑) 文件命令
:e foo/baz/fizz.txt
如果你需要访问深度嵌套的目录,这需要大量的输入。幸运的是,你可以为此使用 Tab 补全。你可以输入 :e f<tab>b<tab><tab><tab>
来选择 foo/baz
:
图 10. 带有补全菜单的 Edit 命令
如果你现在按 Enter
,它将打开 baz
文件夹,而不是仅仅确认选择,这不是你想要的。而如果你再按 Tab
,它将在菜单中继续循环。
相反,你有几个选择。Down
箭头键将(不直观地)“进入”所选目录,你可以通过 Tab 浏览其中的文件。或者,使用 Control-y
(y 代表“yes”)组合键。这将确认 baz
的选择并关闭补全菜单,让你留在命令模式。现在你可以再次按 Tab 来补全命令的 fizz.txt
部分。
确实可以重新映射这些键,使其更像其他软件,老实说,我认为这是 LazyVim 默认应该做的一件事。不过,我还没有找到我喜欢的组合,所以我还是坚持使用默认的快捷键。
你可能不会花很多时间在命令模式。例如,在 LazyVim 中有更容易的方法来打开文件,以及退出编辑器。而且如果你需要对命令历史做更复杂的操作,有一个特殊的窗口可以让你使用插入和普通模式来编辑命令,我们稍后会介绍。
现在,记住 <Tab>
和 Control-y
,你就能在需要时导航命令菜单了。
顺便说一句,最重要的命令是 :help
。Vim 是在人们还不能方便地访问互联网之前创建的,所以它有一个传统,即将所有文档与编辑器一起发布。因此,举例来说,如果你不记得 put
(放置) 文本的键盘快捷键,可以试试 :help put
。或者,如果你想知道 Control-R
键盘快捷键是做什么的,试试 :help CTRL-R
。当然,Vim 的帮助文档也已被你最喜欢的搜索引擎和 AI 聊天机器人索引了,所以如果你愿意,完全可以用全新的方式去问它们。
2.4. 总结
在本章中,我们熟悉了模式编辑的概念以及最重要的 LazyVim 模式。随着本书的深入,还会出现其他迷你模式,但熟悉普通模式、插入模式和命令模式(以及如何在它们之间切换)将对你的 LazyVim 之旅大有裨益。
在下一章中,我们将学习一大堆在文档内部移动光标的不同方法。