VIM | 掌握Vim宏:提高编辑效率的秘密武器

578 阅读8分钟

什么是宏?

宏(英语:Macro)是一种批量处理的称谓。 计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式解释器编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。

通过上面的定义,可以知道宏相当于是将重复的步骤封装成了一个函数,然后通过这个函数,可以进行更简化的处理,而在Vim中宏是一种把一系列的按键操作过程录制并保存下来,然后在必要时刻可以选择快速调用之前录制的宏操作,目的也是为了简化大量重复的操作。

Vim中的宏命令

如何开启录制宏操作

  • 通过q + [字母]的方式触发录制模式,比如按下qa,此时在Vscode下方会显示正在录制的标记:

image.png

  • 该标记的可以这样理解:
    • Recording 代表正在录制宏操作
    • @a表示正在录制的宏操作的唯一标记为a,如果之前按下的组合键是qq那么此时此处就会显示@q
  • 注意点:实际上不管是用小写字母录制宏还是以大写字母录制宏,最后都会转为小写,比如qaqA都是录制唯一标记为a的宏,在触发录制的时候下方显示的内容都是Recording @a,当然qaqA实际上是有些不同的地方的,后面会讲到。

录制宏操作与停止录制

  • 在开启录制之后,后面的操作都会被宏录制下来,直到再次输入q为止,此时按下的q代表停止录制

寄存器是什么?作用呢?

寄存器是Vim用来暂时存放文本的地方,如果在vim中进行了y(复制)、d(删除)、c(修改)一类的操作,文本则会被存储到寄存器中。 而录制了一个新的宏后,Vim会将该操作宏放在寄存器中,例如 qa 等同于录制一个存储在寄存器 a 中的宏。

如何查看录制好的宏操作

  • :reg:通过:reg可以常看录制的宏操作,并可以进行搜索

image.png 下面是如果我要看标记为g的操作宏,这个时候就可以输入g,列表下方也会快速检索出对应的结果,可以看到这里我录制的操作为ggVGd image.png

  • :reg [字母]:reg后面有一个空格):如果要查看指定的宏操作可以在该命令后再加上字母(唯一标识符),这样就可以看到录制的宏操作具体会触发哪些按键了,比如如果要看查看标记为g的操作宏,那么这个时候就可以输入:reg g,右侧会弹出对应的提示(参考下图)

image.png image.png

如何调用录制的宏操作(播放宏操作)

  • 调用(播放)指定的宏:@+[字母]
  • 调用(播放)最后一次执行的宏:@@
  • 重复执行(播放)宏:[数字]+@+[字母](数字代表了需要重复执行的次数)

如何给已录制的宏追加操作

通过q+[大写字母]:在对应的字母后面追加操作命令,比如当前的录制的宏qa对应的操作为dw,如果需要追加光标移动到行末的操作,这个时候就可以使用qA进入追加操作录制模式,给原有的宏追加操作g_,追加操作完成后记得按下q结束这次追加操作的宏录制,这就追加操作的基本流程。

追加操作小案例

比如现在有这样一段代码(|表示的是光标的位置):

let |a = 'a';
let b = 'b';
let c = 'c';
let d = 'd';
let e = 'e';
let f = 'f';
let g = 'g';
let h = 'h';
let i = 'i';
let j = 'j';

