或如何运行递归宏,直到行尾?
#1 楼
也许有一个更简单的方法,但是也许您可以尝试以下方法。假设您将使用寄存器
q
来记录您的递归宏。在记录的开始,键入:
:let a = line('.')
然后,在记录的最后,而不是敲击
@q
来使宏递归,请键入以下命令::if line('.') == a | exe 'norm @q' | endif
最后用
q
结束宏的记录。您键入的最后一条命令将重播宏
q
(exe 'norm @q'
),但前提是当前行号(line('.')
)与最初存储在变量a
中的相同。:normal
命令允许您从Ex模式键入普通命令(如@q
)。以及该命令的原因被包装为字符串并由命令
:execute
执行,以防止:normal
消耗(键入)该命令的其余部分(|endif
)。用法示例。
假设您具有以下缓冲区:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
您想使用递归宏从任意行中递增所有数字。
您可以键入
0
将光标移至行首,然后开始录制宏:qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqq
清除寄存器q
的内容,以便当您在宏定义期间首次调用它时,它不会干扰qq
开始记录:let a=line('.')
存储变量a
中的当前行号Ctrl + a增大光标下方的数字
w
将光标移至下一个数字:if line('.')==a|exe 'norm @q'|endif
调用宏,但前提是行号未更改
q
停止记录定义宏后,如果将光标放在第三行,请按
0
将其移至行的开头,然后按@q
以重播宏q
,它只会影响当前行,而不会影响其他行: 1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
记录后进行宏递归
如果需要,可以在使用宏进行记录后递归它存储在寄存器中的字符串中,并且您可以用点
.
运算符连接两个字符串。这将为您带来一些好处:
无需在录制之前清除寄存器,因为在定义宏之后将在宏中添加字符
@q
,并且在覆盖了所有旧内容之后在录制期间无需键入任何异常,您可以集中精力制作一个简单的可运行的宏
,在递归查看之前对其进行测试它的行为方式
如果像往常一样(非递归地)记录宏,则可以使用以下命令使其递归:
let @q = @q . "@q"
甚至更短:
let @q .= "@q"
.=
是允许将字符串追加到另一个字符串的运算符。这应该在存储在寄存器
@q
内的击键序列的最后添加2个字符q
。 您还可以定义一个自定义命令:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
它定义命令
:RecursiveMacro
,该命令等待寄存器名称作为参数(因为-register
属性传递给:command
)。与以前的命令相同,唯一的区别是您将每次出现的
q
替换为<reg>
。当执行该命令时,Vim会使用您提供的寄存器名称自动扩展<reg>
的每次出现。现在,您所需要做的就是照常记录您的宏(非递归),然后键入
:RecursiveMacro q
以使该宏在递归寄存器q
中的存储。您可以执行相同的操作使其在当前行上保留的条件下进行宏递归:录制之后。您只需串联两个字符串,然后按一下
q
寄存器当前包含的任何按键,就可以在字符串之前和之后进行连接:let @q =
重新定义了寄存器q
的内容":let a=line('.')\r"
在宏工作之前将当前行号存储在变量a
中。\r
对于告诉Vim按Enter并执行命令是必需的,有关类似特殊字符的列表,请参见:help expr-quote
。. @q .
将q
寄存器的当前内容与前一个字符串和下一个字符串连接在一起,":if line('.')==a|exe 'norm @q'|endif\r"
在行没有变化的情况下调用宏q
再次,要保存一些击键,您可以通过定义以下定制命令来自动执行该过程:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
再次,您要做的就是将宏记录为通常(非递归),然后键入
:RecursiveMacroOnLine q
以使存储在寄存器q
中的宏重新出现合并两个命令
还可以调整
:RecursiveMacro
,使其涵盖2种情况:无条件进行宏递归,
在当前行停留的条件下进行宏递归
为此,您可以传递第二个
:RecursiveMacro
的参数。后者将仅测试其值,并根据该值执行前两个命令之一。它会给出这样的内容:command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
或(使用换行符/反斜线使其更具可读性):
command! -register -nargs=1 RecursiveMacro if <args> | let @<reg> .= "@<reg>" | else | let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r" | endif
与以前相同,只是这次您必须提供一个第二个参数
:RecursiveMacro
(由于-nargs=1
属性)。执行此新命令时,Vim会自动使用您提供的值扩展
<args>
。如果第二个参数非零/ true(
if <args>
),将执行该命令的第一个版本(无条件地使宏递归的命令),否则,如果该命令为零/ false,则将执行第二个版本(在保持当前行的条件下使宏递归的命令) 。所以回到上一个示例,它会给出以下内容:
command! -register -nargs=1 RecursiveMacro
\ if <args> |
\ let @<reg> .= "@<reg>" |
\ else |
\ let @<reg> = ":let a = line('.')\r" .
\ @<reg> .
\ ":if line('.')==a | exe 'norm @<reg>' | endif\r" |
\ endif
qq
开始记录内部寄存器q
中的宏<C-a>
递增光标下的数字w
将光标移至ne xt编号q
结束记录:RecursiveMacro q 0
使存储在寄存器q
中的宏递归,但仅直到行尾为止(由于第二个参数0
)3G
将光标移动到任意行(例如3)0@q
从行的开头重播递归宏它应该具有与以前相同的结果:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
但是这次您不必在录制宏时键入分散注意力的命令,您只需关注
在第5步中,如果您向命令传递了一个非零参数,也就是说,如果您键入
:RecursiveMacro q 1
而不是:RecursiveMacro q 0
,则宏q
将无条件地递归,这将提供以下缓冲区:1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
这次宏不会在第三行的末尾而是在缓冲区的末尾停止。
有关更多信息,请参见:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
#2 楼
递归宏将在遇到失败的命令后立即停止。因此,要在一行的结尾处停止,您需要一个将在该行的结尾处失败的命令。默认情况下,
l
命令就是这样的命令,因此您可以使用它停止递归宏。如果光标不在该行的末尾,则只需使用命令h
将其向后移。因此,使用与saginaw相同的示例宏:
qqqqq<c-a>lhw@qq
细分为:
qqq
:清除q寄存器,qq
:开始记录q
寄存器中的一个宏,<c-a>
:增加光标下方的数字,lh
:如果我们在行的末尾,则中止该宏。否则,什么也不做。w
:前进到行中的下一个单词。@q
:递归q
:停止记录。然后可以使用与saginaw描述的相同的
0@q
命令运行宏。*
'whichwrap'
选项可让您定义将哪些移动键环绕到在一行的开头或结尾时显示下一行(请参阅:help 'whichwrap'
)。如果您在此选项中设置了l
,则将破坏上述解决方案。但是,很可能只使用三个默认的普通模式命令之一来前进一个字符(
<Space>
,l
和<Right>
),因此,如果您在l
设置中包含'whichwrap'
,则可以从'whichwrap'
选项中删除不需要的一项,例如对于<Space>
::set whichwrap-=s
然后可以使用
l
命令替换宏的第4步中的<Space>
命令。评论
还要注意,设置virtualedit = one会干扰使用l来检测行尾,尽管不如whichwrap = 1那样严重。
–凯文
19年7月22日在18:02
@凯文好点!我将更新答案以提及“ ve”
–丰富
19年7月23日在10:45
评论
只要宏不更改匹配项的位置(例如,位置),就可以使用位置列表在宏中进行搜索匹配。 :lv / \%3l \ d / g%
– djjcast
16年2月10日在7:45
@djjcast您可以将其发布为答案,我已经尝试过了,它确实很棒。只有一种我不理解的情况,当我在下面的行1 2 3 4 5 6 7 8 9 10中执行宏时,我得到2 3 4 5 6 7 8 9 10 12而不是2 3 4 5 6 7 8 9 10 11.我不知道为什么,也许我输错了什么。无论如何,它似乎比我的简单方法复杂得多,它涉及到正则表达式来描述宏应将光标移至何处,以及一个我从未见过的以这种方式使用过的位置列表。我很喜欢!
– saginaw
16 Feb 10'在8:07
@djjcast对不起,我刚刚明白了,问题出在我的正则表达式中,我应该使用\ d \ +来描述多个数字。
– saginaw
16年2月10日在8:19
@djjcast啊,现在我明白了您所说的宏不应该更改比赛位置的意思。但是我不知道如何解决这个问题。我唯一的想法是从宏内部更新位置列表,但我不习惯该位置列表,这对我来说太复杂了,非常抱歉。
– saginaw
16年2月10日在10:16
@saginaw以相反的顺序遍历比赛似乎可以在大多数情况下解决问题,因为似乎宏更改先前比赛的位置的可能性较小。因此,在:lv ...命令之后,:lla命令可用于跳至最后一个匹配项,而:lp命令可用于按相反顺序进行匹配。
– djjcast
16年2月10日在14:03