Macros 宏
当你不想使用 Vim 脚本或映射,或者如果你还不知道如何更优雅地完成某些任务时,录制宏是执行一次性任务的好方法,它可以帮助快速完成任务。
在 Vim 中,单词 “macros” 可能是指:
- 向寄存器中录制一系列命令(本技巧)。
- 一个把一系列输入的键扩展为一个更长的序列的映射(参见 教程 )。
- 使用 Vim 脚本编写的脚本(存储在以
.vim
为扩展名的文件中,参见:help script
)。
录制宏
每个寄存器都是由一个小写字母进行标识,即 a 至 z。
想要录制一个宏,输入:
q<letter><commands>q " 译者:q<字母><命令序列>q
想要执行该宏<number>次(默认是一次),键入:
<number>@<letter>
所以,完整的处理过程看起来这样:
qd 开始录制,并把后面的命令记录到寄存器 d 中
... 你复杂的命令序列
q 停止录制
@d 执行该宏
@@ 再次执行该宏
示例
先提供以下的数据:
one first example
two second example
three third example
four fourth example
假设你想要把这些数据转换成 Python 程序的一个字典,最终的结果如下:
data = {
'one': 'first example',
'two': 'second example',
'three': 'third example',
'four': 'forth example',
}
为了完成这个任务,可以在改变第一行的同时录制成一个宏。然后,回放该宏以实现改变其他行。当以上动作完成后,再手工输入初始的 data = {
和 最后的 }
的行。
以下演示录制一个合适宏的方法。
-
把光标放在第一行上。
-
键入
qd
(q
表示开始录制;d
就是这个寄存器名称,用于记录后面键入的键序列)。(备注:以下命令后小括号及其内容是注释) -
键入以下命令以改变第一个连续的空白成为
': '
::s/\s\+/': ' (然后点击回车)
-
键入以下命令以实现在行首插入四个空格及
'
:I ' (然后点击 Esc 键)
-
键入以下命令以实现在行尾追加
',
:A',(然后点击 Esc 键)
- 键入以下命令使光标移动到行首,然后移到下一行:
0j(或者按回车)
-
键入
q
停止录制宏。
现在光标应该停在第二行上。键入 @d
会回放该宏一次。这应该会改变第二行,且光标会移动到第三行。键入 2@@
以完成其他行。这个数字 2
表示后面动作执行两次,且 @@
表示回放最后使用过的宏。如果不使用 2@@
,你也可以键入 @d
或 @@
两次。
运行宏
使用以下映射,可以方便地执行录制在寄存器 q
中的宏:
:nnoremap <Space> @q
- 通过键入
qq
开始录制击键。 - 用
q
(如果处于插入insert模式,则先点击Esc
)结束录制。 - 通过按空格键来回放该录制的一系列击键动作。
假设你有一个宏,它操作单行上的文本,那么你可以运行该宏一次,但是,对在可视模式下选择的每一行都有效:
- 可视模式下选择一些行(例如,键入
vip
选择当前段落)。 - 键入
:normal @q
,会在选择的每一行上运行寄存器中的q
宏。
查看宏
你可以使用命令 :registers
来查看 Vim 中当前任意或全部寄存器中的内容。例如,使用 :reg
查看所有寄存器,或者 :reg a
只查看已经录制在寄存器 a
中的内容。键入 :reg abx
则会显示寄存器中 a, b 和 x
中的内容。
保存宏
保存宏以便今后使用,主要有两种方法:
最简单的方法是,如果你在非兼容模式下运行 Vim(如果你有 vimrc
文件,这是默认的),则默认就会出现这种情形。只需在你的 viminfo
选项中包含适当的文本,或者保留非兼容默认选项,Vim 就会自动地将所有寄存器写到一个文件中,并在下次启动时恢复它们。
每个寄存器的内容默认都会被保存起来,并且下一次你运行 Vim 时有效。例如,你可能录制一个宏到寄存器 a
中,然后使用 :q!
退出 Vim。当再次启动 Vim 时,你可以输入 @a
来运行寄存器中的宏 a
。
viminfo
选项可以禁用寄存器的自动保存特性。如果键入 :set vimifo?
显示的值包含(例如)<50
和 s10
,那么,在每个寄存器中将会保存最多 50 行和 10KB 内容。如果这两个项目都是零,则寄存器不会被保存。参见 :help 'viminfo'
。
第二种方法是,在 vimrc
中使用 let
命令来保存宏以备后用(尤其是,如果你认为可能无意中覆盖了这个宏,因此从 viminfo
文件中恢复出来错误的值)。
举一个非常简单的示例,假设你已经录制了一个宏到寄存器 a
中,而该宏的作用就是会跳转到行中第一个出现的字母 a
上,在常规模式下使用以下击键序列:
qa0faq
如果你想不管何时你启动 Vim,也不管你的viminfo
文件中包含什么,都想要恢复这个宏,则你需要编辑你的 vimrc
文件,且加入以下这行:
let @a='0fa'
假设你已经有了录制的宏,你可以容易地插入寄存器的内容,而不是再次键入它们。在输入上面这行时,在键入 let @a='
后,简单地击键 Ctrl-R Ctrl-R a'
,就可以把寄存器 a
中的内容插入。两次 Ctrl-R
是想插入字面量的内容,而不是像键入时那样解释它们。
在上面示例中推荐使用单引号字符('
),因为它们允许你键入的几乎都是字面内容,而在双引号("
)中,你需要转义某些字母。例如,对比 '\1\2\3"abc"'
和 "\\1\\2\\3\"abc\""
。但是,如果寄存器必须包含单引号字母,则你得使用第二个单引号来转义它。例如,如果你想要寄存器包含 "Vim's quote handling is a little trichy"
,那么,你的键入 :let @a='"Vim''s quote handling is a little tritrichy"
。
然而,请注意,使用 :let
的方法对于任何以<CR>
或 <NL>
(回车或换行)结尾的宏都不能正常工作。这是因为,正如在 :help :let-@
中所记载的那样,Vim 在这些条件下把寄存器视为面向行的 (linewise
) 。其原因是在处理复制或删除时,使通过 :let
设置的寄存器能以“正确的方式”进行,但是,它在处理录制的宏时,会遇到麻烦。可能的变通方法包含使用 setreg()
函数,或者添加 "no-op没有动作"
的命令到宏的结尾,例如,a<Esc>
。参见 vim_dev 中有关 unexpected behavior of the :let command 的细节的讨论。
宏中追加命令
如果你已经录制一个几乎正确的宏,但是你需要在该宏中添加很少的命令,那么,你可以容易地把这几个命令添加到已经存在的宏中,而不是再次录制所有的动作。只需使用大写字母来替代寄存器中的小写字母。
例如,如果你已经使用 qa...q
录制到寄存器 a
中,则你可以使用 qA...q
添加到这个宏中(而不是取代它)。
编辑宏
假设你已经录制一个宏在寄存器 a
中,且你想编辑这个宏。有几种方法可以实现它。
在命令行上可视模式
这个过程 上面 已经解释过了,但是,这里是这些步骤的总结:
- 键入
:let @a='
- 击键
Ctrl-R Ctrl-R a
插入当前寄存器a
中的内容(击键 Ctrl-R 两次以精确地插入寄存器内容)。 - 根据需要编辑这些文本。
- 追加一个单引号(
'
)完成该命令,按回车。 - 键入
reg a
查看寄存器中最新的值。 - 输入
@a
执行寄存器中a
中的命令。
注意有关以 <CR>
或 <NL>
结尾的宏的警告。
以上步骤允许你在 Vim 的命令行上编辑宏。另一种方法是把宏保存到一个文件中,就像前面的示例中使用 let @a='0fa'
所演示那样,然后编辑该文件。
缓冲区中常规及插入模式
- 跳转到一个空行,或者打开一个新的缓冲区(
:new
)。 - 键入
"ap
粘贴该寄存器的内容。 - 进入插入模式,并根据需要进行文本编辑。
- 按
Esx
返回常规模式。 - 键入
"ayy
复制修改过的宏到寄存器a
中。 - 击键
dd
,从你正在编辑的文件中删除这些粘贴而来的寄存器内容。
另见
- 录制递归宏
- 反转文本块的顺序,例如录制移动块的宏
- 使用命令行历史在没有宏的情况下重复执行 ex 命令
- 用于快速编辑结构化文本的递归重复
- 轻松回放录制键集
- 为重复工作录制键
- vim-macrobatics —— 像宏中添加一些额外功能的插件
参考资料
- :help registers
- :help recording
- :help :registers
- :help ‘viminfo’
- :help i_CTRL-R_CTRL-R
注释
在进行复杂编辑时,我通常会使用多个宏来结束编辑,而这些宏会调用其他宏。保存这样一个宏集的一个简洁方法是打开一个新文件,并将所有相应的寄存器粘贴到其中。然后将文件的第一行作为一个宏,而该宏会把其余的行加载到适当的寄存器中以便运行这些宏集。当你想恢复时,所有你需要做的就是打开这个文件,复制第一行,执行它,很快地!你的宏集已经加载,可随时使用。
以下是一个问答:
有没有可能为不同的作业设置不同的寄存器组,例如一组用于编辑 xsl 文件,其中包含各种 xsl:xxx 标签的快捷方式,另一组用于编辑 html、 xml、 sql 等?-- 匿名用户
不,这是不可能的。Vim 中的寄存器是跨越整个 Vim 实例的全局寄存器,不像映射、缩写等可以是缓冲区本地的。-- Fritzophrenic(talk)21:49, January 15, 2013 (UTC)
我应该说“如果没有一些脚本,是不可能的”。不过有 BufEnter 和 BufLeave 事件,还有 FileType 事件,它们可以与保存和恢复所需的每个命名寄存器相结合。我知道没有现存的能这样做的插件。–Fritzophrenic (talk) 21:51, January 15, 2013 (UTC)
当我编辑各种文件时,会使用 BufEnter 把 CTRL+/ 映射为用于注释所选行的命令。不同的文件类型需要不同的注释策略,因此我是用 BufEnter 来确定应该为每个文件使用哪个序列。
au BufEnter *.js :vmap ^_ :s/^/\/\// <Enter> :nohlsearch <Enter>
au BufEnter *.py :vmap ^_ :s/^/#/ <Enter> :nohlsearch <Enter>
– Michael S. Meeks 14:11, October 9, 2013 (UTC)
看起来使用 FileType 自动命令 和 buffer-local 映射可以做得更好。参见: help:map-<buffer>
。而该问题是关于录制到寄存器中的宏,而不是关于通过 :map
类型的命令映射的问题。-- Fritzophrenic (talk) 21:39, October 9, 2013 (UTC)