Risposte:
Probabilmente esiste un metodo più semplice, ma forse potresti provare quanto segue.
Supponiamo che utilizzerai il registro q
per registrare la tua macro ricorsiva.
All'inizio della registrazione, digitare:
:let a = line('.')
Quindi, alla fine della registrazione, invece di premere @q
per rendere la macro ricorsiva, digitare il comando seguente:
:if line('.') == a | exe 'norm @q' | endif
Finalmente termina la registrazione della macro con q
.
L'ultimo comando digitato riprodurrà la macro q
( exe 'norm @q'
) ma solo se il numero di riga corrente ( line('.')
) è lo stesso di quello inizialmente memorizzato nella variabile a
.
Il :normal
comando consente di digitare normali comandi (come @q
) dalla modalità Ex.
E il motivo per cui il comando è racchiuso in una stringa ed eseguito dal comando :execute
è impedire :normal
di consumare (digitare) il resto del comando ( |endif
).
Esempio di utilizzo
Supponiamo che tu abbia il seguente buffer:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
E vuoi incrementare tutti i numeri da una linea arbitraria con una macro ricorsiva.
È possibile digitare 0
per spostare il cursore all'inizio di una riga, quindi avviare la registrazione della macro:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqq
cancella il contenuto del registro in q
modo che quando lo si chiama inizialmente durante la definizione della macro, non interferiràqq
avvia la registrazione:let a=line('.')
memorizza il numero di riga corrente all'interno della variabile a
w
sposta il cursore sul numero successivo:if line('.')==a|exe 'norm @q'|endif
richiama la macro ma solo se il numero di riga non è cambiatoq
interrompe la registrazioneDopo aver definito la macro, se si posiziona il cursore sulla terza riga, premere 0
per spostarlo all'inizio della riga, quindi premere @q
per riprodurre la macro q
, dovrebbe interessare solo la riga corrente e non le altre:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Crea una macro ricorsiva dopo la registrazione
Se vuoi, puoi rendere la tua macro ricorsiva dopo la sua registrazione usando il fatto che è memorizzata in una stringa all'interno di un registro e che puoi concatenare due stringhe con l' .
operatore punto .
Questo ti darebbe diversi vantaggi:
@q
verranno aggiunti nella macro dopo che è stata definita e dopo aver sovrascritto qualsiasi contenuto precedente fosse presenteSe registri la tua macro come al solito (in modo non ricorsivo), puoi successivamente renderla ricorsiva con il seguente comando:
let @q = @q . "@q"
O ancora più breve: let @q .= "@q"
.=
è un operatore che consente di aggiungere una stringa a un'altra.
Ciò dovrebbe aggiungere i 2 caratteri @q
alla fine della sequenza di sequenze di tasti memorizzata nel registro q
. È inoltre possibile definire un comando personalizzato:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Definisce il comando :RecursiveMacro
che attende il nome di un registro come argomento (a causa -register
dell'attributo passato a :command
).
È lo stesso comando di prima, l'unica differenza è che sostituisci ogni ricorrenza di q
con <reg>
. Quando il comando verrà eseguito, Vim espande automaticamente ogni ricorrenza di <reg>
con il nome del registro fornito.
Ora, tutto quello che devi fare è registrare la tua macro come al solito (non ricorsivamente), quindi digitare :RecursiveMacro q
per rendere q
ricorsiva la macro memorizzata nel registro .
Puoi fare la stessa cosa per rendere una macro ricorsiva a condizione che rimanga sulla riga corrente:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
È esattamente la stessa cosa descritta all'inizio del post, tranne che questa volta lo fai dopo la registrazione. Basta concatenare due stringhe, una prima e una dopo le sequenze di tasti q
attualmente contenute nel registro:
let @q =
ridefinisce il contenuto del registro q
":let a=line('.')\r"
memorizza il numero di riga corrente all'interno della variabile a
prima che la macro faccia il suo lavoro \r
è necessario per dire a Vim di premere Invio ed eseguire il comando, vedere :help expr-quote
un elenco di caratteri speciali simili, . @q .
concatena il contenuto corrente del q
registro con la stringa precedente e quella successiva,":if line('.')==a|exe 'norm @q'|endif\r"
richiama la macro q
a condizione che la linea non sia cambiataAncora una volta, per salvare alcune sequenze di tasti, è possibile automatizzare il processo definendo il seguente comando personalizzato:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
E ancora, tutto ciò che devi fare è registrare la tua macro come al solito (in modo non ricorsivo), quindi digitare :RecursiveMacroOnLine q
per rendere q
ricorsiva la macro memorizzata nel registro a condizione che rimanga sulla riga corrente.
Unisci i 2 comandi
Puoi anche modificare in :RecursiveMacro
modo che copra i 2 casi:
Per fare questo, potresti passare un secondo argomento a :RecursiveMacro
. Quest'ultimo testerebbe semplicemente il suo valore e, a seconda del valore, eseguirà uno dei 2 comandi precedenti. Darebbe qualcosa del genere:
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
Oppure (usando continuazioni di riga / barre rovesciate per renderlo un po 'più leggibile):
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
È lo stesso di prima, tranne che questa volta è necessario fornire un secondo argomento a :RecursiveMacro
(a causa -nargs=1
dell'attributo).
Quando verrà eseguito questo nuovo comando, Vim si espanderà automaticamente <args>
con il valore fornito.
Se questo secondo argomento è diverso da zero / true ( if <args>
) verrà eseguita la prima versione del comando (quella che rende una macro ricorsiva incondizionatamente), altrimenti se è zero / falso verrà eseguita la seconda versione (quella che rende una macro ricorsiva a condizione che rimanga sulla riga corrente).
Quindi, tornando all'esempio precedente, darebbe la seguente cosa:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq
inizia la registrazione di una macro all'interno del registro q
<C-a>
incrementa il numero sotto il cursorew
sposta il cursore sul numero successivoq
termina la registrazione:RecursiveMacro q 0
rende la macro memorizzata all'interno del registro q
ricorsiva ma solo fino alla fine della riga (a causa del secondo argomento 0
)3G
sposta il cursore su una linea arbitraria (ad esempio 3)0@q
riproduce la macro ricorsiva dall'inizio della rigaDovrebbe dare lo stesso risultato di prima:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Ma questa volta non è stato necessario digitare i comandi di distrazione durante la registrazione della macro, è possibile concentrarsi semplicemente sul crearne uno funzionante.
E durante il passaggio 5, se al comando fosse stato passato un argomento diverso da zero, ovvero se si fosse digitato :RecursiveMacro q 1
invece di :RecursiveMacro q 0
, la macro q
sarebbe diventata ricorsiva incondizionatamente, il che avrebbe fornito il seguente buffer:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Questa volta la macro non si sarebbe fermata alla fine della 3a riga ma alla fine del buffer.
Per ulteriori informazioni, vedere:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10
, ottengo 2 3 4 5 6 7 8 9 10 12
invece di 2 3 4 5 6 7 8 9 10 11
. Non so perché, forse ho sbagliato a scrivere qualcosa. Comunque sembra più sofisticato del mio semplice approccio, e implica regex per descrivere dove la macro dovrebbe spostare il cursore, così come un elenco di posizioni che non ho mai visto usato in questo modo. Mi piace un sacco!
\d\+
per descrivere numeri di più cifre.
:lv ...
comando, il :lla
comando può essere usato per saltare all'ultima partita e il :lp
comando può avanzare sulle partite in ordine inverso.
Una macro ricorsiva si interromperà non appena incontrerà un comando non riuscito. Pertanto, per interrompere alla fine di una riga, è necessario un comando che non riuscirà alla fine della riga.
Per impostazione predefinita *, il l
comando è un tale comando, quindi è possibile utilizzarlo per interrompere una macro ricorsiva. Se il cursore non si trova alla fine della riga, è sufficiente spostarlo indietro in seguito con il comando h
.
Quindi, usando la stessa macro di esempio di saginaw :
qqqqq<c-a>lhw@qq
Suddiviso:
qqq
: Cancella il registro q,qq
: Inizia a registrare una macro nel q
registro,<c-a>
: Incrementa il numero sotto il cursore,lh
: Se siamo alla fine della riga, interrompere la macro. Altrimenti, non fare nulla.w
: Passa alla parola successiva sulla riga.@q
: Ricorsoq
: Interrompe la registrazione.È quindi possibile eseguire la macro con lo stesso 0@q
comando descritto da saginaw.
* L' 'whichwrap'
opzione ti consente di definire quali tasti di movimento verranno spostati sulla riga successiva quando ti trovi all'inizio o alla fine di una riga (Vedi :help 'whichwrap'
). Se hai l
impostato questa opzione, la soluzione sopra descritta verrà interrotta.
Tuttavia, è probabile che si usa solo uno dei comandi in modalità normale di tre predefiniti per avanzare un singolo carattere ( <Space>
, l
, e <Right>
), quindi se avete l
incluso nel vostro 'whichwrap'
ambiente, è possibile rimuovere uno che non si usa dal 'whichwrap'
opzione, ad es. per <Space>
:
:set whichwrap-=s
Quindi è possibile sostituire il l
comando nel passaggio 4 della macro con un <Space>
comando.
virtualedit=onemore
interferirà con l'utilizzo l
per rilevare la fine della linea, sebbene non in modo così grave whichwrap=l
.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q
aumenterà tutti i numeri sulla riga 3. Forse c'è un modo per rendere questa soluzione meno fragile?