Vim 修炼场
CHAPTER 03 · 移动篇 下

移动·行与文件:大范围跳跃

预计阅读 35 分钟(含练习约 1 小时)· 前置:第 2 章 · 本章演示 7 个

学习目标

  • 建立行间与文件级的移动能力:不再用滚轮「逛」到目的地,而是报地址直达
  • 掌握相对行号思维:5j / 3k 一步跳 N 行
  • 掌握文件三键:gg(开头)、G(结尾)、{n}G / :n(直达第 n 行)
  • 掌握结构级移动:段落 { }、配对符 % —— 按代码的结构跳,不按行号跳
  • 会用书签 ma + 'a / `a 和跳转表 Ctrl+o / Ctrl+i 走回头路
  • 了解窗口地标 H M L 与滚动键 Ctrl+d/u、zz 的分工

1. 行内自由之后,新的痛

第 2 章毕业时,你在一行之内已经两键直达任何位置。但真实代码是几百行的文件:光标在第 30 行,你想去第 55 行看看那个函数,再回 30 行继续写 —— 熟悉的剧本上演:右手摸向鼠标,滚轮哗哗滚,眼睛扫过中间二十几行不知所云,到了地方改两笔,又哗哗滚回来。这趟旅程里你的眼睛当了一次「人肉传送带」,而这正是 Vim 诞生的年代里 Ken Thompson 要解决的核心问题之一:光标移动不应该消耗注意力。

先分清两种动作:「滚动」和「到达」。滚动是浏览 —— 你想看看中间有什么,适合鼠标滚轮和 Ctrl+d/u;到达是通勤 —— 你已经知道要去哪,只是要走过去,适合本章的主角们。90% 的日常移动是「到达」,而多数人把它们全办成了「滚动」,痛就痛在这。本章给你三件套:报行号(5j、42G)、认结构({ }、%)、走回头路(书签、跳转表)。

还有一条贯穿全章的伏笔:这些移动键不仅是「去」的门票,它们同时是编辑语言的名词 —— 第 4、5 章你已经见过 dt, 和 diw,本章的 % 也能当名词(d% 删整个块)。移动学得越透,后面动词的射程就越远。

2. 相对行号:把 j/k 变成传送门

j/k 一次一行,去五格之外就得连按五次 —— 问题不在按键,在于你得自己数。解法是让编辑器替你数:相对行号(relative line numbers)。开启后,当前行显示真实行号,其余行显示「离当前行几步」。想去哪行,读一下它头上的数字,报数,走人:

看演示时注意行号列的变化:每跳一步,所有行号都重算一次 —— 因为「相对」的参照物是你自己。这就是它的哲学:位置不是文件的属性,是你和目标的距离。配合计数前缀,2j、4k 变成「报地址」:眼睛看目标、读距离、手指报数,全程不用心算「55 减 30 等于几」。

两个实用建议:①绝对行号 + 相对行号混用是主流配置(当前行给绝对号,方便和报错信息、同事口头引用对齐;其他行给相对距离,方便跳)—— 播放器里演示的就是这种,Vim 里叫 set number relativenumber,IDE 的开启方法见本章 IDE 备注;②距离超过一屏就别数了 —— 相对行号只服务「看得见的距离」,更远的目标交给下一节的文件三键。

💡 相对行号的正确打开方式 开启它之后的第一个星期,强迫自己做一件事:任何垂直移动,先读数字再按 3j,禁止连按 jjj。连按的问题是每次只思考一格,手比脑快;报数逼你先确认目标,一步到位。一周后 数字+j/k 会成为本能,你会发现自己再也不看滚动条了。

3. 文件三键:gg、G 与 {n}G

超出视野的距离,思路从「数步数」切换到「报门牌」:gg 直奔文件第一行,大写 G 直奔最后一行,{n}G 精确直达第 n 行(命令行版是 :n 再回车,效果相同):

