vim motions

4 阅读3分钟

在 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";

  1. 场景:想修改变量名 descriptioninfo

    • 初始光标:在行首 S 处。
    • 操作:按 f d(跳到 d 上),然后按 ce(修改到单词结尾)。
    • 更好的操作ct (c + t + 空格):修改从当前位置直到空格前的内容。
    • 结果String info = "This is a long string";
  2. 场景:想删掉引号里的所有文字,但保留引号

    • 初始光标:在第一个引号 " 上。
    • 操作dt" (Delete Till "
    • 结果String description = "";
    • 解析t 找第二个引号,但停在它前面,所以 d 删除了引号中间的内容,引号被留下了。

二、 单词级动作:w, e, b, ge

这类动作根据单词边界跳转。

核心逻辑:

  • w (word): 下一个单词开头
  • e (end): 当前/下一个单词结尾
  • b (back): 上一个单词开头
  • ge: 上一个单词结尾

详细案例:

文本内容:const user_profile_manager = get_data();

  1. 场景:删除 user_profile_ 这一段

    • 初始光标:在 u 上。
    • 操作d2f_ (删除到第二个下划线)。
    • 或者用单词动作d3w(在 Vim 看来,user, _, profile 是独立的单词)。
    • 结果const manager = get_data();
  2. 场景:光标在行尾,想快速改掉 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
    }
  1. 场景:删掉这一行的注释(从 // 开始往后全删)

    • 初始光标:在 console 的任意位置。
    • 操作f/ (跳到斜杠),然后 d$ (从这里删到行尾)。
    • 结果 console.log("Starting...");
  2. 场景:把整个函数体复制下来

    • 初始光标:在 function 这一行。
    • 操作j (下移一行),V (进入行选中),k (向上或者用其他动作),更快的写法是:站在 {y%% 是跳到匹配括号的动作,也是一种 Motion)。

四、 搜索动作:/

这是最容易被忽略但最强大的 Motion。你可以把搜索作为 dc 的范围。

详细案例:

<div class="container">
    <p>Lots of text here...</p>
    <p>More text...</p>
</div>
<aside>Sidebar</aside>
  1. 场景:我想从当前位置一直删到 </aside> 标签处
    • 初始光标:在 <div 处。
    • 操作d/<\/aside> 然后回车。
    • 结果:除了 </aside> 之后的内容,前面全被删了。
    • 解析:Vim 把“从当前光标到搜索命中点”之间的所有距离当作 d 的操作范围。

五、 核心知识点:Inclusive (包含) 与 Exclusive (排除)

在深入使用 Motion 时,必须理解这个差异,否则你会发现有时多删了一个字母,有时少删了一个。

  1. Exclusive (排除在外的) —— w, b, t, 0, ^

    • 规律:操作范围不包括目标位置的那个字符。
    • 例子dt) 删到括号前,不删括号本身。dw 删到下个词开头,不删下个词首字母。
  2. Inclusive (包含在内的) —— e, f, $, G

    • 规律:操作范围包括目标位置。
    • 例子df) 删到括号,连带括号一起删掉。de 删到词尾,包含词尾那个字母。

总结表格:我该用哪个?

需求场景推荐动作 (Motion)记忆点
修改/删除到某个符号前ct{char} / dt{char}till (直到...前)
修改/删除到某个符号(含符号)cf{char} / df{char}find (找到并包含)
修改/删除光标后的整个单词ce / deend (到结尾)
修改/删除光标前的整个单词cb / dbback (向后找)
删掉这一行剩下的所有东西D (等同于 d$)$ 行尾
删掉当前到搜索词之间所有内容d/pattern搜索作为范围

一句话建议:当你想要操作的是当前位置到某处的路径时,用 Motion;当你想要操作的是光标所在的完整物体(如整个括号内、整个单词)时,用 Text Object (i/a)