在 Vim 中,“动作”(Motions)不仅是移动光标的方式,更是定义操作范围的标尺。当你把动作与操作符(Operator,如 d, c, y)结合时,你就拥有了极其精准的文本编辑能力。
以下是 Vim 动作 (Motions) 的深度分类总结与手把手案例详解。
一、 行内搜索动作:f, t, F, T
这类动作是“狙击手”,用于在当前行内快速定位字符。
核心逻辑:
f(find): 找下一个字符,光标落在字符上。t(till): 找下一个字符,光标落在字符前。F/T: 上述逻辑的反向版本(向左找)。
详细案例:
假设有一行代码:
String description = "This is a long string";
-
场景:想修改变量名
description为info- 初始光标:在行首
S处。 - 操作:按
f d(跳到 d 上),然后按ce(修改到单词结尾)。 - 更好的操作:
ct(c + t + 空格):修改从当前位置直到空格前的内容。 - 结果:
String info = "This is a long string";
- 初始光标:在行首
-
场景:想删掉引号里的所有文字,但保留引号
- 初始光标:在第一个引号
"上。 - 操作:
dt"(Delete Till") - 结果:
String description = ""; - 解析:
t找第二个引号,但停在它前面,所以d删除了引号中间的内容,引号被留下了。
- 初始光标:在第一个引号
二、 单词级动作:w, e, b, ge
这类动作根据单词边界跳转。
核心逻辑:
w(word): 下一个单词开头。e(end): 当前/下一个单词结尾。b(back): 上一个单词开头。ge: 上一个单词结尾。
详细案例:
文本内容:const user_profile_manager = get_data();
-
场景:删除
user_profile_这一段- 初始光标:在
u上。 - 操作:
d2f_(删除到第二个下划线)。 - 或者用单词动作:
d3w(在 Vim 看来,user,_,profile是独立的单词)。 - 结果:
const manager = get_data();
- 初始光标:在
-
场景:光标在行尾,想快速改掉
get_data- 初始光标:在分号
;上。 - 操作:
cb(Change Back)。 - 结果:
const user_profile_manager = [插入模式]; - 解析:
b动作让c操作符作用于“从当前位置到上一个单词开头”的范围。
- 初始光标:在分号
三、 绝对位置与行边界:0, ^, $, G, gg
用于处理一行或一个文件的宏观边界。
核心逻辑:
0: 绝对行首(第一列)。^: 软行首(第一个非空格字符,代码缩进后的位置)。$: 行尾。G/gg: 文件末尾 / 文件开头。
详细案例:
function init() {
console.log("Starting..."); // TODO: remove this
}
-
场景:删掉这一行的注释(从
//开始往后全删)- 初始光标:在
console的任意位置。 - 操作:
f/(跳到斜杠),然后d$(从这里删到行尾)。 - 结果:
console.log("Starting...");
- 初始光标:在
-
场景:把整个函数体复制下来
- 初始光标:在
function这一行。 - 操作:
j(下移一行),V(进入行选中),k(向上或者用其他动作),更快的写法是:站在{按y%(%是跳到匹配括号的动作,也是一种 Motion)。
- 初始光标:在
四、 搜索动作:/
这是最容易被忽略但最强大的 Motion。你可以把搜索作为 d 或 c 的范围。
详细案例:
<div class="container">
<p>Lots of text here...</p>
<p>More text...</p>
</div>
<aside>Sidebar</aside>
- 场景:我想从当前位置一直删到
</aside>标签处- 初始光标:在
<div处。 - 操作:
d/<\/aside>然后回车。 - 结果:除了
</aside>之后的内容,前面全被删了。 - 解析:Vim 把“从当前光标到搜索命中点”之间的所有距离当作
d的操作范围。
- 初始光标:在
五、 核心知识点:Inclusive (包含) 与 Exclusive (排除)
在深入使用 Motion 时,必须理解这个差异,否则你会发现有时多删了一个字母,有时少删了一个。
-
Exclusive (排除在外的) ——
w,b,t,0,^- 规律:操作范围不包括目标位置的那个字符。
- 例子:
dt)删到括号前,不删括号本身。dw删到下个词开头,不删下个词首字母。
-
Inclusive (包含在内的) ——
e,f,$,G- 规律:操作范围包括目标位置。
- 例子:
df)删到括号,连带括号一起删掉。de删到词尾,包含词尾那个字母。
总结表格:我该用哪个?
| 需求场景 | 推荐动作 (Motion) | 记忆点 |
|---|---|---|
| 修改/删除到某个符号前 | ct{char} / dt{char} | till (直到...前) |
| 修改/删除到某个符号(含符号) | cf{char} / df{char} | find (找到并包含) |
| 修改/删除光标后的整个单词 | ce / de | end (到结尾) |
| 修改/删除光标前的整个单词 | cb / db | back (向后找) |
| 删掉这一行剩下的所有东西 | D (等同于 d$) | $ 行尾 |
| 删掉当前到搜索词之间所有内容 | d/pattern | 搜索作为范围 |
一句话建议:当你想要操作的是当前位置到某处的路径时,用 Motion;当你想要操作的是光标所在的完整物体(如整个括号内、整个单词)时,用 Text Object (i/a)。