08
约 4690 字大约 16 分钟
2025-01-27
Vim 有一个非常强大的复制和粘贴系统,早于你在其他编辑器中使用的操作系统剪贴板。幸运的是,LazyVim 配置设置了 Neovim 的剪贴板系统,使其可以自动与操作系统的剪贴板一起工作。
事实上,你已经知道如何将文本剪切到系统剪贴板:只需删除它。
没错。任何时候你使用 d 或 c 动词,删除的文本会自动剪切到剪贴板。这通常非常方便,但有时也有些恼人,所以我将在本章后面向你展示一种避免删除文本保存到剪贴板的方法。
8.1. 粘贴文本 粘贴(在 Vim 中通常称为“放置”)文本使用的是我在第 1 章简要提到的 p 命令。在普通模式下,单个命令 p 将把系统剪贴板中的内容放置在当前光标位置。这通常是你最近删除的文本,但也可以是你从浏览器中复制的 URL 或从电子邮件中复制的文本或任何其他系统剪贴板对象。
插入文本的位置可能有些令人惊讶,但通常它会做你想要的操作。通常,如果你删除了一些单词或一个不是整行的字符串,它会立即在当前光标位置之后插入。然而,如果你使用了一个作用于整行的命令,例如 dd 或 cc,文本将被放置在下一行。这在移动行时节省了一些按键,这是代码编辑中的常见任务。
p 命令可以与计数一起使用,所以在你想粘贴剪贴板中连续 5 份内容的情况下,你可以使用 5p。
当你使用 p 粘贴时,光标会保持在原位,文本将在光标之后插入。如果你想在当前光标位置之前粘贴文本,使用大写的 P,其中的切换动作被解释为“在相反方向执行 p”。与 p 一样,文本将直接插入到光标位置之前,除非它是行级别的编辑,例如 dd,这种情况下它会被放置在上一行。
如果你已经在插入模式并需要粘贴一些内容并继续输入,你可以使用 Control-r 命令,然后按 + 键。r 可能很难记住,但它代表“寄存器”。我们稍后会详细讨论寄存器到底是什么。
8.2. 复制文本 复制文本需要一个新的动词:y。它的行为类似于 d 和 c,只不过它不会修改缓冲区;它只会复制 y 后面的动作或文本对象定义的文本。
你可能会问:“为什么是 y?”它代表“yank”,这是 Vim 中的“复制”。我不知道为什么 vi 把它叫做“yank”,但我猜它是一个反向首字母缩略词。原作者可能注意到键盘上 y 还是空闲的,于是决定找一个与之匹配的词。在那个时候,剪贴板或复制/粘贴的概念还没有标准化,所以他们可以使用任何对他们有用的术语。
y 命令适用于你已经知道的所有动作和文本对象。它尤其在 r 和 R 远程查找命令中非常有用。如果你需要从编辑器的其他地方(甚至是不同的窗口)复制文本到当前光标位置,yR
p 是在不添加不必要的跳转到历史记录的最快方式。 yy 和 Y 命令会复制整行,从光标到行尾,类似于删除和更改文本时的对应命令。
LazyVim 会短暂突出显示你复制的文本,以便指示你的动作命令是否复制了正确的文本。
8.3. 先选择文本 到目前为止,你的 Vim 编辑体验还没有涉及选择文本的概念。这是不是很奇怪?我们已经到第 8 章了!在普通的文字处理器和像 VS Code 的文本编辑器中,你必须先选择文本,才能执行删除、复制、剪切或更改等操作。考虑到在那些编辑器中选择文本有多尴尬(你必须使用鼠标或一些组合键,带有额外的修饰键来进行更大的移动),能完成任何工作真是令人惊叹!
在 Vim 中,你通常先执行动词,然后跟随一个文本动作或对象来隐式选择文本,这通常是最有效的操作方式,但在某些情况下,先选择文本再操作更方便。
这就是视觉模式的用武之地。视觉模式是 Vim 的一种主要模式,类似于普通模式和插入模式。技术上讲,视觉模式有三种子模式。我们将从“视觉字符模式”开始,然后深入探讨另外两种模式。
你可能会认为总是先选择文本以便看到你所操作的内容是有道理的。两个新兴的编辑器 Kakoune 和 Helix 正在试验这种模式。它们非常酷,但我发现“先选择文本”模式有点令人失望。编辑器无法确定任何给定的动作是为了移动选择还是扩展选择,因此你需要按下额外的键告诉它进行扩展。那时,它与在 Neovim 中按 v 进入视觉模式没有什么不同。使用 Helix 几个月后,我发现它实际上平均需要更多的按键,因此我又换回了 Neovim。
要进入视觉字符模式,从普通模式使用 v 命令。然后使用你熟悉的普通模式中的大多数动作移动光标。我说“大多数”是因为视觉模式的键映射独立于普通模式的键映射,插件有时会忽略为两种模式设置它们。然而,LazyVim 对键映射的处理非常好,所以你很少会感到惊讶。
你也可以通过点击并拖动鼠标进入视觉模式。 一旦你在视觉模式中选择了文本,你可以使用通常用来删除、更改或复制选择内容的动词。不同的是,它们会立即应用于选择,而不需要动作。你甚至可以使用单个字符动词,如 x(与 d 相同)或 r 将所有字符替换为同一个字符。完成动词之后,编辑器会自动切换回普通模式。你也可以使用 Escape 或另一个 v 在不执行任何操作的情况下退出视觉模式。
你可以临时退出视觉模式,而不会完全丢失选择。从普通模式,使用 gv(“转到最后一次视觉选择”)命令返回到选择。这很有用,如果你即将执行一个视觉操作,意识到你需要查找一些东西,进行编辑,或从文件的其他地方复制一些内容,然后返回到选择。
使用 o(代表“另一端”)命令将光标移动到块的另一端。如果你选择了一些单词,发现忘记了块另一端的一个单词,这很有用。你不能从视觉模式进入插入模式,所以 o 命令被重新用于这个目的。
8.3.1. 行模式视觉模式 v 命令适用于细粒度选择,但如果你知道选择将从行边界开始和结束,你可以使用(大写)V 进入行模式视觉模式。此时,无论你将光标移动到哪里,光标所在的整行都会被选择。
行模式视觉模式的主要区别在于,当你应用动词操作剪贴板(包括 d、c 和 y)时,行将在行模式下被剪切或复制。稍后你放置它们时,它们将显示在下一行或上一行,而不是立即在光标之后。
8.3.2. 块模式视觉模式 块模式视觉模式是 Vim 的一个独特功能。它允许你选择和操作一块垂直但不水平连续的文本。例如,在以下截图中,我选择了几行中的多个字符:
要进入块模式视觉模式,使用 Control-v 而不是 v 或 V。
在这样的纯文本中,块模式视觉模式看起来并不是很有用,但如果你需要剪切和粘贴 csv 文件或 markdown 表格中的列数据,它非常方便。我不常用它来执行这类操作,但在需要时,我知道没有其他方法可以有效地执行我需要的操作。
如果你使用 Control-V$,你将获得块模式视觉模式的一个轻微变体,其中块扩展到块中每行的末尾。如果你需要块扩展到最长的行,而不是光标当前所在的行,这很方便。 块模式视觉模式还可以用作多光标的(差劲)模仿。如果你在选择一个视觉块后使用 I 或 A 命令,然后输入一些文本并按 Escape,输入的文本将被复制到视觉块的开头或结尾。这个功能的一个常见操作是为 Markdown 有序列表的开头添加 * 字符或需要框架的块注释。
8.4. 寄存器 寄存器是一种存储文本字符串以供以后访问的方法(类似于汇编语言中的定义)。在这方面,它们与剪贴板没有区别。实际上,Vim 中的系统剪贴板是一个寄存器,LazyVim 将其设置为默认寄存器。
但 Vim 还有几十个其他寄存器。这意味着你可以有多个自定义剪贴板,每个剪贴板包含完全不同的文本序列。这在你重构某些内容并需要在多个调用点粘贴几段不同的代码时非常有用。
有几种不同类型的寄存器,但我会先介绍命名寄存器的概念。有二十多个命名寄存器,每个字母一个。
要从普通模式访问寄存器,使用 " 字符(即 Shift-)后跟你要访问的寄存器名称。然后发出你想对那个寄存器执行的动词和动作。
所以,如果我想删除三个单词并将它们存储在 a 寄存器中,而不是系统剪贴板中,我会使用命令 "ad3w。"a 选择寄存器,然后 d3w 作为正常命令删除三个单词。如果我以后想在其他地方粘贴相同的文本,我会使用 "ap 而不是仅仅 p,这样文本将从 a 寄存器粘贴,而不是默认寄存器。
"ad 总是用你选择的文本动作或对象替换 a 寄存器的内容。然而,你也可以使用寄存器名称的大写字母将多个删除命令构建到寄存器中。所以 "Ad 会将你删除的文本追加到现有的 a 寄存器中。
当我从一个函数复制几行代码到另一个函数,但源函数中有一个条件或其他不需要的内容时,我发现这很有用。使用 "ay 复制条件之前的文本,使用 "Ay 追加条件之后的文本,然后用 "ap 一次性粘贴整个内容。
我可以使用例如 "byS 将完全不同的文本复制到 b 寄存器。现在我可以随时使用 "ap 和 "bp 从 a 或 b 寄存器粘贴。
如果你忘记了将文本放在哪个寄存器中,只需按 " 并等待弹出一个菜单,显示所有寄存器的内容。如果那个菜单太难导航,你可以使用 s" 命令打开一个选择对话框,允许你搜索所有寄存器。只需输入你知道在寄存器中的几个字符,使用通常的选择命令导航列表,然后按 Enter 在最后光标位置粘贴该文本。
要从插入模式或命令模式显示相同的菜单,使用 Ctrl-r 而不是 "。
如果你在 s" 选择对话框中,你会注意到除了命名的字母寄存器之外还有许多其他寄存器。我将接下来讨论这些寄存器。
8.4.1. 剪贴板寄存器 在 LazyVim 中,默认情况下,命名为 * 和 + 的寄存器总是与默认(未命名)寄存器相同,并表示系统剪贴板的内容。
要理解为什么,需要一些历史背景:vi 有寄存器,然后操作系统对剪贴板的想法感到兴奋,vi 用户想要将内容复制到系统剪贴板。默认(非 Lazy)Vim 配置意味着如果你想将文本复制到系统剪贴板,你必须总是先键入 "+ 然后 y。在现代工作流程中,你经常将内容复制到浏览器、AI 聊天客户端和电子邮件中,这三个额外的按键(Shift、' 和 +)会变得非常单调。
此外,有些操作系统(通常是基于 Unix)实际上有两个操作系统剪贴板,一个是为你选择的文本隐式使用的,另一个是为你明确使用 Control-c 复制的文本。这些文本将存储在 "* 寄存器中,操作系统允许你使用(通常)中键单击将其粘贴到其他地方。
我建议坚持使用 LazyVim 的同步剪贴板配置,但如果你已经从旧的 Vim 使用方式中形成了肌肉记忆,或者你厌倦了删除的文本随意覆盖你的系统剪贴板,你可以禁用这个集成,使三个寄存器按上述方式工作,而不是链接在一起。为此,使用 space f c 打开 options.lua 配置文件并添加以下行:
Lua vim.opt.clipboard = "" 说到剪贴板内容随机被覆盖,如果你事先知道你不想让特定的删除或更改操作覆盖剪贴板内容,请使用“黑洞”寄存器,"_(下划线)。所以键入 "_d 删除文本而不将其存储在任何寄存器中,包括系统剪贴板。
如果你想将一个寄存器的内容复制到另一个寄存器,可以使用 ex 命令 :let @a = @b,其中 a 和 b 是你想要复制到和从的寄存器名称。这种操作最常见的用途是将系统剪贴板的内容(可能来自不同的程序)复制到一个命名寄存器中,这样在下次发出动词时不会丢失。例如,:let @b = @+ 将系统剪贴板复制到 b 寄存器。
8.4.2. 最后一次复制或插入的文本 每次你在没有指定目标寄存器的情况下发出 y 命令时,文本总是存储在 "0 寄存器以及默认寄存器中。无论你进行多少次删除或更改操作,它都会一直保留在 "0 中,直到下次复制操作。
所以,如果你复制了文本 abc 然后删除了文本 def,p 命令会粘贴文本 def,但你仍然可以使用 "0p 粘贴 abc。
你也可以使用 ".(句点)寄存器粘贴最近插入的文本副本。所以如果你在文档中的某处键入命令 ifoo,然后移动到文档中的其他地方并键入 ".p,它将在新的光标位置插入单词“foo”。". 是一个你可能偶尔想要复制到命名寄存器的寄存器,如果你插入了想要重复使用的文本。使用前面讨论的 :let @c = @. 命令来执行此操作。
8.4.3. 删除(编号)寄存器 编号寄存器应该非常有用,但我发现它们相当令人困惑。寄存器 "1 到 "9 总是按升序包含你最近更改或删除的文本。所以在删除操作之后,无论在 "1 中的内容移动到 "2,"2 移动到 "3,依此类推,"9 中的内容被丢弃。
我永远记不住我最近删除的顺序,所以我通常必须使用 " 菜单查看编号寄存器的内容。很方便的是,我最近删除的文本被存储并且我可以通过这种方式找到它。然而,我通常使用 yanky.nvim 插件(在本章后面讨论),所以编号寄存器对我来说不是那么有用。
还有一个“小删除寄存器”,可以通过 "- 访问。每当你删除任何文本时,它都会存储在编号寄存器中,但如果文本小于一行,它也会存储在这个减号寄存器中。我对这个功能几乎没有用,因为我的大多数更改都小于一行。这意味着它在从编号寄存器中删除之前会被清除。
8.4.4. 当前文件的名称 你当前正在编辑的文件名存储在 "% 寄存器中。它总是相对于编辑器的当前工作目录(通常是你启动 Neovim 时所在的文件夹)。我唯一想访问这个寄存器的时候是将文件名复制到系统剪贴板,使用 :let @+ = @% 这样我可以将其粘贴到 GUI 应用程序或终端中。
8.5. 记录到寄存器 记得我在第 6 章中告诉你的录制命令:qq 录制和 Q 回放录制?事实证明,我在那里有点过于简化了。
录制的命令实际上存储在命名寄存器中。在那一章中,我随意选择了 q 寄存器,当我说使用 qq 开始录制时。但你也可以同样轻松地将其存储在 a 寄存器中,使用 qa 或 f 寄存器中,使用 qf。
qQ 命令“追加到录制”操作类似于大写的 "A 用于追加到寄存器。在这种情况下,Q 仍然是一个随意的名称。你可以使用 qA 或 qZ 追加录制到不同的命名寄存器。
当你在代码库的不同位置需要进行几种不同的重复更改时,多个录制集非常方便。
Q 命令回放录制总是回放最近录制的命令,不管是哪个寄存器。如果你想从不同的寄存器回放,使用 @ 命令后跟寄存器名称。所以,如果你使用 qa 录制,你可以使用 @a 回放。作为快捷方式,@@ 总是回