Wie kann ich ein rekursives Makro erstellen, sodass es nur bis zum Zeilenende ausgeführt wird?
Oder wie man ein rekursives Makro nur bis zum Ende der Zeile ausführt?
Es gibt wahrscheinlich eine einfachere Methode, aber vielleicht können Sie Folgendes versuchen.
Angenommen, Sie verwenden register q
, um Ihr rekursives Makro aufzuzeichnen.
Geben Sie zu Beginn der Aufnahme Folgendes ein:
:let a = line('.')
Geben Sie am Ende der Aufzeichnung @q
den folgenden Befehl ein , anstatt das Makro rekursiv zu machen:
:if line('.') == a | exe 'norm @q' | endif
Beenden Sie abschließend die Aufzeichnung des Makros mit q
.
Der zuletzt eingegebene Befehl gibt das Makro q
( exe 'norm @q'
) nur dann wieder, wenn die aktuelle Zeilennummer ( line('.')
) mit der ursprünglich in Variable gespeicherten übereinstimmt a
.
Mit diesem :normal
Befehl können Sie normale Befehle (wie @q
) aus dem Ex-Modus eingeben .
Der Grund, warum der Befehl in eine Zeichenfolge eingeschlossen und vom Befehl ausgeführt wird, :execute
besteht darin, zu verhindern, dass :normal
der Rest des Befehls ( |endif
) verbraucht (eingegeben ) wird.
Anwendungsbeispiel.
Angenommen, Sie haben den folgenden Puffer:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Und Sie möchten mit einem rekursiven Makro alle Zahlen einer beliebigen Zeile inkrementieren.
Sie können eingeben 0
, um den Cursor an den Anfang einer Zeile zu bewegen, und dann die Aufzeichnung des Makros starten:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqq
Löscht den Inhalt des Registers, q
damit er beim ersten Aufruf während der Definition des Makros nicht beeinträchtigt wirdqq
startet die Aufnahme:let a=line('.')
speichert die aktuelle Zeilennummer in der Variablen a
w
bewegt den Cursor zur nächsten Ziffer:if line('.')==a|exe 'norm @q'|endif
ruft das Makro nur dann auf, wenn sich die Zeilennummer nicht geändert hatq
stoppt die AufnahmeWenn Sie Ihr Makro definiert haben und den Cursor in der dritten Zeile positionieren, drücken Sie 0
, um es an den Zeilenanfang zu verschieben, und @q
drücken Sie dann, um das Makro erneut abzuspielen. Dies q
sollte sich nur auf die aktuelle Zeile und nicht auf die anderen Zeilen auswirken:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Machen Sie ein Makro nach der Aufnahme rekursiv
Wenn Sie möchten, können Sie Ihr Makro nach der Aufzeichnung rekursiv machen, indem Sie die Zeichenfolge in einem Register speichern und zwei Zeichenfolgen mit dem Punktoperator .
verknüpfen.
Dies würde Ihnen mehrere Vorteile bringen:
@q
im Makro hinzugefügt werden, nachdem sie definiert wurden und der alte Inhalt überschrieben wurdeWenn Sie Ihr Makro wie gewohnt (nicht rekursiv) aufzeichnen, können Sie es anschließend mit dem folgenden Befehl rekursiv machen:
let @q = @q . "@q"
Oder noch kürzer: let @q .= "@q"
.=
ist ein Operator, mit dem eine Zeichenfolge an eine andere angehängt werden kann.
Dies sollte die 2 Zeichen ganz @q
am Ende der im Register gespeicherten Folge von Tastenanschlägen hinzufügen q
. Sie können auch einen benutzerdefinierten Befehl definieren:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Es definiert den Befehl, :RecursiveMacro
der auf den Namen eines Registers als Argument wartet (aufgrund des -register
Attributs, an das übergeben wird :command
).
Es ist der gleiche Befehl wie zuvor, der einzige Unterschied ist, dass Sie jedes Vorkommen von q
durch ersetzen <reg>
. Wenn der Befehl ausgeführt wird, erweitert Vim automatisch jedes Vorkommen um den von <reg>
Ihnen angegebenen Registernamen.
Jetzt müssen Sie nur noch Ihr Makro wie gewohnt (nicht rekursiv) aufzeichnen und dann eingeben :RecursiveMacro q
, um das im Register gespeicherte Makro q
rekursiv zu machen.
Sie können das Gleiche tun, um ein Makro unter der Bedingung rekursiv zu machen, dass es in der aktuellen Zeile bleibt:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Es ist genau dasselbe, was zu Beginn des Beitrags beschrieben wurde, außer, dass Sie es dieses Mal nach der Aufnahme tun. Sie verketten nur zwei Zeichenfolgen, eine vor und eine nach den Tastenanschlägen, die das q
Register derzeit enthält:
let @q =
definiert den Inhalt des Registers neu q
":let a=line('.')\r"
Speichert die aktuelle Zeilennummer in der Variablen, a
bevor das Makro seine Arbeit \r
ausführt. Dies ist erforderlich, um Vim anzuweisen, die Eingabetaste zu drücken und den Befehl auszuführen. :help expr-quote
Eine Liste ähnlicher Sonderzeichen finden Sie unter. . @q .
verkettet den aktuellen Inhalt des q
Registers mit dem vorherigen und dem nächsten String,":if line('.')==a|exe 'norm @q'|endif\r"
ruft das Makro q
unter der Bedingung auf, dass sich die Zeile nicht geändert hatUm einige Tastenanschläge zu speichern, können Sie den Prozess durch Definieren des folgenden benutzerdefinierten Befehls automatisieren:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
Alles, was Sie tun müssen, ist, Ihr Makro wie gewohnt aufzuzeichnen (nicht rekursiv) und dann zu tippen :RecursiveMacroOnLine q
, um das im Register gespeicherte Makro q
rekursiv zu machen, sofern es in der aktuellen Zeile verbleibt.
Füge die 2 Befehle zusammen
Sie können auch :RecursiveMacro
so einstellen , dass die 2 Fälle abgedeckt werden:
Dazu können Sie ein zweites Argument an übergeben :RecursiveMacro
. Letzterer testet einfach seinen Wert und führt je nach Wert einen der beiden vorherigen Befehle aus. Es würde so etwas geben:
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
Oder (mit Zeilenfortsätzen / Backslashes, um die Lesbarkeit zu verbessern):
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 ist dasselbe wie zuvor, außer dass Sie diesmal ein zweites Argument für :RecursiveMacro
(wegen des -nargs=1
Attributs) angeben müssen.
Wenn dieser neue Befehl ausgeführt wird, wird Vim automatisch um <args>
den von Ihnen angegebenen Wert erweitert .
Wenn dieses zweite Argument nicht null / wahr ist ( if <args>
), wird die erste Version des Befehls ausgeführt (diejenige, die ein Makro bedingungslos rekursiv macht), andernfalls, wenn es null / falsch ist, wird die zweite Version ausgeführt (diejenige, die es erstellt) ein Makro rekursiv unter der Bedingung, dass es in der aktuellen Zeile bleibt).
Wenn Sie also zum vorherigen Beispiel zurückkehren, erhalten Sie Folgendes:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq
Startet die Aufzeichnung eines Makros im Register q
<C-a>
erhöht die Zahl unter dem Cursorw
bewegt den Cursor zur nächsten Zifferq
Beendet die Aufnahme:RecursiveMacro q 0
macht das im Register gespeicherte Makro q
rekursiv, jedoch nur bis zum Ende der Zeile (aufgrund des zweiten Arguments 0
)3G
bewegt den Cursor auf eine beliebige Zeile (3 zum Beispiel)0@q
Spielt das rekursive Makro ab dem Zeilenanfang abEs sollte das gleiche Ergebnis wie zuvor geben:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Aber dieses Mal mussten Sie die Ablenkungsbefehle während der Aufzeichnung Ihres Makros nicht eingeben, sondern konnten sich einfach darauf konzentrieren, ein funktionierendes zu erstellen.
Wenn Sie in Schritt 5 ein Argument ungleich Null an den Befehl übergeben hätten, dh wenn Sie :RecursiveMacro q 1
stattdessen eingegeben hätten :RecursiveMacro q 0
, wäre das Makro q
bedingungslos rekursiv geworden, was den folgenden Puffer ergeben hätte:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Dieses Mal hätte das Makro nicht am Ende der dritten Zeile, sondern am Ende des Puffers angehalten.
Weitere Informationen finden Sie unter:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q
werden alle Zahlen in Zeile 3 erhöht. Vielleicht gibt es eine Möglichkeit, diese Lösung weniger anfällig zu machen?1 2 3 4 5 6 7 8 9 10
, erhalte ich2 3 4 5 6 7 8 9 10 12
anstelle von2 3 4 5 6 7 8 9 10 11
. Ich weiß nicht warum, vielleicht habe ich etwas falsch geschrieben. Auf jeden Fall scheint es ausgefeilter zu sein als mein einfacher Ansatz, und es beinhaltet Regexes, um zu beschreiben, wohin das Makro den Cursor bewegen soll, sowie eine Standortliste, die ich auf diese Weise noch nie gesehen habe. Ich mag es sehr!\d\+
zur Beschreibung mehrstelliger Zahlen verwenden sollen.:lv ...
Befehl kann also mit dem:lla
Befehl zum letzten Treffer gesprungen werden, und mit dem:lp
Befehl können die Treffer in umgekehrter Reihenfolge durchlaufen werden.Ein rekursives Makro stoppt, sobald es auf einen fehlgeschlagenen Befehl stößt. Um am Ende einer Zeile anzuhalten, benötigen Sie daher einen Befehl, der am Ende der Zeile fehlschlägt.
Standardmäßig * ist der
l
Befehl ein solcher Befehl, sodass Sie ihn zum Stoppen eines rekursiven Makros verwenden können. Befindet sich der Cursor nicht am Ende der Zeile, müssen Sie ihn nur mit dem Befehl zurücksetzenh
.Verwenden Sie also dasselbe Beispielmakro wie saginaw :
Heruntergebrochen:
qqq
: Löschen Sie das q-Register,qq
: Starten Sie die Aufzeichnung eines Makros imq
Register.<c-a>
: Erhöhe die Zahl unter dem Cursor,lh
: Wenn wir am Ende der Zeile sind, brechen Sie das Makro ab. Sonst nichts tun.w
: Weiter zum nächsten Wort in der Zeile.@q
: Recurseq
: Höre auf, aufzunehmen.Sie können das Makro dann mit demselben
0@q
Befehl ausführen, der von saginaw beschrieben wurde.* Mit dieser
'whichwrap'
Option können Sie festlegen, welche Bewegungstasten am Anfang oder Ende einer Zeile in die nächste Zeile übergehen sollen (siehe:help 'whichwrap'
). Wenn Siel
diese Option aktiviert haben, wird die oben beschriebene Lösung nicht mehr unterstützt.Es ist jedoch wahrscheinlich, dass Sie nur einen der drei Standardbefehle für den Normalmodus zum Vorrücken eines einzelnen Zeichens (
<Space>
,,l
und<Right>
) verwenden. Wenn Sie alsol
in Ihre'whichwrap'
Einstellung aufgenommen haben, können Sie einen Befehl entfernen, den Sie nicht verwenden'whichwrap'
Option, zB für<Space>
:Anschließend können Sie den
l
Befehl in Schritt 4 des Makros durch einen<Space>
Befehl ersetzen .quelle
virtualedit=onemore
die Verwendungl
von stört , um das Zeilenende zu erkennen, wenn auch nicht so stark wiewhichwrap=l
.'ve'