03-四处移动 - Some-soda

03-四处移动

第 3 章 四处移动

软件开发者花在编辑代码上的时间远比写代码的时间多。我们总是在调试、添加功能和重构。

我最常做的事情就是在代码库的某个特定行添加一个 print/printf/Println/console.log。

如果你之前使用一些更常见的文字处理或文本编辑器,在 Vim 的模式范式中,导航代码是最不同的事情。即使你习惯了 Vim,LazyVim 默认自带的一些插件也提供了与旧的 Vim 标准不同的代码导航方法。

在 VS Code 中,从代码中的一个点到另一个点,最快的方法通常是使用鼠标。对于小范围移动,箭头键效果很好,并且可以与 ControlAltCmd/Win 结合使用,以更大的增量移动,例如按单词、段落移动,或移动到行首或行尾。还有许多其他的键盘快捷键可以使移动更容易,并且语言服务器支持(Language Server support)可以轻松实现语义代码导航,例如“转到定义”和“转到符号”。

Vim 也支持鼠标导航,但一旦你熟悉了导航键位映射,你可能会减少使用它的频率。LazyVim 拥有与 VS Code 相同的语言服务器协议(LSP)功能的快捷键,而且它们通常更容易访问。Vim 的最大不同之处在于,当你的编辑器处于普通模式时,整个键盘的导航命令你都可以随意使用。

译者注: 语言服务器协议 (Language Server Protocol, LSP) 是一种由微软创建的协议,用于编辑器或 IDE 与提供编程语言特性的“语言服务器”之间进行通信。语言服务器可以提供自动完成、转到定义、查找引用、诊断(错误/警告)等功能。

简单来说,LSP服务负责帮你进行代码分析和补全,C LSP服务专门分析C语言文本,Go LSP服务分析Go语言文本。理论上来说,你可以给TXT装个LSP服务写代码?只要你告诉编辑器:“这个 TXT 文件用某种语言模式打开”,LSP 服务器就可以工作了。

3.1. 寻找文本 (Seek Mode)

LazyVim 自带一个名为 flash.nvim 的插件,它由 LazyVim 的维护者创建,并且与之集成得非常好。

这个插件提供了一种代码导航模式,这种模式在各种 vim 插件中已经存在很多年了,并且在历史上颇具争议。许多资深的 Vim 用户认为它打破了 Vim 的范式。我不会详细说明原因,但我承认这在旧的范式迭代中是成立的,但在现代版本(如 flash.nvim)中则不那么成立了。

译者注: Seek/Flash 模式通过让你输入目标位置的字符,然后为屏幕上所有匹配的字符或字符对分配一个临时的、短的标签(如一个字母),你只需输入该标签即可跳转。这与传统的 Vim 移动方式(如基于方向 hjkl、单词 w/b/e、行内查找 f/t)不同,后者通常是逐步接近目标。争议源于它更像是一种“直接选择”而非“导航”,祖宗之法不可变。

如果你能看到想要导航到的代码位置,flash.nvim 几乎总是将光标移动到那里的最快方法。诚然,它至少需要三次按键,但这三次按键不需要心算或逐步“靠近”目标直到到达那里,心算和逐步逼近是其他Vim 导航技术(以及非模式编辑)中效率较低的原因。

要调用 flash,在普通模式下按 s 键。我对 s 的助记符是“seek”(寻找),尽管我也听说过它被称为“sneak”(潜行)或“search”(搜索)模式。在 LazyVim 中,搜索是另一种行为(它不关心文本当前是否可见),而“潜行”听起来有点太不直观了,所以我用“Seek”。

按下 s 后首先要注意的是,文本会淡化为统一的颜色,状态栏中会有一个小闪电符号,表示 Flash 模式已激活:

seek active dark

图 11. Flash 模式激活

既然你知道光标想去哪里,你的眼睛可能正盯着它,并且你确切地知道那个位置是什么字符。所以在进入 Seek 模式后,只需键入你想要跳转到的字符。

