终于到了我第二喜欢的vim功能了(当然了,最喜欢的是.命令)。我原本计划在介绍完.命令之后介绍宏,以便让各位小伙伴们能了解到vim对于重复操作进行的强大的优化。但是由于宏本身跟寄存器息息相关,所以还是忍痛割爱,将它放到寄存器之后。废话不多说让我们开始吧。

宏的基本使用

我们还是以一个例子进入相应的内容。

我要将下面这段代码

foot = 'foot'
ball = 'ball'
football = foot + ball

变为这样

var foot = 'foot';
var ball = 'ball';
var football = foot + ball;

通过分析它这三行其实做的都是同样的功能,即在每行的行首添加 var 关键字,然后在每行的行尾添加分号。我们可以利用之前介绍的.命令或者针对列的可视模式来处理,只是不管用哪种方法都需要至少两次操作。

问题先放在这里,让我们先了解下什么是宏,以及怎么用宏。

宏是存储在寄存器中的连续的操作指令,以便后续可以对这些指令进行回放。可以使用 q 进行录制,后面跟寄存器名称,表示将接下来的操作记录保存到这个寄存器中。例如使用 qa 表示将接下来的操作保存到 a 这个寄存器中。退出宏的录制可以直接输入 q

针对上面的例子,我们可以执行 qa 进行宏的录制,然后使用 A 在行尾进入插入模式,接着输入 ; 完成行尾的操作。然后使用 I 进入行首,然后在行首输入 var 完成这部分的工作。最后使用 q 退出宏的录制。这样就将这个宏保存在了a 寄存器.



我们可以使用 :reg a 来查看寄存器的内容。



这个内容完全是我们之前通过键盘输入到vim中的内容,只是返回到普通模式输入的是<Esc> 而这个保存的是^[ 因为宏有自己的键盘编码方式,这个方式我觉得不需要特别去查去记,自己就可以从寄存器中查到。

宏录制完成之后,可以使用 @ + 寄存器 来回放寄存器中保存的宏。在回放宏之后可以使用 @@ 来快速回放上一次回放的宏。



到现在各位小伙伴可能已经发现了,它与.命令比较类似,只能机械的执行之前执行过的内容,它无法做到智能化,例如我在录制宏的过程中使用了诸如 2w之类的命令,后面在重复的时候很有可能发生错误。这就要求我们在使用宏的过程中,尽量规范化光标移动,不要搞这种特例的形式。就像写代码不要写死一样。

这里我们还是手动执行了好多次同样的宏,宏与普通的operator 一行支持前面加数字表示重复,例如2@a 表示重复执行两次这个宏。上面的例子我们可以稍微做一下修改,即在最后添加一步将光标移动到下一行的操作——j。然后使用这个特性进行重复。



仔细点可以发现,我们执行了3次这个宏,也就是要执行3次j 操作,但是我们是在第二行执行的宏,也就是剩下的行只允许我们执行一次j 。这里虽然有问题,但是宏还是正确的对文本进行了修改。

这是因为 vim 宏在 motion 执行失败之后会终止执行,这个并不是一个 bug,而是一个特性,也就是说利用这个特性我们可以更好的使用宏。例如上述例子中,宏只执行了一次 j ,第二次执行到j 的时候出错了,于是就停下来了。这就告诉我们不用关心剩下的操作需要重复多少次,只需要给出一个足够大的数,保证已有行能正常进行修改就可以了。

我们再来看一个例子

1. one
2. two
3. three
4. four
5. five
6. six
7. seven
8. eight
9. nine
10. ten

改为

1) One
2) Two
3) Three
4) Four
5) Five
6) Six
7) Seven
8) Eight
9) Nine
10) Ten

我们可以这么归纳这个操作,从行首开始找第一个 .,然后执行替换操作将其替换为 )然后找到下一个单词,将首字母改为大写。我们可以在宏中执行 0f.r)w~j最后退出。读一下这段内容, ~之前没见过吧。之前介绍过,gUgu后面可以跟 motion表示将对应范围的字符转化为大写和小写。g~可以进行大小写反转。而这里的~直接将当前光标所在字符进行反转。



