学会偷懒——首席软件工程师教你开发小妙招


1. 重复输入命令

Vim中的 . 命令用于重复操作。我们先看一下Vim帮助手册中对 . 命令的描述:

.       Repeat last change, with count replaced with [count].        Also repeat a yank command, when the 'y' flag is        included in 'cpoptions'.  Does not repeat a        command-line command.

可以看到 . 命令的作用就是一个,“重复上次修改”。我们先需要了解一下什么可以称为一次修改。例如,当使用 x 命令删除一个字符后,“删除一个字符” 就是一次修改,再使用 . 命令就会重复这个删除操作。再如,使用 p 命令粘贴一段文本,那个这个粘贴就是一次修改,再使用 . 就会重复进行粘贴。特别要注意的是,每次从普通模式到插入模式,输入文字后再回到普通模式,算是一次修改,不论中间输入了多少文字。

实例1

删除多个单词

One Two unwanted wrong words Three Four

首先把光标移动到unwanted的词首。我们可以输入 3dw 删除后面的3个单词,也可以用 dw.. 删除一个单词,然后重复两次。区别在于,第一个命令是一次修改,而后面的操作是三次修改。如果使用 u 命令来撤销操作的话,后面的命令可以分次撤销,而且不用数单词数,即使多按了一两次 . 按键,也不是什么问题,只要按 u 命令撤销就是了。

实例 2

在每一行代码后面加一个逗

ONE = 1TWO = 2THREE = 3FOUR = 4

在第一行输入 A 在行尾插入,输入需要的逗 ,然后按<esc>退回到普通模式。在其他行使用 . 命令就可以重复在行尾插入逗 的操作。这里 A 相当于输入 $a ,移动到行尾并进入到插入模式,不同之处在于,如果使用 $a ,就变成了一次移动加一次修改,就不能直接使用 . 命令来重复操作了,因为 . 命令只会“重复上一次修改”。Vim还有其它一些和 A 类似的“移动并进入插入模式”的命令。


2. Vim宏

当需要执行的操作比较复杂时,我们把操作录制成一个宏来,然后通过播放这个宏完成重复。在普通模式下,按 q 命令加宏名称开始录制宏,再次按 q 键结束录制,然后就可以通过 @ 键播放录制的宏来进行重复操作。

实例1

曾经我们有类似这样的代码:

char *string1 = "Not enough energy.";char *string2 = "We are under attack!";...char *string9 = "Nuclear launch detected.";

从C++11开始这些代码就会产生一个警告,提示不能从字面量转换到char *指针。于是我们考虑把它们替换成更加现代化的C++风格:

std::string string1 {"Not enough energy."};std::string string2 {"We are under attack!"};...std::string string9 {"Nuclear launch detected."};

由于字符串数量较多,我们希望只对 string1 做一次修改,然后在其他行通过重复动作自动完成所有的更改。这可以通过录制下面这样一个宏来完成。

  1. string1 所在行输入 qa 开始录制宏到寄存器 a
  2. 输入 0dwx 删除 char *
  3. 输入 f= ,输入 caw{ 把 = 替换为 {
  4. 输入 $i} 在分 前插入 }
  5. 输入 j 切换到下一行。输入 q 结束宏的录制。

现在我们处于 string2 这一行。输入 @a 即可完成对改行的修改,在输入多次 . 命令完成所有行的修改。或者输入 8@a 直接完成所有修改。

这里由于我们在宏的结尾处输入了 j 命令跳到下一行,所以直接重复操作就可以完成所有行的修改。如果一个宏只有针对一行的操作而没有切换到下一行,我们可以在选择模式下选中需要修改的行,然后执行普通模式下的 @a 命令,那么就会对每一行执行操作,也可以直接完成对所有行的修改。

:'<,'>norm! @a

实例 4

我们使用GTest编写单元测试代码,经常需要对一些成员函数做mock。也就是把如下一些函数:

virtual int GetValue(int proj, int user);virtual bool IsGood(bool lie);virtual bool IsBad(void);HRESULT DoSomething(int proj, int user, bool flag);HRESULT DoSomethingElse(int user, bool flag);

变成下面这样的代码:

MOCK_METHOD2(GetValue, int (int proj, int user));MOCK_METHOD1(IsGood, bool (bool lie));MOCK_METHOD0(IsBad, bool (void));MOCK_METHOD3(DoSomething, HRESULT (int proj, int user, bool flag));MOCK_METHOD2(DoSomethingElse, HRESULT (int user, bool flag));

我们可以录制一个宏来完成操作。和前一个例子相比,这个例子要复杂一些,原因在于 MOCK_METHOD 一系列名称需要知道参数个数。

简单起见,我们通过检查函数定义中的 , 的个数来确定参数数量,比如有一个,则说明有两个参数。这里要注意的是,没有 , 可能有两种情况,一种是函数有一个参数,还有可能函数没有参数,这个需要通过检查参数表是不是 void 来确定。基于这样的想法,可以通过下面的代码获取函数参数:

if getline(".") =~ "([ t]*[:a-zA-Z_].*)"         && getline(".") !~ "([ t]*void[ t]*)"    echo count(getline("."), ",") + 1else    echo count(getline("."), ",")endif

我们可以把结果保存到寄存器里,在后面需要的时候插入寄存器里的内容。这里选择寄存器 o

:let @o=execute('if getline(".") =~ "([ t]*[:a-zA-Z_].*)" && getline(".") !~ "([ t]*void[ t]*)" | echo count(getline("."), ",") + 1 | else | echo count(getline("."), ",") | endif')

查看一下寄存器 o 的内容:

:reg oType Name Content  c  "o   ^J2

这不是我们想要的内容,多了一个 ^J (LineFeed)。需要用下面的命令把这个多余的符 删掉。

:let @o=substitute(strtrans(@o),'^@','','g')

再查看一下,现在OK了:

:reg oType Name Content  c  "o   2

知道怎么获取参数之后就可以开始录制我们需要的宏了。

  1. 在第一行输入 qm 开始录制宏到寄存器 m
  2. 输入 :s/^[ t]*(virtual)*[ t]*//g 删除行首的 virtual ,如果有的话。
  3. 输入前面说的 :let @o=execute(…) :let @o=substitute(…) 两个命令,把当前函数的参数个数保存到寄存器 o
  4. 输入 I 进入插入模式,光标停留在行首,输入 MOCK_METHOD
  5. 输入 =@o 在插入模式下插入参数数量。
  6. 输入 (<esc> ,并输入 ldw 剪切返回类型到无名寄存器。
  7. 输入 f( ,按 i 进入插入模式输入 ,<space><esc> 返回,然后输入 p 把返回类型粘贴在参数表前面。
  8. 输入 $i)<esc> 在分 前增加一个 )
  9. 输入 j 切换到下一行。输入 q 结束宏的录制。

各个步骤的结果如下:

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年3月3日
下一篇 2022年3月3日

相关推荐

输入

结果