Vim / Neovim 完全指南 / 05 - 编辑命令
“Macros in Vim are just recorded sequences of keystrokes — and that’s what makes them so powerful.”
5.1 修改操作(Change)
5.1.1 修改命令详解
| 命令 | 含义 | 说明 |
|---|---|---|
c | change | 删除并进入插入模式 |
cc | change line | 修改整行内容 |
C | change to end | 修改到行尾 |
s | substitute char | 替换当前字符 |
S | substitute line | 替换整行(等同 cc) |
r | replace | 替换单个字符 |
R | replace mode | 进入替换模式 |
cw " 修改到下一词尾
c2w " 修改 2 个单词
ciw " 修改光标所在的单词
ci" " 修改引号内内容
cc " 清空当前行并输入新内容
C " 删除光标到行尾并输入
3cc " 修改 3 行
5.1.2 替换模式(Replace Mode)
R " 进入替换模式(覆盖已有字符)
gR " 虚拟替换模式(按屏幕宽度替换)
r{char} " 替换当前单个字符
gr{char} " 虚拟替换单个字符
5.2 宏(Macro)
5.2.1 宏的概念
宏是 Vim 记录的一系列按键操作,可以无限回放。宏是 Vim 自动化的核心。
录制 → 存储到寄存器 → 回放 → 重复执行
5.2.2 宏操作命令
| 命令 | 功能 |
|---|---|
q{a-z} | 开始录制宏到寄存器 |
q | 停止录制 |
@{a-z} | 回放寄存器中的宏 |
@@ | 重复上次回放的宏 |
{n}@{a-z} | 回放 n 次 |
@: | 重复上次 Ex 命令 |
@/ | 重复上次搜索 |
5.2.3 宏录制最佳实践
关键原则:确保宏可以从任意位置安全重复执行。
好的宏开头: 0 " 回到行首(确保起始位置一致)
好的宏结尾: j " 移到下一行(准备重复)
坏的宏: f,;cw " 依赖当前光标位置,不可靠
5.2.4 宏实战
场景一:批量添加行首注释
原始:
function_a()
function_b()
function_c()
function_d()
目标:
# function_a()
# function_b()
# function_c()
# function_d()
步骤:
1. 光标在第一行
2. qa " 开始录制到寄存器 a
3. I# <Esc> " 行首插入 '# '
4. j " 移到下一行
5. q " 停止录制
6. 3@a " 回放 3 次
场景二:批量格式化 JSON 键
原始:
{"name":"John","age":30,"city":"New York"}
目标(将冒号后加空格):
{"name": "John","age": 30,"city": "New York"}
步骤:
1. qa
2. f: " 跳到冒号
3. l " 右移一位
4. i <Esc> " 插入空格
5. q
6. ;. " 重复查找冒号并重复修改
场景三:将 CSV 行转为 SQL INSERT
原始:
1,Alice,alice@example.com
2,Bob,bob@example.com
3,Charlie,charlie@example.com
目标:
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com');
步骤:
qa
0
cwINSERT INTO users (id, name, email) VALUES (<Esc>
f,
x
i, '<Esc>
f,
x
i', '<Esc>
$
x
i');<Esc>
j
q
2@a
5.2.5 编辑宏寄存器
宏存储在寄存器中,可以直接编辑:
" 查看寄存器 a 的内容
:reg a
" 粘贴寄存器内容到文件中
"ap
" 编辑宏内容(在缓冲区中修改)
" 修改后选中并复制回寄存器
"ayy " 将当前行复制到寄存器 a
5.3 寄存器(Register)
5.3.1 寄存器类型
| 寄存器 | 名称 | 用法 |
|---|---|---|
"" | 无名寄存器 | 默认寄存器 |
"0 | 复制寄存器 | 仅存放 yank 操作 |
"1-"9 | 数字寄存器 | 存放删除/修改的内容 |
"- | 小删除寄存器 | 不跨行的删除 |
"a-"z | 命名寄存器 | 用户自定义 |
"+ | 系统剪贴板 | 与外部程序共享 |
"* | 选择寄存器 | X11 主选择 |
". | 上次插入 | 上次 Insert 模式输入的文本 |
": | 上次命令 | 上次执行的 Ex 命令 |
"/ | 上次搜索 | 上次搜索的模式 |
"_ | 黑洞寄存器 | 删除但不影响其他寄存器 |
5.3.2 使用寄存器
" 指定寄存器进行操作
"ayy " 复制当前行到寄存器 a
"ap " 粘贴寄存器 a 的内容
"bdd " 删除当前行到寄存器 b
"_dd " 删除当前行(不保存到任何寄存器)
" 查看所有寄存器
:reg
:reg a b c " 查看特定寄存器
" 系统剪贴板
"+y " 复制到系统剪贴板
"+p " 从系统剪贴板粘贴
"*y " 复制到 X11 选择
5.3.3 寄存器的工作流程
dd(删除行):
→ 内容存入 "1
→ "1 的旧内容移到 "2
→ ... 最多到 "9
yy(复制行):
→ 内容存入 "0(不影响 "1-"9)
p(粘贴):
→ 从 ""(无名寄存器)读取
→ 如果刚 yy:"" = "0 = 复制的内容
→ 如果刚 dd:"" = "1 = 删除的内容
注意:这是
p在dd后的行为——它粘贴的是刚删除的内容。如果dd后又dd了另一行,第一个dd的内容就在"2了。
5.4 撤销(Undo)与重做(Redo)
5.4.1 基本操作
u " 撤销
<C-r> " 重做(反撤销)
U " 撤销整行的所有修改(仅在当前行有效)
. " 重复上次 Normal 模式的修改
5.4.2 撤销树(Undo Tree)
Vim 的撤销不是线性的,而是一棵树:
原始文本
│
├── 修改 1
│ ├── 修改 2
│ │ └── 修改 3 (当前)
│ └── 修改 4 (分支)
│
└── 修改 5 (另一个分支)
" 在撤销树中导航
g- " 更旧的文本状态
g+ " 更新的文本状态
:earlier 5m " 跳到 5 分钟前的状态
:later 10m " 跳到 10 分钟后的状态
:earlier 100 " 跳到 100 次修改前
:undolist " 查看撤销历史
5.4.3 持久化撤销
" 启用持久化撤销(关闭文件后仍可撤销)
set undofile
set undodir=~/.vim/undodir " Vim
" Neovim 默认在 ~/.local/state/nvim/undo/
-- Neovim Lua 配置
vim.opt.undofile = true
vim.opt.undodir = vim.fn.stdpath("state") .. "/undo"
5.4.4 撤销块(Undo Block)
" 以下操作会创建独立的撤销块:
" 1. 从 Insert 模式返回 Normal
iHello<Esc> " 撤销一次即可删除 "Hello"
" 2. 在 Insert 模式中使用 <C-u>
iHello<C-u>World<Esc>
" 撤销会删除 "World",再撤销会恢复 "Hello"
" 3. 使用 Ctrl-G u 手动分割撤销块
iHello<C-g>uWorld<Esc>
" 撤销删除 "World",再撤销删除 "Hello"
5.5 点命令(Dot Command)
5.5.1 点命令的威力
.(点命令)重复上次 Normal 模式的修改。它只重复最后一次"修改"(从进入 Insert 到返回 Normal 的完整操作)。
" 修改一个单词
ciwnew_word<Esc>
" 移到下一个位置
w
" 重复修改
.
w
.
5.5.2 点命令与操作符结合
" 范式:[操作][移动] → . 重复
" 删除每行第一个引号前的空格
f"xx " 跳到引号,删 2 字符
j " 下一行
. " 重复删除
j
.
5.5.3 点命令的局限
" 点命令无法重复:
" - 光标移动(j, k, w, b 等)
" - Ex 命令(使用 @: 重复)
" - 搜索(使用 n/N 重复)
" - 宏录制中的命令
" 点命令可以重复:
" - 操作符 + 动作(dw, ciw 等)
" - 单字符操作(x, r{char}, ~ 等)
" - 从 Normal 进入 Insert 模式的完整输入
5.6 高级编辑技巧
5.6.1 交换字符/行
xp " 交换当前字符与下一个字符
ddp " 交换当前行与下一行
ddkP " 交换当前行与上一行
5.6.2 大小写转换
~ " 翻转当前字符大小写并移动
g~iw " 翻转单词大小写
gUiw " 单词转大写
guiw " 单词转小写
g~$ " 到行尾翻转大小写
gUU " 整行转大写
guu " 整行转小写
5.6.3 数字增减
<C-a> " 光标下的数字加 1
<C-x> " 光标下的数字减 1
5<C-a> " 加 5
3<C-x> " 减 3
" 业务场景:版本号递增
version = "1.0.3"
" 光标放在 3 上
<C-a> " → version = "1.0.4"
5.6.4 排序
" 对选中的行排序
vip:sort<CR> " 段落内排序
:%sort<CR> " 全文排序
:%sort u<CR> " 排序并去重
:%sort n<CR> " 按数字排序
:%sort i<CR> " 忽略大小写排序
:%sort! r<CR> " 逆序排序
5.6.5 格式化
=ip " 自动缩进段落
=G " 自动缩进到文件末尾
gqip " 格式化段落(textwidth 内换行)
gwip " 格式化但不移动光标
5.7 练习
练习一:批量注释
# 创建练习文件
cat > /tmp/macro-practice.txt << 'EOF'
line one
line two
line three
line four
line five
EOF
nvim /tmp/macro-practice.txt
" 步骤:用宏为每行添加 # 前缀
gg " 到文件开头
qa " 开始录制
I# <Esc> " 行首插入 '# '
j " 下一行
q " 停止录制
4@a " 回放 4 次
练习二:点命令练习
" 任务:将所有 foo 替换为 bar
" 思路:搜索 + 修改 + 点命令
/foo<CR> " 搜索 foo
cawbar<Esc> " 修改为 bar
n " 下一个匹配
. " 重复修改
n " 下一个
. " 重复
5.8 业务场景
| 场景 | 推荐方式 |
|---|---|
| 批量格式转换 | 宏录制 |
| 重复相同修改 | 点命令 . |
| 临时不保存的删除 | 黑洞寄存器 "_d |
| 跨文件复制 | 系统剪贴板 "+y |
| 版本号递增 | <C-a> / <C-x> |
| 批量排序 | :sort 命令 |
| 代码格式化 | =ip / =G |
5.9 总结
| 功能 | 核心命令 |
|---|---|
| 修改 | c cc C s S r R |
| 宏 | q{a-z} 录制 @{a-z} 回放 @@ 重复 |
| 寄存器 | "{a-z} 命名 "+ 系统 "_ 黑洞 |
| 撤销/重做 | u 撤销 <C-r> 重做 g- g+ 树导航 |
| 点命令 | . 重复上次修改 |
| 大小写 | ~ g~ gu gU |
| 数字 | <C-a> 加 <C-x> 减 |
下一步:第 06 章 - 可视模式 → 掌握 Visual、Visual Line、Visual Block 模式下的范围操作。
扩展阅读
:h repeat— 重复/点命令:h registers— 寄存器完整参考:h recording— 宏录制:h undo-branches— 撤销树- Vim Tips Wiki: Macros
- Practical Vim: Tip 66-68 — Macros