Respuestas:
Probablemente haya un método más simple, pero tal vez podría intentar lo siguiente.
Digamos que usará el registro q
para grabar su macro recursiva.
Al comienzo de la grabación, escriba:
:let a = line('.')
Luego, al final de la grabación, en lugar de presionar @q
para hacer que la macro sea recursiva, escriba el siguiente comando:
:if line('.') == a | exe 'norm @q' | endif
Finalmente finalice la grabación de la macro con q
.
El último comando que escribió reproducirá la macro q
( exe 'norm @q'
), pero solo si el número de línea actual ( line('.')
) es el mismo que el inicialmente almacenado en la variable a
.
El :normal
comando le permite escribir comandos normales (como @q
) desde el modo Ex.
Y la razón por la cual el comando está envuelto en una cadena y ejecutado por el comando :execute
es para evitar :normal
consumir (escribir) el resto del comando ( |endif
).
Ejemplo de uso
Digamos que tiene el siguiente búfer:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Y desea incrementar todos los números de una línea arbitraria con una macro recursiva.
Puede escribir 0
para mover el cursor al comienzo de una línea y luego comenzar la grabación de la macro:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqq
borra el contenido del registro q
para que cuando lo llame inicialmente durante la definición de la macro, no interfieraqq
comienza la grabación:let a=line('.')
almacena el número de línea actual dentro de la variable a
w
mueve el cursor al siguiente número:if line('.')==a|exe 'norm @q'|endif
recuerda la macro pero solo si el número de línea no cambióq
detiene la grabaciónUna vez que haya definido su macro, si coloca su cursor en la tercera línea, presione 0
para moverlo al comienzo de la línea, luego presione @q
para reproducir la macro q
, solo debería afectar a la línea actual y no a las otras:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Hacer una macro recursiva después de la grabación
Si lo desea, puede hacer que su macro sea recursiva después de su grabación utilizando el hecho de que está almacenada en una cadena dentro de un registro y que puede concatenar dos cadenas con el .
operador de punto .
Esto le daría varios beneficios:
@q
se agregarán en la macro después de que se haya definido, y después de que haya sobrescrito cualquier contenido antiguo que haya allíSi graba su macro como de costumbre (no recursivamente), puede hacerlo recursivo después con el siguiente comando:
let @q = @q . "@q"
O incluso más corto: let @q .= "@q"
.=
es un operador que permite agregar una cadena a otra.
Esto debería agregar los 2 caracteres @q
al final de la secuencia de teclas almacenadas dentro del registro q
. También puede definir un comando personalizado:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Define el comando :RecursiveMacro
que espera el nombre de un registro como argumento (debido al -register
atributo pasado :command
).
Es el mismo comando que antes, la única diferencia es que reemplaza cada aparición de q
con <reg>
. Cuando se ejecute el comando, Vim expandirá automáticamente cada aparición <reg>
con el nombre de registro que proporcionó.
Ahora, todo lo que tiene que hacer es grabar su macro como de costumbre (no recursivamente), luego escriba :RecursiveMacro q
para que la macro almacenada dentro del registro sea q
recursiva.
Podría hacer lo mismo para hacer una macro recursiva con la condición de que permanezca en la línea actual:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Es exactamente lo mismo que se describe al comienzo de la publicación, excepto que esta vez lo haces después de la grabación. Simplemente concatena dos cadenas, una antes y otra después de las pulsaciones de teclas que el q
registro contiene actualmente:
let @q =
redefine el contenido del registro q
":let a=line('.')\r"
almacena el número de línea actual dentro de la variable a
antes de que la macro haga su trabajo, \r
es necesario decirle a Vim que presione Entrar y ejecute el comando, vea :help expr-quote
una lista de caracteres especiales similares, . @q .
concatena el contenido actual del q
registro con la cadena anterior y la siguiente,":if line('.')==a|exe 'norm @q'|endif\r"
recuerda la macro q
con la condición de que la línea no haya cambiadoNuevamente, para guardar algunas teclas, puede automatizar el proceso definiendo el siguiente comando personalizado:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
Y nuevamente, todo lo que tiene que hacer es grabar su macro como de costumbre (no recursivamente), luego escriba :RecursiveMacroOnLine q
para que la macro almacenada dentro del registro sea q
recursiva con la condición de que permanezca en la línea actual.
Combina los 2 comandos
También puede ajustar :RecursiveMacro
para que cubra los 2 casos:
Para hacer esto, podría pasar un segundo argumento a :RecursiveMacro
. Este último simplemente probaría su valor y, dependiendo del valor, ejecutaría uno de los 2 comandos anteriores. Daría algo como esto:
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
O (usando continuaciones de línea / barras invertidas para que sea un poco más legible):
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
Es lo mismo que antes, excepto que esta vez debe proporcionar un segundo argumento para :RecursiveMacro
(debido al -nargs=1
atributo).
Cuando se ejecute este nuevo comando, Vim se expandirá automáticamente <args>
con el valor que proporcionó.
Si este segundo argumento es distinto de cero / verdadero ( if <args>
), se ejecutará la primera versión del comando (el que hace que una macro sea recursiva incondicionalmente), de lo contrario, si es cero / falso, se ejecutará la segunda versión (la que hace una macro recursiva con la condición de que permanezca en la línea actual).
Volviendo al ejemplo anterior, daría lo siguiente:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq
comienza la grabación de una macro dentro del registro q
<C-a>
incrementa el número debajo del cursorw
mueve el cursor al siguiente númeroq
termina la grabación:RecursiveMacro q 0
hace que la macro almacenada dentro del registro sea q
recursiva pero solo hasta el final de la línea (debido al segundo argumento 0
)3G
mueve el cursor a una línea arbitraria (3 por ejemplo)0@q
reproduce la macro recursiva desde el principio de la líneaDebería dar el mismo resultado que antes:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Pero esta vez no tuvo que escribir los comandos de distracción durante la grabación de su macro, simplemente podría concentrarse en hacer uno que funcione.
Y durante el paso 5, si hubiera pasado un argumento distinto de cero al comando, es decir, si hubiera escrito en :RecursiveMacro q 1
lugar de :RecursiveMacro q 0
, la macro q
se habría vuelto recursiva incondicionalmente, lo que habría dado el siguiente búfer:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Esta vez, la macro no se habría detenido al final de la tercera línea, sino al final del búfer.
Para más información, ver:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10
, obtengo en 2 3 4 5 6 7 8 9 10 12
lugar de 2 3 4 5 6 7 8 9 10 11
. No sé por qué, tal vez escribí algo mal. De todos modos, parece más sofisticado que mi enfoque simple, e involucra expresiones regulares para describir dónde la macro debe mover el cursor, así como una lista de ubicaciones que nunca he visto utilizada de esta manera. ¡Me gusta mucho!
\d\+
para describir números de varios dígitos.
:lv ...
comando, el :lla
comando se puede usar para saltar al último partido y el :lp
comando se puede usar para avanzar sobre los partidos en orden inverso.
Una macro recursiva se detendrá tan pronto como encuentre un comando que falle. Por lo tanto, para detenerse al final de una línea, necesita un comando que fallará al final de la línea.
Por defecto *, el l
comando es dicho comando, por lo que puede usarlo para detener una macro recursiva. Si el cursor no está al final de la línea, entonces solo necesita moverlo hacia atrás con el comando h
.
Entonces, usando el mismo ejemplo de macro que saginaw :
qqqqq<c-a>lhw@qq
Desglosado:
qqq
: Borrar el registro q,qq
: Comience a grabar una macro en el q
registro,<c-a>
: Incrementa el número debajo del cursor,lh
: Si estamos al final de la línea, aborte la macro. De lo contrario, no hagas nada.w
: Avanza a la siguiente palabra en la línea.@q
: Recurseq
: Para de grabar.Luego puede ejecutar la macro con el mismo 0@q
comando descrito por saginaw.
* La 'whichwrap'
opción le permite definir qué teclas de movimiento se ajustarán a la siguiente línea cuando se encuentre al principio o al final de una línea (Ver :help 'whichwrap'
). Si ha l
configurado esta opción, interrumpirá la solución descrita anteriormente.
Sin embargo, lo más probable es que sólo se utiliza uno de los comandos del modo normal de tres por defecto para el avance de un solo carácter ( <Space>
, l
y <Right>
), por lo que si usted ha l
incluido en su 'whichwrap'
entorno, puede quitar uno que no utiliza de la 'whichwrap'
opción, por ejemplo, para <Space>
:
:set whichwrap-=s
Luego puede reemplazar el l
comando en el paso 4 de la macro con un <Space>
comando.
virtualedit=onemore
interferirá con el uso l
para detectar el final de línea, aunque no tan severamente como whichwrap=l
.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q
aumentará todos los números en la línea 3. ¿Quizás haya una manera de hacer que esta solución sea menos frágil?