gg 的来历顺带一提:goto —— g 是「扩展前缀」,g_、gU、gJ 都是它带的词,这个家族你已经很熟了。{n}G 的最高频场景是跟着报错跳:编译器说第 42 行有错、测试输出指向第 118 行 —— 眼睛读到数字,手上报 42G,两秒内人到现场,这是 Vim 用户的标志性动作。另有两个顺手键记一眼:当前行之上/之下第一个非空行用 - / +(少用但偶尔顺手),行内定位交给第 2 章。

注意 G 落地时默认落在该行第一个非空白字符上(受 startofline 选项影响,默认开)—— 所以跳过去通常正好落在代码上而不是缩进空格里,很贴心。而 gg 同理会落在第一行首个非空白字符(演示里第一行顶格,所以是第 0 列)。

顺带一个和 IDE 的对照:两个 IDE 都有「转到行」对话框(VSCode 的 Ctrl+g,IDEA 的 Cmd+l),功能上与 {n}G 等价 —— 区别在交互节奏:对话框要等弹窗、要按回车,Vim 的版本是盲打的,眼睛全程不用离开代码。这也是本教程反复出现的主题:同一个功能,Vim 的形态永远更贴着手指。日常跟着编译报错、测试栈帧跳行时,把 120G 练成肌肉记忆,比每次开「转到行」快出一截。

4. 段落移动:{ 与 }

报行号适合「我知道门牌」,但更多时候你知道的是结构:「下一个函数」「上一段配置」。} 跳到下一段、{ 跳回上一段 —— Vim 对「段落」的定义和第 4 章的 ip 完全一致:空行分隔的一坨连续文本:

两个细节:①落点是分段的空行本身,不是下一段的第一个字符 —— 想直接进段内干活,补一个 j 或 w 就好,不必较劲;②{ } 同样吃计数,2} 跳过一段。代码文件几乎都用空行分隔逻辑块,所以这对键的实际体验是「按函数/逻辑块翻页」—— 调试时快速浏览文件结构,比滚轮高效一个量级,而且它跳的是语义边界,不会停在半截逻辑中间让你分神。

同族还有一对句子键 ( )(按英文句号分句)。在代码里句子边界几乎没有意义(分号不是句号),写 Markdown 或文案时才偶尔有用,知道有这对键即可,不必练。真正值得练的结构键是下面这位。

5. %:配对符任意门