上述命令我们首先使用 0将光标至于行首,这样就规范了每行的查找操作。另外这里由于 10 有两个字符,所以这里使用 f 来查找而不仅仅使用 l往后移动一个字符,最后我们不确定. 和单词之间会不会有空格。所以这里最好是使用 w 而不是 l 。这些细节体现了我们之前说的要更加规范的移动光标。



宏录制完了之后,我们可以利用之前介绍的 motion失败会终止执行的特性,不用数需要处理多少行,直接 10@a(因为第一行已经处理了,所以这里只有9行待处理)

以并行的方式执行宏

我们将上述例子进行变更

1. one
2. two
3. three
4. four
// do something
5. five
6. six
7. seven
8. eight
9. nine
10. ten

执行上次录制的宏,发现它在第5行的位置停止了,因为在第5行中未找到 .,所以它终止了,为了继续运行,需要手动跳过,然后继续执行。假设我们有多处有注释,每次遇到问题就停下来,再手工执行,会显得比较麻烦。为了解决这个问题,我们使用vim提供的另外一种执行宏的方式——以并行的方式执行。

重新录制宏,与之前相比,只需要将j这个操作给去除掉。然后使用针对行的可视模式,选中待处理行,然后针对这些选中行来执行宏。

我们在这里来审视一下这两种方式,并行方式需要提供重复次数,它是第一次执行完了接着执行下一次,下一次的执行依赖于上一次成功的执行。并行则不然,并行是针对选中部分,同时执行一个宏操作。即使中间有错也不影响其他行的运行。



给宏追加命令

还是上面的例子,假设在录制好了宏之后发现我们少了一个j,使用串行话的方式无法顺利执行。这种情况下不需要重新录制宏,只需要在对应寄存器中添加一条指令。

这里补充一下寄存器相关知识。在上一篇介绍寄存器的时候我们只演示了使用小写字母的寄存器,没有提到大写字母的寄存器。根据之前的惯例,大写字母与小写字母都可以使用,大写字母的功能比小写字母要强,例如大写的标签标示全局,小写的只能用于单个文件。这里大写的寄存器与小写的寄存器是同一个寄存器,使用大写时我们可以对寄存器内容进行追加操作。

宏是保存在寄存器中的,q 后面加字母表示宏的内容保存在哪个寄存器中,说到这里,聪明的你已经反应过来该如何将命令追加到寄存器中了。那就是使用 q+大写字母

针对并行操作的例子,假设已经录好其他操作只差一个j 了,我们可以使用 qA 进行追加,然后添加 j 操作即可

追加前宏的内容如下:





添加完成之后,宏变成了如下内容



后面就可以以串行的方式执行这个宏了

配合文件参数列表使用宏

之前介绍过文件参数列表,即使用 :args 可以对文件进行分组,各位小伙伴可能只知道这个,但是没找到它的使用场景。也不知道vim提供这个功能有什么用处。在这里我们就来看看它的一个使用场景。

