前言
之前用 Vim, 遇到字符串的修改时, 基本是
进入编辑Backspace慢慢删 / 估计字符串长度 n
x一点点删其实它还是有更便捷的操作方式
本文对 Vim 文本 删除/修改 做一点讲解。
正文
来系统讲一下 Vim 中**删除(delete)和修改(change)**的常用操作。
核心规律是:
vim
操作符 + 范围
最常见操作符:
vim
d 删除(delete)
c 修改(change):删除后自动进入插入模式
x 删除单个字符
s 删除单个字符后进入插入模式
例如:
vim
di" 删除双引号内部
ci" 修改双引号内部
两者的区别只是:c 会在删除后直接进入插入模式。
1. 引号、括号中的修改与删除
假设有:
js
const message = "this is a very long text";
光标只要位于双引号内容中的任意位置。
保留引号,只删除内容
vim
di"
结果:
js
const message = "";
i 是 inner(内部),所以不包含外围的引号。
保留引号,修改内容
vim
ci"
结果会先变成:
js
const message = "";
并自动进入插入模式。输入新内容:
text
Hello Vim
再按 Esc,得到:
js
const message = "Hello Vim";
这是编辑字符串时最常用的命令之一。
引号也一起删除
vim
da"
a 是 around(连同外围一起)。
js
const message = "this is a very long text";
执行 da":
js
const message = ;
通常在你想移除整个字符串表达式时使用。
各种成对符号都适用
vim
di' " 删除单引号内部
ci' " 修改单引号内部
da' " 删除单引号及内容
di` " 删除反引号内部
ci` " 修改反引号内部
di( " 删除小括号 () 内部
ci( " 修改小括号 () 内部
da( " 连 () 一起删除
di[ " 删除方括号 [] 内部
ci[ " 修改方括号 [] 内部
da[ " 连 [] 一起删除
di{ " 删除花括号 {} 内部
ci{ " 修改花括号 {} 内部
da{ " 连 {} 一起删除
括号通常也可使用对应的另一种写法:
vim
di) " 等同于 di(
di] " 等同于 di[
di} " 等同于 di{
例如:
python
process(user_id, verbose=True)
光标在括号内部时:
vim
ci(
会保留 (),清空参数并进入插入模式:
python
process()
2. 修改、删除当前单词
假设内容是:
text
The quick brown fox
光标位于 quick 的任何位置。
删除当前单词
vim
diw
i:innerw:word
结果:
text
The brown fox
注意:它只删除单词本身,可能留下两个空格。
删除单词以及一个相邻空格
vim
daw
a:around word
结果通常更符合删除"一个词"的预期:
text
The brown fox
修改当前单词
vim
ciw
删除 quick 并进入插入模式。输入 slow 后:
text
The slow brown fox
ciw 是日常代码编辑里使用频率极高的一条。
cw 与 ciw 的区别
很多人会把它们混用,但有一个细微差异。
vim
cw
表示"从光标当前位置修改到单词末尾"。
vim
ciw
表示"修改完整的当前单词"。
例如:
text
hello
^
光标在 l 上:
vim
cw
只会删除 llo;
vim
ciw
会删除整个 hello。
所以想稳定地替换整个单词,优先用:
vim
ciw
3. 从当前位置删到某个位置
除了文本对象,你也可以将删除/修改和移动命令组合。
基本形式:
vim
d + 移动命令
c + 移动命令
例如:
vim
dw " 从光标删除到下一个单词开头
de " 从光标删除到单词末尾
db " 向前删除到上一个单词开头
d$ " 删除到行尾
d0 " 删除到行首(包括行首空白)
d^ " 删除到第一个非空白字符
修改版本:
vim
cw " 修改到下一个单词开头
ce " 修改到单词结尾
cb " 向前删除到上一个单词开头后,进入插入模式
c$ " 修改到行尾
C " 等同于 c$
一些直观示例
内容:
js
const total = calculatePrice(items, discount);
光标位于 calculatePrice 中间。
vim
ciw
直接替换整个函数名。
光标在 items 上:
vim
ci(
删除整个参数列表内容,保留括号:
js
const total = calculatePrice();
光标在等号右边,想清空这一行后半段:
vim
d$
结果:
js
const total =
想删掉后半段并立即输入新表达式:
vim
C
输入:
js
getTotal(cart);
得到:
js
const total = getTotal(cart);
4. 按字符查找后删除/修改
这是行内编辑非常高效的一组。
vim
f{字符} 跳到右侧下一个字符
t{字符} 跳到右侧下一个字符之前
F{字符} 向左找字符
T{字符} 向左跳到字符之后
它们可以和 d、c 组合。
例如:
text
name: value, next: another
^ 光标在 n
删除到逗号,但不删逗号
vim
dt,
结果:
text
, next: another
d + t,:删除到逗号的前一个位置。
删除到逗号,包含逗号
vim
df,
结果:
text
next: another
修改到逗号前
vim
ct,
先删除 name: value,然后进入插入模式,逗号保留。
这对修改 CSV、函数参数、对象字段、链式调用的一段内容很实用。
重复查找
vim
; " 重复最近一次 f / t / F / T
, " 反向重复最近一次 f / t / F / T
例如:
vim
f,
; " 跳到下一个逗号
; " 再跳到下一个逗号
5. 整行和多行删除、修改
当前行
vim
dd " 删除当前行
cc " 删除当前行并进入插入模式
yy " 复制当前行
也可以用:
vim
D " 从光标删到行尾
C " 从光标修改到行尾
S " 等同于 cc
指定数量的行
vim
3dd " 删除当前行及其后两行,共 3 行
5cc " 删除 5 行并开始编辑
4yy " 复制 4 行
删除到文件开头 / 结尾
vim
dgg " 从当前位置删除到文件开头
dG " 从当前位置删除到文件结尾
修改范围也可以:
vim
cG " 删除当前位置到文件结尾,然后进入插入模式
不过这种操作范围大,使用前建议确认一下光标位置。
6. 选择后删除或修改
可视模式适合范围不规则、但你能直接看到并选中的内容。
vim
v " 字符级选择
V " 行级选择
Ctrl-v " 块选择(列选择)
选中后:
vim
d " 删除选中内容
c " 删除并进入插入模式
x " 删除
y " 复制
例如:
- 按
v; - 移动光标选中一段;
- 按
d删除。
块选择:批量删列、改多行
例如代码:
js
const first = 1;
const second = 2;
const third = 3;
如果要删掉每一行前面的 const :
- 光标放到第一行
c; - 按
Ctrl-v; - 向下选择三行;
- 向右选中
const的宽度; - 按
d。
批量添加前缀:
Ctrl-v选中多行的第一列;- 按
I(大写i); - 输入
//; - 按
Esc。
批量修改行尾:
Ctrl-v选中多行;- 按
A(大写a); - 输入内容;
Esc。
7. 常用文本对象清单
文本对象是 Vim 删除和修改的精髓。
| 操作目标 | 删除内部 | 修改内部 | 删除整体 |
|---|---|---|---|
| 当前单词 | diw |
ciw |
daw |
| 双引号内容 | di" |
ci" |
da" |
| 单引号内容 | di' |
ci' |
da' |
| 小括号内容 | di( |
ci( |
da( |
| 方括号内容 | di[ |
ci[ |
da[ |
| 花括号内容 | di{ |
ci{ |
da{ |
| HTML 标签内容 | dit |
cit |
dat |
| 当前句子 | dis |
cis |
das |
| 当前段落 | dip |
cip |
dap |
例如 HTML:
html
<a href="/docs">Read documentation</a>
光标位于文字部分时:
vim
cit
删除 Read documentation 并进入插入模式,但保留标签:
html
<a href="/docs"></a>
如果用:
vim
dat
则整个 <a ...>...</a> 都会删掉。
8. 删除单字符、替换单字符
vim
x " 删除光标所在字符
X " 删除光标前一个字符
s " 删除当前字符并进入插入模式
r{字符} " 替换当前字符,例如 rx:替换为 x
R " 连续覆盖输入(Esc 退出)
示例:
text
helo
^
光标在第二个 l 后面的 o 前,想补一个 l:
vim
i
即可。
如果当前字符写错了:
text
helo
^
光标在 o 上,执行:
vim
ra
会得到:
text
hela
如果要将一个字符替换为一个较长文本:
vim
s
删除当前字符并进入插入模式。
9. 删除不想覆盖剪贴板的内容
默认 d、dd、x 删除的内容会存入 Vim 寄存器,因此会覆盖你刚复制的内容。
场景:
- 你先
yy复制了一行; - 之后想删除几行垃圾内容;
- 最后还想
p粘贴刚才复制的那一行。
可以把无用删除送到"黑洞寄存器":
vim
"_d
"_dd
"_di"
"_ciw
例如:
vim
"_dd
删除当前行,但不影响你此前复制的内容。
10. 删除后粘贴:替换选中内容
一个很实用的技巧:
vim
v{选择内容}p
在可视模式中按 p,会用最近复制的内容替换当前选中区域。
例如:
yiw复制单词 A;- 移到单词 B;
viwp选中 B 并用 A 替换。
在较新的 Vim/Neovim 中,这通常还会保留原先的复制内容,方便连续替换;如果你的版本/配置行为不同,想确保复制内容不受影响,可在替换前重新复制,或使用寄存器保存。
11. 结合搜索进行删除和修改
假设文件里有许多 debug:
vim
/debug
找到一个后:
vim
ciwproduction<Esc>
将其改为 production。
接下来:
vim
n
.
n:找下一个debug.:重复上一次修改
这比逐次输入 ciwproduction 快得多。
类似地,删除多个搜索结果附近内容时也可以:
vim
/temporary
diw
n
.
. 会重复最近一次可重复的修改操作。
12. 最值得先练成肌肉记忆的删除/修改命令
vim
x 删除一个字符
dd 删除一行
D 删除至行尾
dw 删除到下一个单词
de 删除到单词末尾
d$ 删除到行尾
ciw 修改当前单词
diw 删除当前单词
daw 删除单词及邻近空格
ci" 修改双引号内内容
di" 删除双引号内内容
da" 删除整个双引号字符串
ci( 修改括号内内容
ci{ 修改代码块内容
di{ 清空代码块
dt, / df, 删除到逗号前 / 删除到逗号(包含逗号)
ct, 修改到逗号前
u 撤销
Ctrl-r 重做
. 重复上一次修改
可以把 Vim 的编辑思路概括为:
vim
我想删除什么? d
我想删除后立刻重写? c
范围是单词? iw / aw
范围是引号内部? i"
范围是括号内部? i(
范围是到某个字符? t{字符} / f{字符}
范围是到行尾? $
所以当你看到:
js
sendEmail(user, "old subject", body);
光标在 "old subject" 内时,几乎不需要移动,直接:
vim
ci"
输入新标题即可。这正是文本对象让 Vim 高效的地方。