Odpowiedzi:
Prawdopodobnie jest to prostsza metoda, ale może wypróbujesz następujące.
Załóżmy, że użyjesz register q
do zarejestrowania swojego rekurencyjnego makra.
Na samym początku nagrania wpisz:
:let a = line('.')
Następnie na samym końcu nagrania, zamiast naciskać, @q
aby makro było rekurencyjne, wpisz następujące polecenie:
:if line('.') == a | exe 'norm @q' | endif
Na koniec zakończ rejestrację makra za pomocą q
.
Ostatnie wpisane polecenie odtworzy makro q
( exe 'norm @q'
), ale tylko jeśli bieżący numer linii ( line('.')
) jest taki sam, jak początkowo przechowywany w zmiennej a
.
:normal
Komenda pozwala wpisać normalnych poleceń (jak @q
) z trybu Ex.
Powodem, dla którego polecenie jest zawinięte w ciąg znaków i wykonane przez polecenie, :execute
jest zapobieganie wykorzystaniu :normal
(wpisaniu) reszty polecenia ( |endif
).
Przykład użycia
Załóżmy, że masz następujący bufor:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
I chcesz zwiększyć wszystkie liczby z dowolnej linii za pomocą rekurencyjnego makra.
Możesz wpisać, 0
aby przesunąć kursor na początek linii, a następnie rozpocząć rejestrowanie makra:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqq
usuwa zawartość rejestru q
, aby po wywołaniu go podczas definicji makra nie przeszkadzałqq
rozpoczyna nagrywanie:let a=line('.')
przechowuje bieżący numer linii w zmiennej a
w
przesuwa kursor do następnej liczby:if line('.')==a|exe 'norm @q'|endif
przywołuje makro, ale tylko jeśli numer linii nie zmienił sięq
zatrzymuje nagrywaniePo zdefiniowaniu makra, jeśli umieścisz kursor na trzeciej linii, naciśnij, 0
aby przenieść go na początek linii, a następnie naciśnij, @q
aby odtworzyć makro q
, powinno to wpływać tylko na bieżącą linię, a nie na inne:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Zrób makro rekurencyjne po nagraniu
Jeśli chcesz, możesz uczynić makro rekurencyjnym po jego nagraniu, korzystając z faktu, że jest ono przechowywane w ciągu wewnątrz rejestru i że możesz połączyć dwa ciągi z .
operatorem kropki .
Dałoby to kilka korzyści:
@q
zostaną dodane do makra po jego zdefiniowaniu i po nadpisaniu istniejącej zawartościJeśli makro zapisujesz w zwykły sposób (nie rekurencyjnie), możesz później ustawić je jako rekurencyjne za pomocą następującego polecenia:
let @q = @q . "@q"
Lub nawet krócej: let @q .= "@q"
.=
to operator, który pozwala dołączyć ciąg do innego.
To powinno dodać 2 znaki @q
na samym końcu sekwencji naciśnięć klawiszy zapisanych w rejestrze q
. Możesz również zdefiniować niestandardowe polecenie:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Definiuje polecenie, :RecursiveMacro
które czeka na nazwę rejestru jako argument (ze względu na -register
przekazany atrybut :command
).
To samo polecenie, jak poprzednio, jedyną różnicą jest to zastąpić wszystkie wystąpienia q
z <reg>
. Kiedy polecenie zostanie wykonane, Vim automatycznie rozszerzy każde wystąpienie o <reg>
podaną nazwę rejestru.
Teraz wszystko, co musisz zrobić, to zapisać makro w zwykły sposób (nie rekurencyjnie), a następnie wpisać, :RecursiveMacro q
aby makro przechowywane w rejestrze było q
rekurencyjne.
Możesz zrobić to samo, aby makro było rekurencyjne pod warunkiem, że pozostanie w bieżącym wierszu:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Jest to dokładnie to samo, co opisano na początku postu, z tym wyjątkiem, że robisz to po nagraniu. Po prostu łączysz dwa ciągi, jeden przed i jeden po dowolnym naciśnięciu klawisza, który q
rejestr zawiera obecnie:
let @q =
redefiniuje zawartość rejestru q
":let a=line('.')\r"
przechowuje bieżący numer wiersza wewnątrz zmiennej, a
zanim makro wykona swoją pracę, \r
konieczne jest, aby powiedzieć Vimowi, aby nacisnął Enter i wykonał polecenie, zobacz :help expr-quote
listę podobnych znaków specjalnych, . @q .
łączy bieżącą zawartość q
rejestru z poprzednim ciągiem i następnym,":if line('.')==a|exe 'norm @q'|endif\r"
przywołuje makro q
pod warunkiem, że linia się nie zmieniłaPonownie, aby zapisać niektóre naciśnięcia klawiszy, możesz zautomatyzować proces, definiując następujące niestandardowe polecenie:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
I znowu wszystko, co musisz zrobić, to zapisać makro jak zwykle (nie rekurencyjnie), a następnie wpisać, :RecursiveMacroOnLine q
aby makro przechowywane w rejestrze było q
rekurencyjne pod warunkiem, że pozostanie w bieżącej linii.
Scal 2 polecenia
Możesz również dostosować, :RecursiveMacro
aby obejmował 2 przypadki:
Aby to zrobić, możesz przekazać drugi argument do :RecursiveMacro
. Ten ostatni po prostu przetestuje swoją wartość i, w zależności od wartości, wykona jedno z 2 poprzednich poleceń. Dałoby to coś takiego:
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
Lub (używając kontynuacji linii / ukośników odwrotnych, aby była bardziej czytelna):
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
Jest tak samo jak poprzednio, tyle że tym razem musisz podać drugi argument :RecursiveMacro
(z powodu -nargs=1
atrybutu).
Kiedy to nowe polecenie zostanie wykonane, Vim automatycznie rozszerzy się <args>
o podaną wartość.
Jeśli ten drugi argument jest niezerowy / true ( if <args>
), pierwsza wersja polecenia zostanie wykonana (ta, która bezwarunkowo powoduje, że makro rekursywnie), w przeciwnym razie, jeśli jest zero / false, zostanie wykonana druga wersja (ta, która powoduje, że makro rekurencyjne pod warunkiem, że pozostaje w bieżącej linii).
Wracając do poprzedniego przykładu, dałoby to następującą rzecz:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq
rozpoczyna rejestrowanie makra w rejestrze q
<C-a>
zwiększa liczbę pod kursoremw
przesuwa kursor do następnej liczbyq
kończy nagrywanie:RecursiveMacro q 0
sprawia, że makro przechowywane w rejestrze jest q
rekurencyjne, ale tylko do końca linii (z powodu drugiego argumentu 0
)3G
przesuwa kursor do dowolnej linii (na przykład 3)0@q
odtwarza makro rekurencyjne od początku wierszaPowinien dać taki sam wynik jak poprzednio:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Ale tym razem nie musisz wpisywać rozpraszających poleceń podczas rejestrowania makra, możesz po prostu skupić się na stworzeniu działającego.
A podczas kroku 5, jeśli przekazałeś niezerowy argument do polecenia, to znaczy, jeśli wpisałeś :RecursiveMacro q 1
zamiast :RecursiveMacro q 0
, makro q
stałoby się bezwarunkowo rekurencyjne, co dałoby następujący bufor:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Tym razem makro nie zatrzymałoby się na końcu 3. linii, ale na końcu bufora.
Aby uzyskać więcej informacji, zobacz:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10
, dostaję 2 3 4 5 6 7 8 9 10 12
zamiast 2 3 4 5 6 7 8 9 10 11
. Nie wiem dlaczego, może coś pomyliłem. W każdym razie wydaje się bardziej wyrafinowane niż moje proste podejście i wymaga wyrażeń regularnych, aby opisać, gdzie makro powinno przesunąć kursor, a także listę lokalizacji, której nigdy nie widziałem w ten sposób. Bardzo to lubię!
\d\+
opisywać wiele cyfr.
:lv ...
komendy, :lla
aby przejść do ostatniego dopasowania, a :lp
komendy można użyć, aby przejść do kolejnych meczów w odwrotnej kolejności.
Makro rekurencyjne zatrzyma się, gdy tylko napotka polecenie, które się nie powiedzie. Dlatego, aby zatrzymać się na końcu linii, potrzebujesz polecenia, które zawiedzie na końcu linii.
Domyślnie * l
polecenie jest takim poleceniem, więc możesz go użyć do zatrzymania rekurencyjnego makra. Jeśli kursor nie znajduje się na końcu wiersza, wystarczy przesunąć go z powrotem za pomocą polecenia h
.
Tak więc używając tego samego makra przykładowego jak saginaw :
qqqqq<c-a>lhw@qq
Zepsuty:
qqq
: Wyczyść rejestr q,qq
: Rozpocznij rejestrowanie makra w q
rejestrze,<c-a>
: Zwiększ liczbę pod kursorem,lh
: Jeśli jesteśmy na końcu linii, przerwij makro. W przeciwnym razie nie rób nic.w
: Przejście do następnego słowa w linii.@q
: Recurseq
: Zatrzymaj nagrywanie.Następnie możesz uruchomić makro za pomocą tego samego 0@q
polecenia, które opisano w saginaw.
* Ta 'whichwrap'
opcja pozwala określić, które klawisze ruchu będą przewijane do następnej linii, gdy znajdziesz się na początku lub na końcu linii (patrz :help 'whichwrap'
). Jeśli l
ustawiłeś tę opcję, spowoduje to uszkodzenie opisanego powyżej rozwiązania.
Jednak jest prawdopodobne, że używasz tylko jeden z trzech domyślnych poleceń normalnym trybie na pogłębianie pojedynczy znak ( <Space>
, l
, i <Right>
), więc jeśli masz l
włączone w 'whichwrap'
ustawieniach, można usunąć jedno z nich nie korzystać z 'whichwrap'
opcja, np. dla <Space>
:
:set whichwrap-=s
Następnie możesz zastąpić l
polecenie w kroku 4 makra <Space>
poleceniem.
virtualedit=onemore
będzie kolidować z użyciem l
do wykrywania końca linii, choć nie tak poważnie jak whichwrap=l
.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q
Zwiększy wszystkie liczby w wierszu 3. Może istnieje sposób, aby to rozwiązanie było mniej kruche?