我们还是以之前的 neovim 配置文件的工程为例,我要在每个lua文件中添加一行注释 --this is add by vim macro 。打开一个 lua 文件之后,使用 :args **/*.lua 来将每个 lua 文件加入到参数列表中。然后随意打开一个 lua 文件,在录制宏的时候执行 ggO<ESC>S--this is add by vim macro 然后退出。这里还是贯彻了前面说的要是移动更加规范,我们先用 gg 移动到第一行,以便能准确的在首行插入内容。由于在 lua 文件中有注释的话使用 O 添加一行的时候它会自动添加一个注释。但是不能确保所有的 lua 文件在行首都有注释,所以我们先使用 S 删除一行并进入插入模式。当然通过配置也可以取消这个特性,等介绍到文件类型的时候再来讨论这个。

此时文件已经发生了变化,如果我们直接执行宏的话,之前录制时修改的文件将会两次执行相同的命令,所以这里不能保存,可以执行 :edit! 放弃本次修改,或者如果已经修改了的,可以执行u进行回退。

结合之前介绍的在命令模式中执行普通模式的命令,可以使用 :argdo normal @aargdo 表示循环对参数列表中的每个文件执行相同的操作。

录制宏:



添加参数列表:



执行宏

上述的操作方式采用的是并行的执行宏,我们可以对其进行一些修改,让其支持串行的方式。

还记得之前介绍的怎么遍历参数列表吗,不记得也没关系。我们可以使用 :next 来访问下一个,:prev 来访问上一个。配合之前的命令可以使用 ggO<Esc>S--this is add by vim macro:next。我们无法知道参数列表中到底有多少个文件,但是可以利用失败即终止这个特性输入一个足够大的数字即可,例如 100@a即可。这样就省去了执行命令模式中命令的相关操作。

对比两个宏发现我只需要在之前的宏后面添加一个 :next 指令即可,所以这里就直接执行了 qA:nextq

对比上面两种方式发现,并行执行的时候中间某个缓冲区如果出错并不影响其他缓冲区的执行,这就给我们排查造成了一定的问题,一旦出错我们不得不打开每一个缓冲区查看执行的结果来找到出错的位置。而串行则会停在出错的位置,我们只要针对出错的部分做一定的调整,然后继续执行就好了。而且这个例子中列表参数并不会循环遍历,也就不用担心之前修改过的内容又被修改。

编辑宏内容

上面我们说到宏是保存在寄存器中的一组操作指令,既然可以利用往寄存器中追加内容的方式往宏中追加指令,那么是不是我只要更新了寄存器中的内容,在执行宏的时候命令就会改变呢?如果你能这么想,那么恭喜你都会抢答了,而且答对了!

还是以上面那个添加注释的例子为例,假设我之前忘记了删除新添加的 --,也就是我录入的宏变成了 ggOthis is add by vim macro 我们会发现在第一行是注释的文本中它的表现是正常的,但是第一行不是注释,添加的就是有问题的,例如 nvim-config/lua/config/auto-session.lua。我们发现了这个问题需要对这个宏进行修改。

首先我们需要将 宏从寄存器中放到编辑器中,这就要使用 :put a取出寄存器中的内容,你可能会疑惑为什么不用 "ap 呢,这是因为 p 命令默认会将寄存器中的内容放到光标所在位置的后面,而 :put 则会直接放到下一行,所以这里还是放入到当前命令之后要好。接着修改一下这个宏。在对应位置加上 S 这个操作,最后使用 0d$ 从行首粘贴到行尾,注意这里尽量不要使用 dd,它会连带着换行符一块进行粘贴,可能会破坏宏的指令。最后我们可以先删除之前粘贴的一行,再重新执行这个宏

最后的叨叨

宏是vim提供的很有用的功能,希望我通过本文让各位小伙伴对它有一个初步的认识,想要用好宏这个强大的工具还是需要花大量的时间去学习研究的。vim这个工具也是常用常学常新的,时不时你就能发现自己当初不知道的内容,就像有小伙伴给我留言给我介绍了一些我之前不知道的命令,在这里对所有给我留言的小伙伴表示感谢。vim的指令实在太多了,指望我把所有好用的一一介绍,文章的篇幅就显的太长了,这里我就不加了,各位小伙伴有什么好用的方式也可以留言给其他不会的小伙伴一个学习的机会。大家一起共同进步。谢谢大家

vim 从嫌弃到依赖(16)——宏的更多相关文章

  1. Vim使用技巧(5) -- 宏的录制与使用

    想象一个场景,我们怎么快速把下面的所有链接都加上双引号?可能你手速快,可以很快的加完,但是如果链接有上万个呢?你如何在十秒以内加完? 这时候就需要用到“宏”(其实除了宏vim还有其它方法加上双引号,这 ...

  2. Installing Vim 8.0 on Ubuntu 16.04 and Linux Mint 18

    sudo add-apt-repository ppa:jonathonf/vim sudo apt update sudo apt install vim uninstall sudo apt re ...

  3. vim重复操作的宏录制

    在编辑某个文件的时候,可能会出现需要对某种特定的操作进行许多次的情况,以编辑下面的文件为例: ;==================================================== ...

  4. 【转】所需即所获:像 IDE 一样使用 vim

    转自:  https://github.com/yangyangwithgnu/use_vim_as_ide 所需即所获:像 IDE 一样使用 vim yangyangwithgnu@yeah.net ...

  5. VIM插件攻略

    工欲善其事,必先利其器.一个强大的开发环境可以大大提高工作效率.好吧,我知道这是废话...不过,我想一定有很多跟我一样打算进入Linux平台开发的新手,一开始都为找不到一个像Windows下的VS那样 ...

  6. iOS中常见的一些宏

    原文链接 1.处理NSLog事件(开发者模式打印,发布者模式不打印) #ifdef DEBUG #define NSLog(FORMAT, ...) fprintf(stderr,"%s:% ...

  7. iOS - 常用的宏定义

    1.处理NSLog事件(开发者模式打印,发布者模式不打印) 1 2 3 4 5   #ifdef DEBUG   #define NSLog(FORMAT, ...) fprintf(stderr,& ...

  8. 0050 Linux VIM 命令

    1.  模式切换 vim的模式 $ vi filename 进入normal 模式,这是命令模式,用于执行大多数常用的编辑命令,不能输入 敲i 进入 insert 模式,这是正常的编辑模式,按Esc ...

  9. ctagst简单应用,将Vim改造:Ctags,Taglist,Cscope,OmniCppComplete,SuperTab,Winmanager,NERDTree,MiniBufExplorer,vimrc

    vim + ctags $ ctags #给同一个目录下的所有文件建立tags 这时在tags文件所在的目录下打开源文件阅读,vim就会自动调用tags文件.如果tags文件不在当前目录下,能在命令模 ...

  10. 【转】将Vim改造为强大的IDE—Vim集成Ctags/Taglist/Cscope/Winmanager/NERDTree/OmniCppComplete(有图有真相)

    原文网址:http://blog.csdn.net/bokee/article/details/6633193 工欲善其事,必先利其器.一个强大的开发环境可以大大提高工作效率.好吧,我知道这是废话.. ...

随机推荐

  1. 使用 FHE 实现加密大语言模型

    近来,大语言模型 (LLM) 已被证明是提高编程.内容生成.文本分析.网络搜索及远程学习等诸多领域生产力的可靠工具. 大语言模型对用户隐私的影响 尽管 LLM 很有吸引力,但如何保护好 输入给这些模型 ...

  2. 机器学习周刊第五期:一个离谱的数据可视化Python库、可交互式动画学概率统计、机器学习最全文档、快速部署机器学习应用的开源项目、Redis 之父的最新文章

    date: 2024/01/08 这个网站用可视化的方式讲解概率和统计基础知识,很多内容还是可交互的,非常生动形象. 大家好,欢迎收看第五期机器学习周刊 本期介绍7个内容,涉及Python.概率统计. ...

  3. DNS--智能地址解析(view视图)

    域名:xinenhui.com DNS服务器:192.168.198.128 DNS1:192.168.198.129 DNS2:192.168.198.146 1 简介 使客户端就近访问DNS服务器 ...

  4. js将页面上取得的元素坐标转换为电脑屏幕坐标

    代码: <!DOCTYPE html> <html> <head> <title>计算屏幕坐标</title> <meta chars ...

  5. @Scheduled cron 定时任务表达式含义,及* ?的区别

    好多网友对@Scheduled cron表达式含义做了阐述,个人认为很多对于 * ?的说明不够具体也不算准确,借此本文特别对 * ?做一下说明. cron格式:[秒数][分钟][小时][日期][月份] ...

  6. 类加载机制-深入理解jvm

    一.什么是类的加载: 如上图,java文件通过编译器变成了.class文件,接下来类加载器又将这些.class文件加载到JVM中.其中类装载器的作用其实就是类的加载. 二.原理 (类的加载过程及其最终 ...

  7. SpringCloud学习 系列六、服务平滑离线

    系列导航 SpringCloud学习 系列一. 前言-为什么要学习微服务 SpringCloud学习 系列二. 简介 SpringCloud学习 系列三. 创建一个没有使用springCloud的服务 ...

  8. nextTick用法

  9. Visual Studio Code如何校验yaml格式文件

    1.yaml格式校验快捷键:Shift+Alt+F默认没有安装,这时候点击安装: 2.选择YAML Language Support by Red Hat   3.安装完成后,自动开启校验,当格式错误 ...

  10. docker容器常用操作

    1.查看运行容器 docker ps:  2.查看所有容器 docker ps -a:  3.查看容器的日志 docker logs 容器名称/容器ID: 4.运行镜像 docker run --na ...