跳过正文
  1. Posts/

Vim 学习笔记 Day 002:移动,从“硬挪”到“直接到位”

·4252 字·9 分钟
📖 阅读 --
DogDu
作者
DogDu
工作结束或者累了, 要不休息一会, 看会动漫吧 ~
目录
Vim 14 天 - 这篇文章属于一个选集。
§ 2: 本文

今日主题
#

  • 主主题:移动:从“硬挪”到“直接到位”
  • 副主题:把 h j k l 从主导航降级成兜底导航

学习目标
#

  • 解决“明明知道怎么动,但移动成本太高”的问题。
  • 明确 Vim 里的高效移动不是更快地多按几次,而是更少地按、更直接地到。
  • 建立三层移动视角:
    • 按字符微调
    • 按词和按行快速到位
    • 按文件位置跳转
  • 为下一天的 operator + motion 做准备。

前置回顾
#

  • Day 001 已经建立一个关键前提:
    • 普通模式是主工作模式。
    • 插入模式只是短暂停留。
  • 既然普通模式是主工作模式,下一步就不是学着“怎么继续打字”,而是学着“怎么先到对的位置”。
  • 当前本地环境已确认:
    • vim 9.2
    • nvim 0.12.0
  • 本篇仍以 vim 的基础移动模型为主,因为这部分在 vim / nvim / LazyVim 中都通用。

典型场景
#

这一篇要解决的是真实编辑里很常见的几种慢:

  • 想改一处单词,结果一路按方向键或 h l 挪过去。
  • 想去行首或行尾,却还在一点点来回蹭。
  • 想去文件开头、文件结尾或某一段附近,结果只能靠滚屏和猜位置。
  • 明明只是想改“这个词”或“这一行结尾”,却因为定位慢,后续编辑也慢。

如果移动还是“硬挪”,那后面的删除、修改、复制都会显得笨重。

最小命令集
#

今天只保留最常用、最值得形成肌肉记忆的那一层。

字符级微调
#

  • h / l
    • 向左 / 向右移动一个字符
  • j / k
    • 向下 / 向上移动一行

行内快速到位
#

  • 0
    • 到本行第一个字符
  • ^
    • 到本行第一个非空白字符
  • $
    • 到本行最后一个字符

按词移动
#

  • w
    • 到下一个词的开头
  • e
    • 到当前词或下一个词的结尾
  • b
    • 回到上一个词的开头
  • ge
    • 回到上一个词的结尾

行内找字符
#

  • f{char} / F{char}
    • 在当前行向前 / 向后找到某个字符,并落到它上面
  • t{char} / T{char}
    • 在当前行向前 / 向后找到某个字符,但停在它前一个位置
  • ; / ,
    • 重复上一次 f / F / t / T 搜索,或反方向重复

文件级定位
#

  • gg
    • 到文件开头
  • G
    • 到文件结尾
  • {count}G
    • 到指定行,例如 20G

count
#

  • {count}{motion}
    • 让一个 motion 一次跨多步,例如 3w5j20G

软换行场景补充
#

  • gj
    • 按屏幕显示行向下
  • gk
    • 按屏幕显示行向上

它是怎么用的
#

先改一个判断:高效移动不是“按得快”,而是“少按几次”
#

很多人刚开始学移动,会把重点放在:

  • 能不能不用方向键
  • 能不能把 h j k l 按熟

这当然有用,但它只解决了“怎么挪”,还没有解决“为什么还要挪这么多次”。

真正更重要的是:

  1. 先判断目标在词内、行内,还是文件级位置。
  2. 选最接近那个尺度的 motion。
  3. 到位后再做编辑。

所以 h j k l 不是废弃,而是降级成微调工具。

0^$ 解决的是“行内定位”问题
#

这三个键非常高频,因为你经常会遇到:

  • 去行首补点内容
  • 去行尾追加点内容
  • 跳到代码缩进后的真正开头

最容易混的是:

  • 0 是真正的第一个字符,包含行首空格或缩进
  • ^ 是第一个非空白字符
  • $ 是最后一个字符

写代码时,^ 往往比 0 更常用,因为你通常想落在代码开头,不是落在缩进空格上。

webge 解决的是“按词到位”问题
#

这是 Day 002 的真正主力。

当你看到一个目标词时,应该逐渐从:

  • l l l l l

转成:

  • w
  • b
  • e
  • ge

可以这样理解:

  • w:往前找下一个词头
  • e:往前找词尾
  • b:往回找词头
  • ge:往回找词尾

官方帮助 :help word-motions 也说明了这组命令的核心是“按 word / WORD 移动”,而不是按字符移动。

W / E / B / gE:按“非空白块”移动
#

D:\program\Learn-Vim\ch05_moving_in_file.md 很值得补的一个点是:

  • word
    • 受标点、连字符等影响更大
  • WORD
    • 只按空白分隔,粒度更粗

所以当你面对下面这种内容时:

TXT 代码 · 共 1 行
snake_case-name
  • w 往往会在更细的边界停下
  • W 会把整块非空白内容看成一个更大的单位