把光标放到 (、{、[(或它们的闭括号)上,按 % —— 直达配对的另一半。多深、多远、跨多少行都无所谓:

% 的日常用法有三层。第一层,看结构:光标在函数的 { 上按 %,一眼看到函数结尾在哪行,再按一次弹回来 —— 「这个函数多长」两键就有答案。第二层,配对检查:改完一堆括号,从最外层 % 一下,落点符合预期说明配对没坏;落点诡异说明有括号漏了或多了,这是比肉眼数括号可靠得多的自检。第三层,当名词:d% 从光标删到配对符(含两端)—— 想删掉整个 if 块、整层包装?光标放到 { 上,d%,完事。它和第 4 章的 da{ 是近亲:区别是 % 要求光标先站在括号上,而对象只要求在块内 —— 现场哪个顺手用哪个。

💡 光标不在括号上?先搭个桥 % 只认光标脚下的字符。最常见的组合是 f( + %:先用第 2 章的 f 把光标送上括号,再跳。另外一对冷门但好用的:在块的任意位置直接跳配对,可以用 [( / ])(跳到本层开/闭括号)—— 记不住没关系,用 f 搭桥 + % 已经够日常了。

6. 书签:m、' 与 `

「到达」解决了一半,另一半是回来。场景:你在第 200 行改代码,需要去文件头查一个常量的定义 —— 查完怎么回 200 行?再 200G 一次?行号还可能已经变了。正确工具是书签(mark):出发前钉个钉子,回来时一键召回。

语法:m{字母} 设书签(如 ma),'{字母} 跳到书签所在行首,`{字母} 连行带列精确还原。设书签时屏幕毫无提示 —— 这是它被低估的原因之一,人们不信任看不见的操作,但 m 是 Vim 里最可靠的静默操作之一。命名建议:用有含义的字母,mb(begin)、me(end)、mt(todo)比随手 ma 三天后还记得住。

两个进阶知识点先混个脸熟:①小写 a–z 是本文件书签,大写 A–Z 跨文件且持久(重启也在);②还有一对系统自动维护的特殊书签:''(两个单引号)跳到上次跳转出发的地方 —— 在两处来回横跳时它是零成本往返门,试试 G 之后按 ''。

书签最经典的用工场景是「对照改码」:接口定义处设 mi(interface)、调用密集区设 mc(calls),写实现时 'i 看一眼签名、'c 回战场 —— 两三个字母就是你的私人分屏。再比如 code review:从头部意见区设 mr,一路往下改,改完 'r 回去对清单,不多不少正好够用。

7. 窗口地标 H M L 与滚动家族

H(High)、M(Middle)、L(Low)分别跳到可见窗口的第一行、正中、最后一行。注意它们的坐标系是屏幕而不是文件 —— 不管文件滚到哪,三键永远落在你眼前这屏的三颗地标上:

使用频率上 H M L 属于二线键(有 {n}G 和相对行号在,垂直直达很少缺工具),但两个场景很好用:①翻页/搜索后先按 M 定锚再观察四周;②配合 d 家族吃射程 —— dH 删到窗口顶、dL 删到窗口底(「这一屏的下半部分不要了」的裸奔说法,第 7 章可视化模式会给更稳的替代)。

滚动家族一句话扫清(演示器窗口固定,自己到 IDE 里感受):Ctrl+d / Ctrl+u 向下/向上滚半页(最常用的浏览键,手指不用离开主键区);zz 把当前行滚到屏幕正中,zt 顶到最上,zb 压到最下 —— z 家族不移动光标,只挪屏幕。高频组合:长行/长函数里改完一处 zz 把战场居中;% 跳到块尾后 zz 把结局拉到眼前。至于整页的 Ctrl+f / Ctrl+b,和 PageUp/PageDown 同义,交给个人口味。

8. 跳转表:Ctrl-o 与 Ctrl-i

书签要手动设钉,适合「我知道会回来」的地方;更多时候你只是逛着逛着想原路返回 —— 这需要自动记录的历史。Vim 维护着一张跳转表(jumplist):每次大跳(gg、G、{n}G、%、'a、搜索命中……)都会登记一条。Ctrl+o 沿表后退,Ctrl+i 前进 —— 和浏览器的后退/前进是同一个思想:

把它和书签分工记住:书签是「存档点」,跳转表是「浏览历史」。两三步之内回得来,用 Ctrl+o 连按;要固定往返于两个工作点,用 ma 更稳(跳转表会被后续跳转冲,书签不会)。跳转表在 IDE 里还有个重量级队友:第 12 章会讲 gd(跳到定义)—— 跳过去看一眼源码,Ctrl+o 回来,这对组合是「查证-回岗」工作流的地基,而地基本章已经打好。另外注意:小步移动(j、w、f)不进跳转表 —— 历史只记大跳,不会被碎步刷屏,这个设计体会一下会很佩服。

9. 移动篇毕业:去哪儿,用什么

两章移动课到此收官,把全部工具压进一张决策表 —— 以后每当你伸手去摸鼠标,先扫一眼这张表:

目的地按键一句话备注
同一行内某处fx / w / $ …第 2 章:两键到达
邻近几行3j / 2k配相对行号,读数字报数
已知行号42G 或 :42报错、测试输出指路专用
文件头 / 尾gg / G两端书挡
下一段 / 上一段} / {按逻辑块翻,落在空行上
配对符另一半%光标先站上括号;还能 d%
屏幕顶 / 中 / 底H / M / L坐标系是屏幕,不是文件
回来(临时)Ctrl+o浏览历史后退,Ctrl+i 前进
回来(固定)'a / `a先 ma 设钉;` 连列一起还原
只是想看看Ctrl+d / Ctrl+u浏览才滚动,到达别滚动

最后给一个自测标准,达标就算移动篇毕业:任何一次光标移动超过 5 行,你的手都没有离开主键区;任何一次「去而复返」,你都有意识地选了 Ctrl-o 或书签而不是重新找。做到这两条,第 5 章的语法在你手上就有了飞毛腿 —— 编辑语言的名词库,至此齐装满员。

10. 每日训练计划(30 分钟 × 3 天)

天时间盒内容通过标准
第 1 天0–10 分钟开相对行号;重放演示 1–2,读 §1–§3;靶场里 10 组「读数字 → 数字 j/k」不再连按 j 超过两次
10–30 分钟练习 1:报数跳10 组 8 组一步命中
第 2 天0–10 分钟重放演示 3–5,读 §4–§6;真实文件里 }{{} 和 % 各巡一圈d% 删块一次成功
10–30 分钟练习 2 + 练习 3书签巡逻三轮不出错
第 3 天0–10 分钟重放演示 6–7,读 §7–§9;把决策表抄进便签贴屏幕边能默写决策表 8 成
10–30 分钟练习 4:盲测毕业考10 题对 7 题,移动篇毕业

11. 如果本章只记住三件事

  1. 到达别滚动:知道去哪就报地址 —— 3j(相对行号)、42G(行号)、}(下一段)、%(配对符);滚轮和 Ctrl+d/u 只留给「随便看看」;
  2. 回来有专门工具:临时回程连按 Ctrl+o,固定往返设书签 ma —— 别用重新找的方式回去;
  3. 移动键都是名词:今天学的 % 明天就是 d% 的射程 —— 移动词每多一个,动词的火力就翻一倍,这是「编辑语言」的复利。

移动篇两章到此完成,你的光标已经能在文件里飞行。下一章进入全站核心:给「东西」起名字 —— 文本对象。

练习

练习 1:报数跳(10 分钟)

靶场开好相对行号,光标放文件中部。心里选一个目标行 → 瞄一眼它头上的相对数字 → 数字+j 或 k 一步命中。10 组。然后升级:目标定在当前窗口外(看不见的行),改用 行号G 或 :行号(先开绝对行号或用状态栏行号)。通过线:10 组里 8 组不用按第二次。

练习 2:书签巡逻(10 分钟)

在一个 100 行以上的真实文件里:ma 在文件头附近设点、mb 中部、mc 尾部。然后按 'c → 'a → `b(注意第三个用反引号,感受连列还原)循环巡逻五轮。再玩 G + Ctrl+o:跳到文件尾,连按 Ctrl+o 一路退回出发地,再用 Ctrl+i 前进回去。体会「存档点」和「历史」的手感差异。

练习 3:% 往返工坊(10 分钟)

找一段嵌套两层以上的代码:①光标 f( 送上一个内层括号,% 往返 10 次;②光标放到函数的 { 上,% 看结尾、% 弹回,5 组;③d% 删掉一个 if 块,u 悔回;4j 挪到另一层再 d%,u。通过线:能说出 d% 和 da{ 对光标位置要求的区别(前者要求站在括号上,后者只要求在块内)。

练习 4:盲测毕业考(10 分钟)

对真实代码,10 个任务先说按键再看答案:①去文件第一行 ②去最后一行 ③去第 42 行(说出两种按键)④去下一段 ⑤回上一段 ⑥光标在 { 上,看这个块在哪结束 ⑦删掉光标所在的整个 if 块(括号要求?)⑧设书签并一键回岗 ⑨把当前行滚到屏幕中央 ⑩沿历史后退三步。答案:gg / G / 42G 或 :42 / } / { / % / d%(光标需在 { 上;在块内则用 da{)/ ma…'a / zz / Ctrl-o 连按三次。10 对 7 毕业。

常见陷阱

🕳 相对行号下找「绝对第 N 行」 开了相对行号后,行号列全是距离,想直接去「第 120 行」却拿 120 当距离按 120j —— 大型事故现场。记住:当前行头顶那个没有正负号的数字才是绝对行号;去绝对行号用 120G 或 :120,相对数只用来跳「看得见」的目标。
🕳 以为 } 落在下一段的第一个字符上 } 的落点是分段的空行本身(或文件末尾),不是下一段开头 —— 于是按完感觉「没动」。这不是 bug:{ } 移动到的是「段边界」。养成补一枪的习惯:}j(进段)或 }w(直达第一个词)。
🕳 Ctrl-d / Ctrl-u 被 IDE 抢走了 在 VSCode 里 Ctrl+d 原生是「添加多光标」,装了 VSCodeVim 后默认被 Vim 接管成半页滚动(多光标改用 gb);但若你改过键盘快捷键或用别的插件,可能半页滚动就失灵了。滚不动先查键冲突;IDEA 里 mac 的 Cmd+d 是复制行,和 Vim 无关,Ctrl+d 才是滚动。详见本章 IDE 备注。
🕳 指望小写书签隔天还在 小写 a–z 书签是会话级的:关掉文件/IDE 就消失;要跨会话、跨文件,用大写 A–Z。另外别把书签当任务清单长期囤 —— 它最好的用法是「当下这几分钟的往返钉」,用完就换,钉子越少越记得住哪根是哪根。

VSCode / IDEA 差异

VSCode(VSCodeVim)

  • 相对行号:设置项 editor.lineNumbers 改为 relative(设置界面搜 "Line Numbers");当前行会自动显示绝对号,正好是推荐的混用形态
  • Ctrl+d:VSCode 原生是「添加下一个匹配为多光标」;装 VSCodeVim 后默认接管为半页滚动,多光标用 gb(第 7 章)。若想还原原生行为,在 Keyboard Shortcuts 里自己仲裁
  • %、HML、zz、书签、Ctrl+o/i 跳转表:VSCodeVim 全部原生支持,照 Vim 语义用
  • VSCode 自己也有「后退/前进」导航(Go 菜单),但它与 Vim 跳转表历史不互通 —— 用 Vim 键就认 Vim 的表,别混着按

IntelliJ IDEA(IdeaVim)

  • 相对行号:在 ~/.ideavimrc 加 set relativenumber(与已有的 set number 同时开即混用形态);IDEA 编辑器设置里也有原生 Relative line numbers 选项 —— 建议用 IdeaVim 的一侧,行为和 Vim 文档一致
  • Ctrl+d/u:mac 上 IDEA 的 Cmd+d 是复制当前行(与 Vim 无关);Ctrl+d 由 IdeaVim 接管为半页滚动。若个别键不生效,是 sethandler 仲裁问题,第 12 章配置篇统一处理
  • 书签:IDEA 自带书签(F11)与 Vim 的 m 书签互不相通,选一套用即可;IdeaVim 的 A–Z 大写书签同样支持跨文件
  • Ctrl+o 在 IDEA 里原生是「回到上一个编辑位置」—— IdeaVim 激活后走 Vim 跳转表语义,覆盖之;想用原生的可从菜单 Navigate › Back 进入

本章速记

按键作用按键作用
3j / 4k按相对行号跳 N 行% / d%配对符往返 / 删整块
gg / G文件第一行 / 最后一行ma + 'a设书签 / 跳回(行)
42G / :42直达第 42 行(两种)`a / ''书签(带列)/ 上次位置
} / {下一段 / 上一段(空行分界)Ctrl+o / Ctrl+i跳转表后退 / 前进
H / M / L窗口顶 / 中 / 底Ctrl+d/u / zz半页滚动 / 当前行居中

一句话心法:到达报地址(行号、段落、配对符),回来靠钉子(书签)和历史(Ctrl-o)—— 滚动只留给浏览。