例如,在下面的截图中,我想修复本节标题中的拼写错误,将 Test 改为 Text

seek s dark

图 12. Seek (寻找) s 字符

我按了 ss(第一个 s 启动 Seek 模式,第二个 s 是要寻找的字符),截图中每一个 s(包括大写)都变成了蓝色。状态栏中的 flash 图标旁边有一个 s 字符,表明我寻找的是 s

此外,所有离光标最近的 s 字符右侧都有一个绿色的标签。如果我想跳转到那些 s 字符中的任何一个,我只需键入那个标签,然后砰的一声,我就到那里了。

然而,我想按的字符太远了,没有唯一的标签,因为我的文本中有很多 s 字符。没关系!我只需要键入目标 s 字符右侧的字符,也就是 t。现在我的屏幕看起来像这样:

seek st dark

图 13. Seek (寻找) st 字符

现在,文件中所有的 st 实例都被高亮为蓝色,并且由于 st 没有 s 那么多,所有这些实例旁边都有一个标签。我想移动到的文本被标记为 p,所以我按下 p,我的光标就移动到了我想更改的 s 字符上。现在我可以输入 rxs 替换为 x(我们将在第 6 章讨论编辑代码,但现在你已经尝到了一点甜头)。

译者注: r 是普通模式下的替换命令 (replace),rx 的意思是将光标下的单个字符替换为 x

如果你在分割窗口中打开了多个文件(我们将在第 9 章讨论),Seek 模式可以用来将光标移动到屏幕上的任何地方,而不仅仅是当前活动的分割窗口中。

然而,Seek 模式确实有缺点,至少在 flash.nvim 实现它的方式上是这样。如果你在标记的匹配项定位到该位置之前,你就用完了可供搜索的文本,那么有些字符你无法直接移动到的。对我来说,这种情况最常发生在我想要编辑行尾的时候。如果我想编辑一个以 n 作为最后一个字符的行而输入 sn,但有许多 n 字符比我想移动到的那个更靠近我的光标,flash 可能不会标记我想移动到的 n,并且它不接受回车符作为“下一个字符”的输入。

因此,我不在行尾附近使用 seek。相反,我会 seek 到同一行中间的某个词,然后使用 A,你可能还记得,这会把我置于行尾的插入模式。或者,如果我不想进入插入模式,我会使用 $ 符号(Shift+4),这是普通模式下“将光标移动到当前行尾”的命令。

3.2. 滚动屏幕

Seek 模式只有在你想要跳转到的文本在屏幕上可见时才有效。你看不到的东西是无法标记的!通常,这意味着你想使用搜索或之后会讨论的更大范围或更具体的移动,但也是有一些快捷键可以用来滚动屏幕,以便在Seek之前看到你的目标。

这些快捷键按照 Vim 的标准来说有点不寻常,因为它们大多涉及了 control 键的使用。多么反模式啊!根据我的经验,这些快捷键实际上用得并不多。事实上,我为了写这一章,已经忘记了其中一些,不得不去查阅资料。

我最常用的滚动键绝对是 Control-dControl-u,助记符是 down(向下)和 up(向上)。它们将窗口滚动半个屏幕的文本量。光标相对于窗口保持在相同的位置,这意味着它相对于文档向上或向下移动了半个屏幕的文本量。

如果你需要移动得更远,可以使用 Control-fControl-b 快捷键(forward 向前和 backward 向后),它们滚动一整页的文本。我不喜欢这两个,因为我总是不太清楚光标最终会停在哪里,会让我迷失方向。但如果你需要快速滚动某些内容到视图中以便使用 Seek 模式,它们可能很方便。与 Control-dControl-u 不同,Control-fControl-b 可以前缀一个计数,所以如果你需要向前滚动 5 页,可以输入 5<Control-f>