日常结论先记这个就够:

  • 普通文本和代码里的常规跳词,先用 w / e / b
  • 遇到路径、参数串、带符号的片段,补上 W / E / B / gE

count 让移动不只是“一个一个来”
#

本地 motion.txtLearn-Vim 都强调了同一件事:

  • 大多数 motion 都能接 count

所以不要把它想成少量特殊写法,而要把它想成通用句式:

  • 3w
    • 往前跳三个词
  • 5j
    • 向下五行
  • 20G
    • 直接去第 20 行

这会明显减少“我知道方向对,但还是要连按很多次”的感觉。

f / t / ; / ,:同一行内横向找目标时,比 llll 更值钱
#

当目标还在当前行时,很多时候甚至不用上 w

本地 motion.txt 里:

  • f{char}
    • 找到字符并落到它上面
  • t{char}
    • 找到字符,但停在它前一格
  • ;
    • 继续按同方向重复上一次行内搜索
  • ,
    • 反方向重复

真实场景里很常见:

  • 想跳到这行里的 =
  • 想停在 ) 前面
  • 想在两个引号之间快速来回

这时优先想到:

  • f=
  • t)
  • ;
  • ,

ggG{count}G 解决的是“文件级定位”问题
#

只要文件稍微长一点,你就会遇到:

  • 回文件顶部看标题或 imports
  • 去文件底部追加内容
  • 跳到某个大致已知的行号

这时再用滚动或连续 j k 就太慢了。

  • gg 用于直接回开头
  • G 用于直接去结尾
  • {count}G 用于直接去指定行

这组动作会让你第一次感受到:Vim 不是“更麻烦地移动”,而是“允许你直接去结构位置”。

gj / gk 是给“显示行”准备的,不是替代 j / k
#

当一行内容很长、屏幕发生软换行时:

  • j / k 仍然按“真实文本行”移动
  • gj / gk 按“屏幕显示行”移动

所以它们不是谁更高级,而是解决不同问题。

如果你在 Markdown、长注释或长 JSON 中经常遇到“明明看起来就在下一屏幕行,但 j 一下跳太多”,就该想到 gj / gk

常见操作套路
#

套路 1:改某个单词,先按词到位,再进入编辑
#

错误直觉:

  • 一直 l 挪过去
  • 到附近后再 ia

更稳的方式:

  1. w / b 到目标词附近
  2. ege 精调到词尾附近
  3. 必要时再用 h / l 微调
  4. 再进入插入或等待下一天的 operator + motion

套路 2:想在行首代码前后动手,先分清 0^
#

如果你要处理的是缩进本身,用 0

如果你要处理的是这行真正的内容开头,用 ^

这会显著减少“明明到了行首却还差一点”的感觉。

套路 3:同一行找目标字符,优先 f / t
#

场景:

  • 你看见目标就在这一行
  • 只是横向距离有点远

这时先别下意识按很多次 l,而是优先看:

  • 目标字符能不能用 f{char} 直接到
  • 需不需要用 t{char} 停在它前面
  • 到了之后用 ; / , 继续重复

套路 4:跨大段内容时,不要拿 j / k 当主力
#

更好的优先级通常是:

  1. 先看是不是 gg / G / {count}G
  2. 再看是不是 w / b
  3. 最后才是 h j k l

这就是“直接到位”的核心。

套路 5:遇到屏幕软换行,记得切换到 gj / gk
#

如果你看见的是“视觉上的下一行”,但编辑器内部其实还是同一长行,那就不要和 j / k 硬扛。

环境差异:vim / nvim / LazyVim
#

这些移动在 vimnvim 中的基础语义一致
#

本地帮助文件里,Vim 9.2Neovim 0.12.0motion.txt 都保留了相同的移动模型:

  • h j k l
  • 0 ^ $
  • w e b ge
  • gg G
  • gj gk

所以 Day 002 学到的不是“某个发行版技巧”,而是 Vim 系编辑器的共同底层。

到了 LazyVim,这些基础移动不会被替代
#

LazyVim 会增强:

  • 文件搜索
  • buffer 切换
  • 项目导航
  • LSP 跳转

但进入文件后的细粒度定位,仍然离不开今天这些 motion。

如果 Day 002 不稳,到了 LazyVim 往往会出现一种假熟练:

  • 项目能打开
  • 文件能跳到
  • 但进到文件里改具体内容时还是慢

今日练习(5-10 分钟)
#

练习材料
#

新建一个临时文件,写入下面内容:

TXT 代码 · 共 5 行
vim motion should reduce wasted movement
longer lines make direct navigation more important
I want to move by word instead of drifting by character
sometimes a wrapped line is still one real line
snake_case-name behaves differently for word and WORD

