Jak zatrzymać makro rekurencyjne na końcu linii?

13

Jak mogę utworzyć makro rekurencyjne, aby działało tylko do końca linii?

Lub jak uruchomić rekurencyjne makro tylko do końca linii?

DinushanM
źródło

Odpowiedzi:

11

Prawdopodobnie jest to prostsza metoda, ale może wypróbujesz następujące.

Załóżmy, że użyjesz register qdo zarejestrowania swojego rekurencyjnego makra.

Na samym początku nagrania wpisz:

:let a = line('.')

Następnie na samym końcu nagrania, zamiast naciskać, @qaby 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.

:normalKomenda 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, :executejest 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ć, 0aby 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
  1. qqqusuwa zawartość rejestru q, aby po wywołaniu go podczas definicji makra nie przeszkadzał
  2. qq rozpoczyna nagrywanie
  3. :let a=line('.') przechowuje bieżący numer linii w zmiennej a
  4. Ctrl+ azwiększa liczbę pod kursorem
  5. w przesuwa kursor do następnej liczby
  6. :if line('.')==a|exe 'norm @q'|endif przywołuje makro, ale tylko jeśli numer linii nie zmienił się
  7. q zatrzymuje nagrywanie

Po zdefiniowaniu makra, jeśli umieścisz kursor na trzeciej linii, naciśnij, 0aby przenieść go na początek linii, a następnie naciśnij, @qaby 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:

  • nie ma potrzeby czyszczenia rejestru przed nagraniem, ponieważ znaki @qzostaną dodane do makra po jego zdefiniowaniu i po nadpisaniu istniejącej zawartości
  • nie trzeba wpisywać niczego niezwykłego podczas nagrywania, możesz skupić się na stworzeniu prostego, działającego makra
  • możliwość przetestowania przed rekurencyjnym sprawdzeniem, jak się zachowuje

Jeś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 @qna 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, :RecursiveMacroktóre czeka na nazwę rejestru jako argument (ze względu na -registerprzekazany atrybut :command).
To samo polecenie, jak poprzednio, jedyną różnicą jest to zastąpić wszystkie wystąpienia qz <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 qaby makro przechowywane w rejestrze było qrekurencyjne.


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 qrejestr zawiera obecnie:

  1. let @q = redefiniuje zawartość rejestru q
  2. ":let a=line('.')\r"przechowuje bieżący numer wiersza wewnątrz zmiennej, azanim makro wykona swoją pracę,
    \rkonieczne jest, aby powiedzieć Vimowi, aby nacisnął Enter i wykonał polecenie, zobacz :help expr-quotelistę podobnych znaków specjalnych,
  3. . @q .łączy bieżącą zawartość qrejestru z poprzednim ciągiem i następnym,
  4. ":if line('.')==a|exe 'norm @q'|endif\r"przywołuje makro qpod warunkiem, że linia się nie zmieniła

Ponownie, 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 qaby makro przechowywane w rejestrze było qrekurencyjne pod warunkiem, że pozostanie w bieżącej linii.


Scal 2 polecenia

Możesz również dostosować, :RecursiveMacroaby obejmował 2 przypadki:

  • uczynić makro rekurencyjnym bezwarunkowo,
  • uczyń makro rekurencyjnym pod warunkiem, że pozostanie na bieżącej linii

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=1atrybutu).
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
  1. qq rozpoczyna rejestrowanie makra w rejestrze q
  2. <C-a> zwiększa liczbę pod kursorem
  3. w przesuwa kursor do następnej liczby
  4. q kończy nagrywanie
  5. :RecursiveMacro q 0sprawia, że ​​makro przechowywane w rejestrze jest q rekurencyjne, ale tylko do końca linii (z powodu drugiego argumentu 0)
  6. 3G przesuwa kursor do dowolnej linii (na przykład 3)
  7. 0@q odtwarza makro rekurencyjne od początku wiersza

Powinien 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 1zamiast :RecursiveMacro q 0, makro qstał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
saginaw
źródło
2
Listy lokalizacji można używać do przewijania wyników wyszukiwania w makrze, o ile makro nie zmienia pozycji dopasowań, np. :lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@qZwiększy wszystkie liczby w wierszu 3. Może istnieje sposób, aby to rozwiązanie było mniej kruche?
djjcast,
@djjcast Możesz opublikować to jako odpowiedź, próbowałem i działa naprawdę świetnie. Jest tylko jeden przypadek, którego nie rozumiem, kiedy wykonuję makro w poniższym wierszu 1 2 3 4 5 6 7 8 9 10, dostaję 2 3 4 5 6 7 8 9 10 12zamiast 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ę!
saginaw
@djjcast Przepraszam, właśnie zrozumiałem, że problem pochodzi po prostu z mojego wyrażenia regularnego, powinienem był \d\+opisywać wiele cyfr.
saginaw
@djjcast Ach teraz rozumiem, co miałeś na myśli, mówiąc, że makro nie powinno zmieniać pozycji dopasowań. Ale nie wiem jak rozwiązać ten problem. Jedyny pomysł, jaki mam, to aktualizacja listy lokalizacji z poziomu makra, ale nie jestem przyzwyczajony do listy lokalizacji, jest to dla mnie zbyt skomplikowane, naprawdę przepraszam.
saginaw
1
@saginaw Iterowanie po meczach w odwrotnej kolejności wydaje się, że rozwiązałoby to problem w większości przypadków, ponieważ wydaje się, że makro ma mniejszą szansę na zmianę pozycji poprzednich meczów. Po komendzie można użyć :lv ...komendy, :llaaby przejść do ostatniego dopasowania, a :lpkomendy można użyć, aby przejść do kolejnych meczów w odwrotnej kolejności.
djjcast,
9

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 * lpolecenie 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:

  1. qqq: Wyczyść rejestr q,
  2. qq: Rozpocznij rejestrowanie makra w qrejestrze,
  3. <c-a>: Zwiększ liczbę pod kursorem,
  4. lh: Jeśli jesteśmy na końcu linii, przerwij makro. W przeciwnym razie nie rób nic.
  5. w: Przejście do następnego słowa w linii.
  6. @q: Recurse
  7. q: Zatrzymaj nagrywanie.

Następnie możesz uruchomić makro za pomocą tego samego 0@qpolecenia, 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 lustawił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 lwłą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ć lpolecenie w kroku 4 makra <Space>poleceniem.

Bogaty
źródło
1
Zauważ też, że ustawienie virtualedit=onemorebędzie kolidować z użyciem ldo wykrywania końca linii, choć nie tak poważnie jak whichwrap=l.
Kevin
@Kevin Dobra uwaga! Zaktualizuję moją odpowiedź, aby wspomnieć've'
Rich