要将窗口滚动单行,请使用 Control-yControl-e。我不知道为什么选择了这些快捷键。没有助记符。我很容易忘记它们,所以我从不使用它们。这些快捷键接受计数前缀,所以如果你能记住它们,它们对于微调文本位置很有用。这些快捷键的主要优点是它们不会移动光标,除非光标会滚出屏幕,所以如果你正在处理某一行并且需要更多可见性但不想移动光标,你可以使用 Control-yControl-e 来实现。

我不使用这些键的原因(除了缺少像样的助记符)是我更喜欢使用 z 模式进行相对光标定位。

3.2.1. Z 模式

z 菜单模式是光标定位、代码折叠和随机命令的大杂烩。你可以通过在普通模式下按 z 键来查看列表:

z menu dark

图 14. z 菜单

如果这看起来像一个大菜单,那你了解了一部分,还有大量其他的 z 模式快捷键,它们太晦涩了,以至于 which-key 插件(弹出菜单的插件)都懒得在菜单中列出它们!我将在这里介绍三个最有用的与滚动相关的,我们稍后会讨论其他的。

译者注: which-key.nvim 是一个 Neovim 插件,当你按下像 Spacez 这样的前缀键后,它会弹出一个窗口显示所有可能的后续按键及其功能。

我专门使用的相对光标快捷键是 ztzbzz。它们分别将光标当前所在的行移动到屏幕的顶top、底bottom 或中middle(zz 代表 middle,没有更好的单字母可选)。移动到顶部或底部时,它会在光标上方或下方留下几行上下文。

译者注: zz 通常被记为将当前行置于屏幕中央 (center/middle),使其“Zzzzz”处于焦点。

还有其他的命令也会将光标移动到窗口的第一列,但我建议将之前的命令与 0 结合使用,例如 zt0zb0zz0,而不是去记那些快捷键。0 命令就是“转到行首”的意思。如果你的键盘有 home 键,你也可以使用 home,但在许多键盘上 0 更容易按到。

你可以在 Neovim 文档中通过输入 :help scrolling 找到其他滚动快捷键,但我刚才提到的那些应该已经满足你的需求了。

3.3. Vim 的第一法则

Vim 里有一条神圣的规则,我经常出于某些正当理由打破它。除非你像我一样是那种非常奇怪的个性化爱好者,否则你不应该那么频繁地打破它:

永远不要使用箭头键移动光标。

这条规则的背景是,在大多数键盘上,将手移动到箭头键大约需要十分之一秒,再移回主行(home row)又需要十分之一秒。我并不相信这十分之一秒累加起来能达到可观的时间量,即使考虑到我一生中输入的数百万个字符。(是的,数百万。我曾经计算过)。

但是我确实认为大多数键盘上的箭头键会对你双手的长期健康造成不良影响,老实说,你越习惯用于替代方向键的 Vim 快捷键,就越会越倾向于使用它们。

当你第一次看到 Vim 的箭头键绑定时,它们似乎相当不直观:hjkl。它们分别映射到方向:左、下、上、右。如果觉得 l 代表 而不是 很奇怪,或者你想知道为什么他们跳过了 i(因为这看起来像是一个字母序列),请看看你的键盘。

译者注:

  • h: 左 (left)
  • j: 下 (down)
  • k: 上 (up)
  • l: 右 (right) 这源于 ADM-3A 终端键盘布局,Bill Joy 开发 Vi 时使用的就是这种终端,其 hjkl 键上印有箭头。祖宗之法不可变,这个我是真不喜欢,我还是喜欢 h j k l 左上右下。

如果你是使用标准 Qwerty 键盘的英语使用者,字母 hjkl 位于你右手下方的主行上,这是整个键盘上最容易按到的四个键。

在 Neovim 中打开一个较大的文件(你可以使用 :e 路径/文件名),并尝试使用主行键移动光标左、右、上、下。在你这样做的时候,我会告诉你为什么我不使用它们,因为我三点不正常。