练习任务
#

  1. vimnvim 打开这个文件。
  2. 只用 wb,在第一行和第三行之间反复找 motionwordcharacter 这些词。
  3. 在第二行里分别用 0^$ 感受行首、非空白开头、行尾的区别。
  4. gg 回到文件开头,再用 G 去文件结尾。
  5. 2G3G4G 分别跳到对应行。
  6. 在第一行或第三行试一次 3w2b,感受 count 和 motion 的组合。
  7. 在第二行找一个明显字符,试一次 ft;, 的配合。
  8. 把光标放到最后一行,比较 wW 的落点差异。
  9. 如果你的窗口足够窄,制造软换行后分别试 j / kgj / gk 的差异。

完成标准
#

  • 能明确说出 h j k l 是微调,不是主导航。
  • 能在真实文本里优先想到 w / b / e / ge
  • 能在同一行目标较远时想到 f / t,而不是一路 l
  • 能把 {count}{motion} 当成自然句式,而不是例外。
  • 能分清 0^
  • 能用 gg / G / {count}G 完成文件级定位。

今日问题与讨论
#

我的问题
#

问题 1:是不是以后就不该再用 h j k l 了?
#

  • 简答:
    • 不是。它们仍然很重要,但更适合微调,而不是长距离主导航。
  • 场景:
    • 已经接近目标,只差 1 到 2 个字符。
  • 依据:
    • :help motion.txt 把它们作为最基础的字符级与行级移动;高效来自“选对尺度”,不是完全废掉某组命令。
  • 当前结论:
    • h j k l 要保留,但要从主力降级成兜底。
  • 是否需要后续回看:

问题 2:0^ 到底该优先记哪个?
#

  • 简答:
    • 两个都要记,但写代码时通常先养成 ^ 的习惯。
  • 场景:
    • 一行前面有缩进空格,而你只想落到代码开头。
  • 依据:
    • :help left-right-motions 对两者给出的定义不同:0 到第一字符,^ 到第一个非空白字符。
  • 当前结论:
    • 改缩进时想 0,改内容时多想 ^
  • 是否需要后续回看:

问题 3:j / kgj / gk 为什么都需要?
#

  • 简答:
    • 因为它们处理的是两种不同的“行”。
  • 场景:
    • 长行发生软换行时,视觉上一行不等于真实文本一行。
  • 依据:
    • :help up-down-motions 明确区分了 linewise 的 j / k 和 display lines 的 gj / gk
  • 当前结论:
    • 普通代码文件常用 j / k,长段文本或软换行场景要想到 gj / gk
  • 是否需要后续回看:

外部高价值问题
#

问题 1:wW 到底差在哪?
#

  • 问题:
    • 它们看起来都像“往前跳一个词”,为什么还要分大小写两套?
  • 简答:
    • 因为 wordWORD 的边界规则不同,WORD 只按空白分隔,粒度更粗。
  • 场景:
    • 代码里出现 snake_case-name、路径、参数串、带标点的片段。
  • 依据:
    • 本地 motion.txtword / WORD 的定义。
    • D:\program\Learn-Vim\ch05_moving_in_file.md
  • 当前结论:
    • 普通文本和代码常规跳词先想小写;遇到符号很多但你只想按整块跳,再补大写。
  • 是否需要后续回看:

常见误区或易混点
#

  • 误区 1:不用方向键,就等于移动已经高效
    • 不是。连续按很多次 h l 仍然是低效移动。
  • 误区 2:w / b 只是“可选技巧”
    • 不是。它们是从字符移动升级到词级移动的核心。
  • 误区 3:0^ 差不多
    • 不差不多。缩进一多,区别会非常明显。
  • 误区 4:gg / G 只在超大文件里才有用
    • 不是。只要超过几屏,就已经比滚动更省力。
  • 误区 5:gj / gk 没必要学
    • 只要你编辑 Markdown、注释、日志或任何长行文本,它们就会变得实用。

扩展内容
#

  • g_
    • 到行尾最后一个非空白字符,适合某些代码行尾定位。
  • n|
    • 直接跳到当前行第 n 列,偶尔处理固定列文本时会很直接。
  • %
    • 在成对的括号、方括号、花括号之间跳转,后面进入代码编辑时会更常用。
  • ( / ){ / }
    • 按句子或段落移动,更适合长文本场景。

今日小结
#

Day 002 的关键不是“记更多方向命令”,而是建立一个判断:

  • 远距离不要硬挪
  • 按词、按行、按文件位置直接到位
  • h j k l 负责微调,不负责包办一切

只要这一步建立起来,下一天学 operator + motion 时,很多组合就会自然顺起来。

明日衔接
#

下一步建议进入:

  • Day 003:operator + motion

重点会开始解决:

  • 为什么 dc 这种操作离不开移动
  • 为什么 dwded$ 这种组合才是 Vim 的真正编辑节奏
  • 为什么 Day 002 的移动不是独立知识,而是后续编辑动作的基础

复习题
#

  1. 为什么说高效移动不是“按得更快”,而是“按得更少”?
  2. 0^ 的区别是什么?写代码时谁更常用?
  3. webge 分别更适合解决什么定位问题?
  4. ggG20G 分别适合什么场景?
  5. 在软换行场景里,为什么要区分 j / kgj / gk
Vim 14 天 - 这篇文章属于一个选集。
§ 2: 本文