Pregunta:
¿Cómo puedo crear una macro recursiva para que solo se ejecute hasta el final de la línea?
¿O cómo ejecutar una macro recursiva solo hasta el final de la línea?
Respuesta:
Probablemente exista un método más simple, pero tal vez pueda probar 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 @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 almacenado inicialmente en la variable a
.
El comando :normal
permite escribir comandos normales (como @q
) desde el modo Ex.
Y la razón por la que el comando está envuelto en una cadena y ejecutado por el comando :execute
es para evitar que :normal
consuma (escriba) 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 principio de una línea y luego iniciar 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 registroq
para que cuando lo llame inicialmente durante la definición de la macro, no interfiera-
qq
inicia la grabación -
:let a=line('.')
almacena el número de línea actual dentro de la variablea
- Ctrl + a incrementa el número debajo del cursor
-
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ón
Una vez que haya definido su macro, si coloca el cursor en la tercera línea, presione 0
para moverlo al principio de la línea, luego @q
para reproducir la macro q
, solo debería afectar la línea actual y no las demás :
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 punto .
operador.
Esto le brindaría varios beneficios:
- no es necesario borrar el registro antes de la grabación, porque los caracteres
@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 estuviera allí - no es necesario escribir nada inusual durante la grabación, puede concentrarse en hacer una macro simple y funcional
- posibilidad de probarlo antes de hacerlo recursivo para ver cómo se comporta
Si graba su macro como de costumbre (de forma no recursiva), puede hacerla recursiva posteriormente 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 pulsaciones de teclas almacenada 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 atributo -register
pasado a :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 de <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 escribir :RecursiveMacro q
para hacer que la macro almacenada dentro del registro q
recursiva.
Puede hacer lo mismo para hacer que una macro sea 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 principio 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 tecla que contenga el registro q
actualmente:
-
let @q =
redefine el contenido del registroq
-
":let a=line('.')\r"
almacena el número de línea actual dentro de la variablea
antes de que la macro haga su trabajo
\r
es necesario para decirle a Vim que presione Enter y ejecute el comando, vea:help expr-quote
para una lista de caracteres especiales similares, -
. @q .
concatena el contenido actual del registroq
con la cadena anterior y la siguiente, -
":if line('.')==a|exe 'norm @q'|endif\r"
recuerda la macroq
con la condición de que la línea no haya cambiado
Nuevamente, para guardar algunas pulsaciones de 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 escribir :RecursiveMacroOnLine q
para hacer que la macro almacenada dentro del registro q
recursiva con la condición de que permanezca en la línea actual.
Fusionar los 2 comandos
También puede modificar :RecursiveMacro
para que cubra los 2 casos:
- hacer una macro recursiva incondicionalmente,
- hacer una macro recursiva con la condición de que permanezca en la línea actual
Para hacer esto, puede 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 diagonales inversas para hacerlo 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 a :RecursiveMacro
(debido al -nargs=1
).
Cuando se ejecute este nuevo comando, Vim 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 (la que hace que una macro sea recursiva incondicionalmente), de lo contrario, si es cero / falso, se ejecutará la segunda versión ( el que hace que una macro sea recursiva con la condición de que permanezca en la línea actual).
Entonces, 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 registroq
-
<Ca>
incrementa el número debajo del cursor -
w
mueve el cursor al siguiente número -
q
finaliza la grabación -
:RecursiveMacro q 0
hace que la macro almacenada dentro del registroq
recursiva pero solo hasta el final de la línea (debido al segundo argumento0
) -
3G
mueve el cursor a una línea arbitraria (3 por ejemplo) -
0@q
reproduce la macro recursiva desde el principio de la línea
Deberí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 :RecursiveMacro q 1
lugar de :RecursiveMacro q 0
, la macro q
se habría vuelto recursiva incondicionalmente, lo que habría dado lo siguiente buffer:
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