**译者注:**这快捷键我有三不用。

首先,我是左撇子,所以右手主行感觉稍微不那么方便。其次,我用了二十年的 Dvorak 布局。jkl 键不在我的主行上。第三,我使用 Kinesis Advantage 360 键盘,除了其他奇特的布局特性外,它将箭头键放在我手指可及的范围内,所以我不需要移动手去按它们。

命运弄人,这些怪异之处某种程度上相互抵消了。jk 键正好位于我惯用的左手下方的左、右箭头键的正上方。所以我用 Left Right, j k 进行导航。如果你没有我这么奇怪,你可能应该按照 Vim 设计的方式使用右手主行键。

**译者注:**Dvorak布局的键盘 j k 在左下角,然后他的Kinesis Advantage 360 键盘方向键在右下角,所以就这样。

Vim、Neovim 和 LazyVim 都非常擅长重用移动(motions),所以你会发现,随着本书的进展,hjkl 被用于许多不同的导航方式。花足够的时间去真正习惯它们。而且要认识到,如果你需要连续按这些键两次以上来移动光标,那你就是在浪费按键次数。

3.4. 计数 (Counting)

Vim 中绝大多数命令都可以前缀一个计数来重复该移动多次。计数通常是在你想要重复的命令之前输入一串数字。

所以,例如,要将光标向上移动 15 行,你需要进入普通模式并按下 15k。要将光标向右移动五个字符,使用 5l

这就是为什么 LazyVim 默认的行号如此奇怪的原因。考虑下面的截图:

relative lines dark

图 15. 相对行号

在此截图中,我的光标位于第 126 行,该行在左侧边栏(gutter)中高亮显示。它也显示在我窗口的右下角,虽然我在这张截图中把它裁剪掉了。但是在第 126 行的正上方,我们看到行号 1,它的正下方我们也看到行号 1。

译者注: 相对行号 (Relative Line Numbers) 显示当前行号的绝对值,而其他行显示它们相对于当前行的偏移量。这使得使用计数 (<数字>j<数字>k) 进行垂直跳转非常方便,因为你不需要心算距离,直接看行号即可。瞪眼法秒了,其实我感觉也没比心算快多少,我还是喜欢绝对行号。

假设我想将光标移动到 Scrolling the screen 标题。

这一行旁边的数字是 5,所以我不需要数行数或做任何心算来确定移动光标所需的计数。我只需键入 5j,我的光标就移动到了期望的行。

现在你知道它们的用途了,我建议保持相对行号开启,直到你习惯它们。如果你觉得它们分散注意力或者就是不用它们,你可以通过编辑你的 LazyVim 配置来更改为普通行号。打开文件 ~/.config/nvim/lua/config/options.lua,这个文件应该由 LazyVim 为你创建,但初始情况除了描述其用途的注释外没有任何内容。

你可以使用 Space 模式命令 <Space>fc 来快速查找 LazyVim 配置目录中的文件。这会弹出一个我们将在下一章详细讨论的文件选择器。输入 options 并按 <Enter> 打开该文件。

译者注: <Space>fc 通常映射到 “Find Config”(查找配置)命令。

要默认禁用相对文件号,请将此行添加到文件中并保存:

列表 7. 禁用相对行号

vim.opt.relativenumber = false

然后重新打开 Neovim,你应该会在左栏看到行号的绝对值。

就我个人而言,我觉得行号不是很有用,而且我不喜欢浪费宝贵的屏幕宽度来显示那些字符。正如已经成为一个反复出现的主题,我承认我有点奇怪!但如果你也想完全禁用行号,你需要在 options.lua 中添加第二行:

列表 8. 禁用所有行号

vim.opt.number = false
vim.opt.relativenumber = false

