W03: 视觉艺术与自动化原语
掌握视觉块操作的工程化应用, 深度解析宏的寄存器本质及其递归逻辑.
1. 可视模式详解
1.1 三种可视模式
v " 字符可视模式 (Character-wise)
V " 行可视模式 (Line-wise)
Ctrl + v " 块可视模式 (Block-wise)1.2 可视模式操作
o " 切换到选区另一端
O " 块模式下切换到同一行另一端
gv " 重新选择上次的选区
1v " 选择与上次相同大小的区域1.3 可视模式下的动词
选中后可使用任何操作符:
d " 删除选区
y " 复制选区
c " 修改选区
> " 缩进
< " 反缩进
~ " 切换大小写
u " 转小写
U " 转大写
J " 合并行
: " 对选区执行 Ex 命令2. 块模式的极致生产力 (Visual Block)
Ctrl + v 进入的块视觉模式是 Vim 批量修改的核武器.
2.1 列编辑: 批量插入
初始:
name: Alice
name: Bob
name: Charlie
操作: 在每行前添加 "user_"
1. 光标置于第一行 'n' 上
2. Ctrl+v 进入块模式
3. 2j 向下选择 3 行
4. I (大写) 进入插入模式
5. 输入 "user_"
6. Esc
结果:
user_name: Alice
user_name: Bob
user_name: Charlie原理: Vim 并非实时同步, 而是在按 Esc 的瞬间将指令重放到所有选中行.
2.2 列编辑: 批量追加
初始:
const a = 1
const b = 2
const c = 3
操作: 在每行末尾添加分号
1. Ctrl+v 选择第一列
2. 2j 向下扩展
3. $ 扩展到行尾 (关键!)
4. A (大写) 在末尾追加
5. 输入 ";"
6. Esc
结果:
const a = 1;
const b = 2;
const c = 3;2.3 列删除
初始:
# comment1
# comment2
# comment3
操作: 删除所有注释符号
1. 光标置于 '#' 上
2. Ctrl+v 进入块模式
3. 2j 向下选择
4. l 扩展选择 '# '
5. d 删除
结果:
comment1
comment2
comment32.4 批量替换
" 选中列后
c " 修改选中的列
r{char} " 将选中区域替换为字符3. 自动化内核: 宏 (Macros)
在 Vim 中, 宏只是存储在寄存器中的指令流字符串.
3.1 录制与回放
q{a-z} " 开始录制到寄存器 (如 qa)
q " 停止录制
@{a-z} " 回放宏 (如 @a)
@@ " 重复上次的宏
{n}@{a-z} " 执行 n 次 (如 10@a)3.2 健壮的宏设计原则
录制宏时的最佳实践:
1. 开始前重置位置
- 0 (行首)
- ^ (首个非空)
- gg (文件首)
2. 使用可重复的动作
- 用搜索 /pattern 而非绝对位置
- 用 f{char} 而非多次 l
3. 结束时定位到下一目标
- j (下一行)
- n (下一个搜索匹配)
- } (下一段)
示例: 为每行添加引号
qa " 开始录制
0 " 到行首
i"<Esc> " 插入开头引号
$ " 到行尾
a"<Esc> " 追加结尾引号
j " 下一行
q " 停止录制3.3 查看宏内容
" 查看寄存器内容
:reg a
:registers a
" 以文本形式编辑
"ap " 在当前位置粘贴寄存器 a 的内容3.4 编辑宏
如果录制的宏有错误, 不需要重录:
" 1. 新建空行, 粘贴宏内容
"ap
" 2. 编辑文本 (这就是宏的指令序列)
" 例如: 0i"^[$a"^[j
" └──────┴───┴────┴──┴─ 这些是按键序列
" 3. 选中修改后的文本
0v$
" 4. 复制回寄存器
"ay
" 注意: ^[ 代表 Esc, 输入方式是 Ctrl+v 然后 Esc3.5 寄存器追加
qA " 大写字母表示追加到已有宏
... " 追加的操作
q " 停止4. 递归宏
这是大规模文本转换的终极方案.
4.1 原理
在宏的末尾调用自己:
qa " 开始录制
... " 操作内容
@a " 调用自己 (此时 a 还在录制中)
q " 停止录制
@a " 执行 - 会无限循环直到遇到错误4.2 安全终止
递归宏在以下情况自动停止:
- 搜索失败 (如
/pattern没有找到) - 移动失败 (如
j在最后一行) - 任何操作出错
" 示例: 处理所有匹配行直到结束
qa " 开始录制
/TODO<CR> " 搜索 TODO
dd " 删除该行
@a " 递归调用
q " 停止
@a " 执行 - 删除所有含 TODO 的行4.3 对范围执行宏
:5,10 normal @a " 对第 5-10 行执行宏 a
:'<,'>normal @a " 对可视选区执行宏
:%normal @a " 对所有行执行宏
:g/pattern/normal @a " 对匹配行执行宏5. 宏的高级进阶 (Macro Pro Tips)
5.1 并行执行宏
虽然递归宏很酷, 但在多行上并行执行通常更稳健, 因为某一行报错不会影响其他行.
" 对可视选区选中的每一行执行宏 a
:'<,'>normal @a
" 对整个项目所有 Go 文件执行宏
:argdo %normal @a5.2 插件增强: tpope/vim-repeat
原生 Vim 的 . 只能重复内置命令. 安装 vim-repeat 后, 很多插件操作 (如 surround) 也可以被 . 重复.
重要的是, 它也允许你在编写插件或自定义映射时, 通过简单的一行代码让其支持 . 重复.
5.3 宏与寄存器的可视化管理
如果你的宏逻辑非常复杂, 推荐使用 tada/vim-macro 或类似的 UI 插件来查看和管理寄存器中的指令序列.
6. 实战案例
5.1 CSV 转 JSON
初始:
name,age,city
Alice,25,NYC
Bob,30,LA
目标:
{"name": "Alice", "age": "25", "city": "NYC"},
{"name": "Bob", "age": "30", "city": "LA"}," 假设光标在数据第一行
qa " 开始录制
0 " 行首
i{"name": "<Esc> " 插入
f, " 找逗号
s", "age": "<Esc> " 替换逗号
f, " 找下一个逗号
s", "city": "<Esc> " 替换
$ " 行尾
a"},<Esc> " 追加
j " 下一行
q " 停止
@a " 执行
@@ " 重复5.2 批量重命名变量
初始:
let userName = "Alice";
console.log(userName);
return userName;
目标:
let user_name = "Alice";
console.log(user_name);
return user_name;" 使用宏 + 搜索
/userName<CR> " 搜索
qa " 录制
ciwuser_name<Esc> " 修改单词
n " 下一个匹配
q " 停止
10@a " 执行 10 次
" 或者使用 :%s/userName/user_name/g7. 本周实战任务
6.1 块编辑
- 为 50 行代码批量添加
//注释前缀 - 为多行字符串添加首尾引号
- 对齐多行赋值语句的等号
6.2 宏录制
- 录制一个宏: 将
var替换为const - 练习编辑宏: 粘贴、修改、重新存储
- 使用
:normal @a对指定范围执行宏
6.3 递归宏
- 录制递归宏删除所有空行
- 使用递归宏处理多层嵌套结构
8. 思考题
- 块模式的
I和A与普通模式有何不同? - 为什么宏要以
0开始、j结束? - 如何修改一个录制错误的宏?
- 递归宏如何安全终止?
- 什么场景更适合用宏而非替换命令?
可视块和宏是 Vim 批量处理的两大支柱. 掌握它们, 你就拥有了文本自动化的能力.