现在的需求是需要给所有声明的变量以及值前面加上_,如果是通过宏的操作来解决就可以这样写:

  1. 首先录制宏,按下qa开启录制
  2. 接下来就进行操作就可以了,输入v+gb+I+_+ctrl+[+q
    1. v:进入可视化模式
    2. gb:选中光标所在的单词相同的且未被选中的第一个单词
    3. I:再选中的内容前进行插入
    4. _:这次需要添加的_服号
    5. ctrl+[:退出输入模式

20240807\_160123.gif

  1. 现在通过10+@+a调用下刚才设置的宏,发现并不是想要的结果

20240807\_162119.gif

发生这个的原因是因为10次宏执行都是在同一行执行的,而希望的效果是执行完成后切换到下一行执行同样的效果,那么这个时候就需要给已经录制的宏追加新的操作了
  1. 给之前录制的宏追加操作,首先开启追加宏录制qA,输入j跳转到下一行,按下q结束追加宏录制,现在看看效果:

20240807\_163103.gif

宏的安全机制

对于多次执行宏操作的时候,如果出现无法执行对应操作时,比如检索不到对应的字符串的时候,会报错并停止执行。

运用这个特性,可以做一些事情,比如在批量替换字符串的情况下,文本中有多个要替换的内容,但是当前不知道具体有多少个需要替换的字符串,这个时候可以通过配合/检索并替换的操作来录制一个宏,然后通过[数字]+ @ + [字母]的方式,重复执行该宏操作,如果执行的次数超过了当前需要替换的字符串,此时Vim会因为检索不到对应的字符串报错停止替换操作,所以可以利用这个特性,我们可以在一些重复操作中无心智负担地进行全量执行,比如大概预估要重复执行的次数,然后直接执行一个绝对大于数目的次数,就可简单安全地达到目的。

如何获取已暂存的宏?

  • 方法1:"+[字母]+p:粘贴名称为[字母]的宏操作
    • "+[字母]:读取已暂存的宏操作
    • p:粘贴
  • 方法2::put +[字母],效果与方法1一样,注意:put后面需要加上空格

如何修改已暂存的宏?

录制了一个新的宏后,Vim会将该操作宏放在寄存器中,而若需要修改暂存的宏,主要需要做3步:

  1. 获取宏操作的内容
  2. 修改宏操作的内容
  3. 将修改后的内容重新放回寄存器中

对应的操作如下:

  1. 通过上文中获取已暂存的宏的方法将暂存的宏操作给打印出来
  2. 修改打印出来的宏操作
  3. 前文中提到过,寄存器的概念,操作宏实际上就是存放在寄存器中的宏,而要将东西放入到寄存器,前文中也提到过了,在vim中进行了y(复制)、d(删除)、c(修改)一类的操作,文本会被存储到寄存器中,而如果要将修改后的宏操作重新放回去也是如此,按下"+[字母]此时 Vim 会等待你接下来存入寄存器中的内容,该内容会覆盖掉原有寄存器[字母]中的内容,所以这个时候将此处修改后的内容进行复制、删除等等操作都可以实现,比较常见的就是yyywdwddcc等等

小案例

比如现在我需要删除当行内容后移动到下一行,已经录制的名为a的宏对应的操作内容为dd,如果还需要移动到下一行,我们可以选择上面追加宏操作的方式,也可以用修改已暂存的宏的方式,下面通过修改已暂存的宏的方式来试试看:

  1. 获取操作宏的内容:通过"ap,打印出dd
  2. 修改宏操作的内容:修改ddddj
  3. 将修改后的内容重新放回寄存器中:输入"a,然后在ddj那行输入dd删除改行的内容,这样就可以把ddj这个新的宏操作替换到寄存器a中了
  4. 通过@a测试下是否可行

20240807_215416.gif

该方式存在的问题

注:该问题存在于Vscode的Vim插件中,终端上的Vim不会有这个问题。 如果宏中带有功能组合键,比如从输入模式返回normal模式的时候,会输入<C-[>,此时而此前进入输入模式的时候按下了i,这个时候打印出来的宏中必定会前后分别带有i<C-[>,问题就出在这里,i会把后面的所有内容都识别为要输入的字符串,最后看到的替换后执行的宏对应的效果就是下图了:

image.png

对于该问题目前没有好的解决方案,推荐的做法就是重新录制宏。

宏的使用技巧

使用过程中,可以注意两个点:

  • 规范好光标的位置
  • 使用相对位置的操作符,比如ewft、文本对象等等,此外还少注意少用hkjl这些相当于是绝对位置,不能确保光标的所在位置,不能应对一些通用的情况。