译者注: vim.opt 是 Neovim 中用来访问和设置 Vim 选项的 Lua API。number 控制是否显示绝对行号,relativenumber 控制是否显示相对行号。LazyVim 默认是 number = truerelativenumber = true,这会产生混合行号效果(当前行显示绝对行号,其他行显示相对行号)。我猜的。

3.5. 查找模式 (Find Mode)

如果你需要将光标移动到离当前位置相对较近的位置,你可能想使用 LazyVim 的查找模式,而不是我们之前描述的 Seek 模式。Neovim 中的默认查找模式相当有限,但是启用了 Seek 模式的 flash.nvim 插件极其好用。

译者注: 查找模式 (f, F, t, T) 是 Vim 内建的行内快速跳转方式。f{char} 跳转到当前行光标后的下一个 {char} 字符处。F{char} 跳转到当前行光标前的上一个 {char} 字符处。t{char} 跳转到当前行光标后的下一个 {char} 字符的前一个位置。T{char} 跳转到当前行光标前的上一个 {char} 字符的后一个位置。; 重复上一次查找,, 反向重复上一次查找。

要进入查找模式,请按 f 键。与 Seek 模式类似,屏幕的一部分会变暗,提示你应该输入另一个字符。在你这样做之后,该字符在光标之后的所有实例都将被高亮显示。例如,fs 将高亮显示当前光标位置之后的所有字母 s 的实例。

这就是 查找 模式和 Seek 模式的相似之处。光标会立即向前跳转到光标后的第一个匹配字符,而不是显示标签。你还会注意到光标之前的文本都没有变暗,并且光标之前行中的匹配字符也没有被高亮显示。

相反,我们需要使用计数来跳转到字符的后续实例。如果我想向前跳转到第三个高亮的 s,我输入 3f,我的光标就会移动到那里。然而,如果你想跳转到一个更靠后的 s,你可能不想逐个计算有多少个 s 键。幸运的是,在你使用计数后,LazyVim 会让你保持在查找模式,所以你可以猜测大约有多少个 s 字符,然后在你更接近目标后,用新的计数重复查找。如果你只想向前跳转一个 s 字符,你不需要输入计数,只需单独按 f 键,你就会向前移动。

译者注: 这段与标准 Vim 的 f 行为略有不同。标准 Vim 中,f{char} 直接跳到第一个匹配项。要跳到后续匹配项,需要使用 ; (重复上次查找) 或 , (反向重复上次查找)。输入 3fs 会被解释为其他命令。作者描述的应该是 flash.nvim 增强后的行为。标准 Vim 的 f 模式不是一个持续的状态,每次查找都是独立的。

更新: 查阅 flash.nvim 文档,它确实增强了 f/t,使其可以像 Seek 一样显示标签或通过重复按键跳转,但作者描述的“使用计数跳转 (3f)”和“保持在查找模式”可能依赖特定配置。标准 Vim 的方式是 fs 跳到第一个 s,然后按 ; 跳到下一个 s,再按 ; 跳到再下一个 s。

我英语不太好,大概是我没看懂,同时我也没测试出来作者说的东西,你可以自己尝试一下。正常情况下按下 f 然后输入要查找的字符,再按 f 或者 ; 就会跳转到下一个。

如果你数错了或猜错了,跳得太远了,别担心!你可以利用(Shift 后的)F 表示“向后查找”这一事实,并且它也可以被计数。所以如果你需要移动到第 15 个高亮的 s,完全可以猜测 18f,意识到跳过了三个,然后使用 3F 跳回前一个字符。

译者注: 同样,标准 Vim 中 F{char} 是向后查找第一个 {char}3F (标准 Vim) 通常不起作用。向后重复查找是用 ,。作者描述的 3F 跳转回 3 个 s 听起来像是自定义行为。标准操作会是:fs (跳到第一个s), ; (跳到第二个), …, ; (跳到第18个), , (跳回第17个), , (跳回第16个), , (跳回第15个)。

