关于Vim中d{motion}命令在不同motion下表现差异及通用规则的咨询
d{motion}会有两种不同的删除效果? 这其实是Vim里移动命令(motion)的包含性(inclusive)与排他性(exclusive)属性在起作用,这是Vim文本操作的核心规则之一,完全能统一解释你遇到的两种场景:
拆解你的两个场景
场景1:3w是排他性(exclusive)motion
当你执行3w时,光标会跳到第3个单词的起始位置,但这个motion是排他的——意思是它的「目标位置」(也就是那个单词的第一个字符)并不被包含在motion覆盖的范围内。
所以d3w会删除从当前光标位置开始,到3w目标位置之前的所有字符。在你的例子里,光标初始在(之后,3w跳到int的i,但i不属于3w覆盖的范围,因此删除的是(int 这部分,留下了i,最终代码变成fori = 0; ...。
场景2:%是包含性(inclusive)motion
%是用来匹配括号/引号的motion,这类针对文本块边界的命令属于包含性的——它的目标位置(匹配到的括号)会被包含在motion覆盖的范围内。
所以d%会删除从当前光标位置开始,到%目标位置包括目标位置的所有字符。你的例子里,%匹配到对应的),d%就把(int i = 0; i < 10; i++)这整个括号区域(包括括号本身)都删掉了,最终剩下for{。
通用规则:判断motion的包含/排他属性
Vim里的motion可以通过以下方式快速区分,也可以用可视化模式验证:
- 排他性motion:大多数基于单个字符、单词的移动命令,比如
w、b、h、l。你可以执行v{motion},选中的文本范围就是d{motion}会删除的内容——比如v3w只会选中到目标单词的前一个字符。 - 包含性motion:基于文本块、括号匹配、文本对象的命令,比如
%、(、)、{、},以及a)、i(这类文本对象,还有G、gg这类行首行尾命令。同样用v{motion}测试,v%会选中从光标到匹配括号的全部内容(包括括号)。
简单总结:跳转到「块边界」的motion通常是包含性的;跳转到「字符/单词起始/中间」的motion通常是排他性的。
额外小技巧:强制调整包含范围
如果你想让排他性motion变成包含性的,可以在motion前加v,比如dv3w就会删除到3w的目标字符(包括该字符);反之,也可以在可视化模式下用o调整选中范围,但日常使用时,记住motion本身的属性就足够应对大多数场景。
内容的提问来源于stack exchange,提问作者Tryer