此外,如果你知道要查找的字符在文档中光标的后面或上方,你可以一开始就用 F 而不是 f 进入查找模式。这将立即开始向后查找操作,而不是向前。而且如果你一开始就知道想要向后或向前跳转给定字符的三个实例,你可以在首次进入查找模式时就使用计数。

还有一种查找模式的细微变体,我称之为“To”模式,尽管官方的 Vim 助记符实际上是“’Til”(直到)模式。你根据想要移动的方向使用 tT 进入该模式。

“To”模式的行为与查找模式完全相同,只是它跳转到目标字符的正前方

你可能认为 To 模式有点多余,因为你可以相当容易地使用查找模式,然后跟一个 h 将光标向左移动。但是当你要将其与编辑文本的操作结合起来时,“To”模式非常有用,我们稍后会讨论。举个例子,如果你使用命令 d2ts,它将删除光标和它遇到的第二个 s 之间的所有文本,但会保留那个 s。这比使用查找命令然后必须进入插入模式添加回 s 所需的 d2fsis<Escape> 要容易得多。

译者注: d 是删除操作符。Vim 的操作符 (d, c, y 等) 通常后跟一个移动命令 (motion),表示操作的范围。d2ts 的意思是:d (删除) 2t (到第二个 s 之前) 的 s (字符)。所以删除从当前位置开始,直到第二个 s 字符之前的所有内容。而 d2fs 会删除到第二个 s 字符包括s。因此 tf 在与操作符结合时,包含范围的细微差别非常重要,太细了。

3.6. 按词移动

ft 感觉太大,而带计数的 hjkl 光标移动感觉太小时,你最可能想使用按词移动命令。在其他编辑器和 IDE 中,你可能习惯于通过按住 ControlAltOption 并使用箭头键来实现此功能。

Neovim 更简单,你不需要将手移动到键盘的箭头键区域,也不需要同时按住多个键。

相反,你只需进入普通模式并按 w 键即可移动到下一个词的开头。如果你想移动到当前词的末尾,请使用 e 键。如果你已经在当前词的末尾,e 将会移动到下一个词的末尾。

译者注:

  • w (word): 向前移动到下一个单词的开头。
  • e (end of word): 向前移动到当前或下一个单词的末尾。

当你想要将其与计数结合时,这很有用:如果你需要移动到当前词之后第二个词的末尾,请按 3e。这与按三次 e 相同,会移动到当前词的末尾,然后是下一个词的末尾,最后到达你想要的目标词的末尾。如果你需要移动到当前词之后特定数量的词的开头,w 也可以前缀计数。

如果你想向后移动,请使用 b 键。这将把你移动到当前词的开头,或者如果你已经在词的开头,它将移动到前一个词的开头。和以前一样,使用计数来移动到更前面的词的开头。

译者注: b (backward): 向后移动到当前或上一个单词的开头。

令人惊讶的是,移动到前一个词的末尾需要更多的工作,因为你需要按两个键:g 后跟 e。这个的助记符是“go to end of previous word”(转到前一个词的末尾)。实际上,你会发现出于某种原因,你很少需要这个功能,老实说,我通常使用 beb 移动到前一个词的开头,然后 e 到达该词的末尾)来移动到前一个词的末尾。然而,如果你确实使用 ge,它也可以与计数结合。你需要输入类似 4ge 的东西,具体取决于计数。命令 g4e 不会做任何有用的事情。

译者注: ge 向后移动到上一个单词的末尾。将计数放在 g 之前,如 4ge,表示重复 ge 动作 4 次。

总的来说,你可能偶尔会听到 web 命令被称为“web”词组。它只是指“按词移动”。这些可能是你将使用的最常见的移动方式,比单个光标位置移动更常用,仅仅是因为大多数编辑操作往往涉及更改或删除一个词或一串词。

3.7. 按词移动,但范围更大 (Moving by WORDS, only BIGGER)

web 词组的“大写”形式也按词移动,但“词”的定义略有不同。具体来说,大写 W 会移动到下一个空白字符之后,而小写 w 则会使用其他形式的标点符号来分隔单词。考虑一个在许多语言中看起来像这样的对象方法调用:

列表 9. 示例方法调用

myObj.methodName('foo', 'bar', 'baz');

如果你的光标当前在该行的开头,按 w 会将你的光标移动到行上的句点,第二次按 w 会将你移动到 m,随后的 w 按键也会在括号和引号处停止。

另一方面,如果你的光标在行首,按 W 会一路将你移动到 "bar" 参数中的第一个引号处,因为那是第一个空白字符所在的位置。

译者注:

  • 小写 w, e, b 将单词定义为由字母、数字和下划线组成的序列,或者由非空白字符组成的序列(标点符号等)。
  • 大写 W, E, B 将 WORD 定义为仅由非空白字符组成的序列,以空白字符分隔。这通常在处理代码时更有用,因为它会跳过 .(' 等。

作为可视化,以下是使用 w 与使用 W 时在该行代码上的所有停靠点:

列表 10. wW 的行为

myObj.methodName('foo', 'bar', 'baz');
-----ww---------w-w--w--ww--w--ww--w-w----->  
------------------------W------W-----W-----> 

**译者注:**根据定义,第一个 W 应该停在 ‘bar’ 的引号处,第二个 W 停在 ‘baz’ 的引号处。我稍微调整了注释使其更清晰。

BEgE 的移动行为类似,按空白分隔的 WORD 在相应的方向上移动,而不是按标点符号分隔的单词。

在 Vim 和 LazyVim 的配置方式中,有一件有点烦人的事情是,没有办法在 CamelCaseWords(驼峰命名)或 snake_case_words(蛇形命名)的各个单词之间导航。如果你愿意,可以使用 fCt_ 之类的方法,但我稍后会告诉你如何设置 nvim-spider 插件,使导航这些常见的编程结构更简单。

译者注: 确实,标准 Vim 不直接支持在驼峰或蛇形命名的子词间跳转。需要插件或自定义映射来实现。

3.8. 行目标 (Line Targets)

很多时候,你需要移动到当前正在编辑的行的开头或结尾。如果你的目标是移动到那个位置并进入插入模式,通常可以使用 IA,但如果你需要移动到那里并保持在普通模式(例如,用于其他目的,如删除或更改单词),你可以使用 ^$0 命令。

如果你熟悉正则表达式,你可能知道 ^ 用于匹配文本开头或行首,而 $ 用于匹配行尾,所以使用这两个快捷键来匹配当前行的开头和结尾的助记符,希望比它们初看起来不那么难记。

译者注:

  • ^: 移动到当前行的第一个非空白字符。
  • $: 移动到当前行的最后一个字符(行尾)。
  • 0 (零): 移动到当前行的第一列(绝对行首),无论是否有前导空白。

然而,这两者之间缺乏某种对称性。$ (Shift-4) 命令仅仅表示“转到行尾”,即结尾换行符之前的最后一个字符,无论该字符是什么。^ 或脱字符 (Shift-6) 表示“转到此行文本的开头”。这里的“文本的”很重要:如果你的行开头有空白(例如缩进),^ 脱字符将不会转到第一列,而是会转到第一个非空白字符。

要移动到行的最开始,请使用 0 键。0唯一映射到命令的数字键,因为其他数字键都用于开始计数。但是用 0 开始计数没有意义,所以我们可以用它来表示“移动到第零列”。

还有一个命令可以转到行尾(不包括尾随空白),但我从未使用过它,可能是因为我通常配置了格式化程序来修剪尾随空白,所以这种情况不会出现。

两个字符的组合 g_ (g 下划线) 表示“转到最后一个非空白字符”。我猜 _ 有点像“不是空格”,所以用来助忆?我把它包含进来是为了全面,但你不会经常使用它的。你也可以选择组合你学过的其他命令,这样就不必记住这个一次性的命令。例如,你可以使用三个字符的 $ge(结合“行尾”和“向后移动到单词末尾”)或 $be 来移动到行上的最后一个非空白字符。你有多种选择;选择你觉得最容易记住或输入的那一个!

3.9. 跳转到特定行

如果你编译一些代码或运行 linter(代码检查工具),你总会被告知错误发生的行号(除非编译器特别没用)。

你可以通过输入行号作为计数,后跟(Shift 后的)G 来跳转到特定行。所以 100G 会将你的光标移动到第 100 行。或者,你可以使用 :100<Enter> ex 命令。

G 也可以不带计数地发出,在这种情况下,G 命令将总是带你到文件的末尾。

如果你愿意,可以用 1G 转到文件顶部,但由于这是一个非常常见的操作,你可以改用 gg(两个小写 g)。所有情况下 g 的助记符都是“Go to”(转到),g 后面可以跟很多东西(:help g 会向你介绍我没有涵盖的内容,但请注意 LazyVim 已经覆盖了其中一些)。

译者注:

  • <number>G: 跳转到第 <number> 行。
  • G: 跳转到文件最后一行。
  • gg: 跳转到文件第一行。

由于你最可能想要“转到”的地方是行号,所以最容易输入的 Ggg 命令被用于行号导航。

3.10. 跳转历史 (Jump History)

所有这些跳来跳去可能会让你有点迷失方向。幸运的是,有两个非常有用的快捷键可以返回你之前跳转过的地方。

Control-o 是我最常使用的非模式、基于 Control 的快捷键。老实说,我应该把它绑定到更容易按到的地方,我用得太频繁了。它基本上意味着“Older position in jump list”(转到跳转列表中较旧的位置),即回到你跳转之前的位置。

译者注:Control-o 理解为 “jump out” 或 “go back to old location” 可能更容易记忆。

这在你深入文件或模块编辑代码,然后意识到需要在文件顶部导入一个库时超级方便。你可以用 gg 跳转到文件顶部,用 s 寻找你想添加 import 的行,然后进入插入模式添加 import。现在你想回到你刚才工作的代码处,以便实际使用这个 import。按几次 Control-o 就能带你回去。

Neovim 会记录你所有的跳转历史,所以你可以在几个位置之间跳转(也许是为了查找文档或函数的调用签名),并且总能找到回去的路。

如果你跳得太远了,可以使用 Control-i 快捷键在历史记录中向前跳转。它正好是 Control-o 的反向操作。我不知道为什么选择 io 来做这个;也许是因为它们在 Qwerty 键盘上是相邻的?它们用得足够普遍,一旦你学会了,就不会忘记。

译者注:Control-i 理解为 “jump in” 或 “go forward to inner location” (相对于之前的跳转点) 可能有助于记忆。在很多终端或 GUI 应用中,Tab 键有时会被映射为 Control-i,所以如果 Control-i 不起作用,尝试 Tab 可能有效(尽管在 Vim 中 Tab 有其他用途)。

3.11. 总结

在 Vim 中导航代码是一个巨大的话题。你已经学到了足够的命令,可以比大多数非模式编辑器梦想的更有效地导航 Vim 窗口。但我们仅仅触及了皮毛,稍后我们还将介绍一系列更有用的代码导航命令。

我们介绍了 LazyVim 的 Seek 模式以跳转到可见窗口的任何位置,然后介绍了滚动命令以确保你想跳转到的东西是可见的。接着我们介绍了用主行键移动光标,并通过计数来倍增它们。

我们学习了查找模式与 Seek 模式的不同之处,尽管它们表面上相似。然后我们介绍了一些标准的快捷键,用于按词移动和移动到行上的关键位置,之后是跳转到特定行。最后我们介绍了如何导航到你之前跳转过的地方。

在下一章中,我们将学习更多关于打开文件和导航文件系统的内容。