Globalne wyszukiwanie i zamiana w Vimie, zaczynając od pozycji kursora i zawijając

112

Kiedy wyszukuję za pomocą / polecenia trybu normalnego:

/\vSEARCHTERM

Vim rozpoczyna wyszukiwanie od pozycji kursora i kontynuuje w dół, zawijając do góry. Jednak gdy wyszukuję i zamieniam za pomocą :substitutepolecenia:

:%s/\vBEFORE/AFTER/gc

Zamiast tego Vim zaczyna się od początku pliku.

Czy jest sposób, aby Vim przeprowadzał wyszukiwanie i zastępowanie, zaczynając od pozycji kursora i zawijając do góry, gdy dotrze do końca?

Basteln
źródło
2
\vpattern- `` bardzo magiczny '' wzorzec: znaki niealfanumeryczne są interpretowane jako specjalne symbole wyrażeń regularnych (bez konieczności zmiany znaczenia)
Carlos Araya
jaki jest sens, aby zacząć od bieżącej pozycji i zawijać plik zamiast używać tylko %? Nie widzę sensu, żebyś i tak przeglądał cały plik.
tymik 22.02.18
@tymik Celem było rozpoczęcie wyszukiwania od kursora i przejście przez każdy wynik krok po kroku. Ale nie używałem Vima od lat.
basteln

Odpowiedzi:

50

1.  Nie jest trudno osiągnąć zachowanie za pomocą dwuetapowej substytucji:

:,$s/BEFORE/AFTER/gc|1,''-&&

Najpierw polecenie substytucji jest uruchamiane dla każdej linii, zaczynając od bieżącej do końca pliku:

,$s/BEFORE/AFTER/gc

Następnie :substitutepolecenie to jest powtarzane z tym samym wzorcem wyszukiwania, ciągiem zastępczym i flagami, używając :& polecenia (patrz :help :&):

1,''-&&

Ta ostatnia jednak dokonuje podstawienia w zakresie wierszy od pierwszego wiersza pliku do wiersza, w którym ustawiono poprzedni znacznik kontekstu, minus jeden. Ponieważ pierwsze :substitutepolecenie przechowuje pozycję kursora przed rozpoczęciem rzeczywistych zamian, wiersz, do którego adresowany ''jest adres,  jest wierszem bieżącym przed wykonaniem tego polecenia zastępowania. ( '' Adres odnosi się do ' pseudoznaku; zobacz :help :rangei :help ''po szczegóły.)

Zauważ, że drugie polecenie (po | separatorze polecenia - zobacz :help :bar) nie wymaga żadnej zmiany, gdy wzorzec lub flagi są zmieniane w pierwszym.

2.  Aby zaoszczędzić trochę pisania, aby wyświetlić szkielet powyższego polecenia podstawienia w linii poleceń, można zdefiniować mapowanie w trybie normalnym, na przykład:

:noremap <leader>cs :,$s///gc\|1,''-&&<c-b><right><right><right><right>

Końcowa <c-b><right><right><right><right>część jest niezbędna, aby przesunąć kursor na początek wiersza poleceń ( <c-b>), a następnie cztery znaki w prawo ( <right> × 4), umieszczając go w ten sposób między pierwszymi dwoma ukośnikami, aby użytkownik mógł rozpocząć wpisywanie wzorca wyszukiwania . Gdy żądany wzór i zamiana są gotowe, wynikowe polecenie można uruchomić, naciskając Enter.

(Można rozważyć umieszczenie //zamiast ///w odwzorowaniu powyżej, jeśli ktoś woli wpisać wzorzec, a następnie samemu wpisać separujący ukośnik, a następnie ciąg zastępujący, zamiast używać strzałki w prawo, aby przesunąć kursor na już istniejący separujący ukośnik rozpoczynający się część zamienna.)

ib.
źródło
1
Jedynym problemem związanym z tym rozwiązaniem jest to, że opcje last (l) i quit (q) nie zatrzymują wykonywania drugiej komendy zastępczej
Steve Vermeulen
@eventualEntropy Zobacz moje rozwiązanie poniżej dotyczące monitowania o kolejne naciśnięcie „q”.
q335r49
2
Nie zapomnij (tak jak ja) uciec z rury, jeśli umieszczasz to w kluczowym mapowaniu.
jazzabeanie
11
O mój Boże, co w ogóle znaczą te wszystkie arbitralne postacie. Jak się tego nauczyłeś?
Rocky Raccoon
2
@Tropilio: Wtedy można spróbować coś takiego: :noremap <leader>R :,$s///gc\|1,''-&&<c-b><right><right><right><right>. Wstawia :,$s///gc|1,''-&&do wiersza poleceń z kursorem między pierwszymi dwoma ukośnikami. Po wpisaniu żądanego wzoru i zamiany możesz uruchomić wynikowe polecenie, naciskając klawisz Enter .
ib.
174

Używasz już zakresu, %który jest krótką ręką 1,$oznaczającą cały plik. Aby przejść od bieżącej linii do końca, którego używasz .,$. Kropka oznacza bieżącą linię i $ostatnią linię. Więc polecenie byłoby następujące:

:.,$s/\vBEFORE/AFTER/gc

Ale .można założyć, że linia lub bieżąca może zostać usunięta:

:,$s/\vBEFORE/AFTER/gc

Aby uzyskać więcej pomocy, zobacz

:h range
Peter Rincker
źródło
Dzięki, już o tym wiedziałem. Chcę, aby wyszukiwanie i zamiana zawijały się po osiągnięciu końca dokumentu. Z:., $ S to nie robi, po prostu wyświetla komunikat „Nie znaleziono wzorca”.
basteln
7
Dla „następnych dziesięciu wierszy”:10:s/pattern/replace/gc
tutaj
10
mimo że nie tego szukał OP, było to dla mnie bardzo pomocne, dzięki!
Tobias J
51

%jest skrótem do 1,$
(Vim help => :help :%równe 1, $ (cały plik).)

. jest pozycją kursora, więc możesz to zrobić

:.,$s/\vBEFORE/AFTER/gc

Aby zamienić od początku dokumentu do kursora

:1,.s/\vBEFORE/AFTER/gc

itp

Zdecydowanie sugeruję przeczytanie instrukcji o zasięgu, :help rangeponieważ prawie wszystkie polecenia działają z zakresem.

mb14
źródło
Dzięki, już o tym wiedziałem. Chcę, aby wyszukiwanie i zamiana zawijały się po osiągnięciu końca dokumentu. Z:., $ S to nie robi, po prostu wyświetla komunikat „Nie znaleziono wzorca”.
basteln
@basteln: w poście była literówka // skopiowałeś i wkleiłeś?
sehe
Więc chcesz zamienić WSZYSTKO, ale ponieważ chcesz poprosić o potwierdzenie, chcesz zacząć od aktualnej pozycji. Po prostu zrób to dwa razy.
mb14
możesz zamiast tego użyć n i &.
mb14
hmm, wydaje mi się, że n i & to najlepsze rozwiązanie, chociaż nie jest dokładnie tak, jak powinno (z pytaniem tak / nie przy każdym meczu). Ale zdecydowanie wystarczająco dobrze, dzięki! :)
basteln
4

W KOŃCU znalazłem rozwiązanie na fakt, że zakończenie wyszukiwania zawija się do początku pliku bez pisania ogromnej funkcji ...

Nie uwierzysz, ile czasu zajęło mi wymyślenie tego. Po prostu dodaj monit, czy zawijać: jeśli użytkownik naciśnie qponownie, nie zawijaj. Zasadniczo zakończ wyszukiwanie, dotykając qqzamiast q! (A jeśli chcesz zawijać, po prostu wpisz y.)

:,$s/BEFORE/AFTER/gce|echo 'Continue at beginning of file? (y/q)'|if getchar()!=113|1,''-&&|en

Właściwie mam to przypisane do skrótu. Na przykład, jeśli chcesz wyszukać i zamienić każde słowo pod kursorem, zaczynając od bieżącej pozycji, na q*:

exe 'nno q* :,$s/\<<c-r>=expand("<cword>")<cr>\>//gce\|echo "Continue at beginning of file? (y/q)"\|if getchar()==121\|1,''''-&&\|en'.repeat('<left>',77)
q335r49
źródło
Moim zdaniem takie podejście - wstawianie dodatkowego znaku zachęty pomiędzy dwoma poleceniami zaproponowanymi w mojej odpowiedzi - dodaje niepotrzebną złożoność bez poprawiania użyteczności. W oryginalnej wersji , jeśli wyjdziesz z pierwszego polecenia zamiany (za pomocą q, llub Esc ) i nie chcesz kontynuować od góry, możesz już ponownie nacisnąć qlub Esc, aby od razu wyjść z drugiego polecenia. Oznacza to, że proponowane dodanie zachęty wydaje się skutecznie powielać już obecną funkcjonalność.
ib.
Koleś ... WYPRÓBOWAŁEŚ swojego podejścia? Powiedzmy, że chcę zamienić kolejne 3 wystąpienia „let” na „unlet”, a następnie - to jest klucz - kontynuuj edycję akapitu . Twoje podejście „Naciśnij y, y, y, teraz naciśnij q. Teraz zaczynasz szukać w pierwszym wierszu akapitu. Naciśnij ponownie q, aby zakończyć. Teraz wróć do poprzedniego miejsca, aby kontynuować edycję. OK, g ;. A więc w zasadzie: yyyqq ... zmieszaj się ... g; (lub u ^ R) Moja metoda: yyyqq
q335r49
Idealną metodą jest oczywiście yyyqkontynuacja edycji bez dezorientujących skoków. Ale yyyqqjest prawie tak samo dobre, jeśli weźmiesz pod uwagę dwukrotne dotknięcie, a prawie jedno dotknięcie pod względem łatwości użytkowania.
q335r49
Ani oryginalne polecenie, ani jego wersja z zachętą nie przywracają kursora do poprzedniej pozycji w tym scenariuszu. Pierwsza opuszcza użytkownika przy pierwszym wystąpieniu wzoru od góry (tam, gdzie znajdowała się w momencie qdrugiego naciśnięcia ). Ta ostatnia pozostawia kursor w ostatnim zastąpionym wystąpieniu (tuż przed qnaciśnięciem pierwszego ).
ib.
1
W wersji pierwotnej , jeżeli preferowane jest, aby automatycznie umieścić kursor do uprzedniego położenia (bez typowania dla Ctrl + O ), można po prostu dodać do norm!``polecenia: :,$s/BEFORE/AFTER/gc|1,''-&&|norm!``.
ib.
3

Oto coś bardzo szorstkiego, które rozwiązuje obawy związane z zawijaniem wyszukiwania metodą dwuetapową ( :,$s/BEFORE/AFTER/gc|1,''-&&) lub pośrednim „Kontynuować na początku pliku?” podejście:

" Define a mapping that calls a command.
nnoremap <Leader>e :Substitute/\v<<C-R>=expand('<cword>')<CR>>//<Left>

" And that command calls a script-local function.
command! -nargs=1 Substitute call s:Substitute(<q-args>)

function! s:Substitute(patterns)
  if getregtype('s') != ''
    let l:register=getreg('s')
  endif
  normal! qs
  redir => l:replacements
  try
    execute ',$s' . a:patterns . 'gce#'
  catch /^Vim:Interrupt$/
    return
  finally
    normal! q
    let l:transcript=getreg('s')
    if exists('l:register')
      call setreg('s', l:register)
    endif
  endtry
  redir END
  if len(l:replacements) > 0
    " At least one instance of pattern was found.
    let l:last=strpart(l:transcript, len(l:transcript) - 1)
    " Note: type the literal <Esc> (^[) here with <C-v><Esc>:
    if l:last ==# 'l' || l:last ==# 'q' || l:last ==# '^['
      " User bailed.
      return
    endif
  endif

  " Loop around to top of file and continue.
  " Avoid unwanted "Backwards range given, OK to swap (y/n)?" messages.
  if line("''") > 1
    1,''-&&"
  endif
endfunction

Ta funkcja używa kilku hacków, aby sprawdzić, czy powinniśmy zawinąć do góry:

  • Brak zawijania, jeśli użytkownik naciśnie klawisze „l”, „q” lub „Esc”, z których każda wskazuje na chęć przerwania.
  • Wykryj ostatnie naciśnięcie klawisza, rejestrując makro w rejestrze „s” i sprawdzając jego ostatni znak.
  • Unikaj nadpisywania istniejącego makra, zapisując / przywracając rejestr „s”.
  • Jeśli już nagrywasz makro podczas używania polecenia, wszystkie zakłady są wyłączone.
  • Próbuje postępować właściwie, przerywając.
  • Pozwala uniknąć ostrzeżeń o „zasięgu wstecznym” z wyraźną ochroną.
wincent
źródło
1
Wyodrębniłem to do małej wtyczki i umieściłem na GitHub tutaj .
wincent
Właśnie zainstalowałem wtyczkę, działa jak urok! Dziękuję bardzo za zrobienie tego!
Tropilio
1

Spóźniłem się na imprezę, ale zbyt często polegałem na tak późnych odpowiedziach, aby tego nie robić. Zebrałem wskazówki dotyczące reddit i stackoverflow, a najlepszą opcją jest użycie \%>...cwzorca w wyszukiwaniu, który pasuje tylko do kursora.

To powiedziawszy, również psuje wzór dla następnego kroku wymiany i jest trudny do wpisania. Aby przeciwdziałać tym efektom, funkcja niestandardowa musi później przefiltrować wzorzec wyszukiwania, a tym samym go zresetować. Zobacz poniżej.

Walczyłem z mapowaniem, które zastępuje następne wystąpienie i przeskakuje do następnego, a nie więcej (i tak było moim celem). Jestem pewien, że opierając się na tym, można wypracować globalną substytucję. Podczas pracy nad rozwiązaniem mającym na celu coś takiego :%s/.../.../g, poniższy wzorzec odfiltrowuje dopasowania we wszystkich wierszach po lewej stronie kursora - ale jest czyszczony po zakończeniu pojedynczego zastępowania, więc traci ten efekt bezpośrednio po tym, przeskakuje do następny mecz i dzięki temu jest w stanie przejść przez wszystkie mecze jeden po drugim.

fun! g:CleanColFromPattern(prevPattern) return substitute(a:prevPattern, '\V\^\\%>\[0-9]\+c', '', '') endf nmap <F3>n m`:s/\%><C-r>=col(".")-1<CR>c<C-.r>=g:CleanColFromPattern(getreg("/"))<CR>/~/&<CR>:call setreg("/", g:CleanColFromPattern(getreg("/")))<CR>``n

simlei
źródło
Dzięki, zgadzam się, że późna odpowiedź jest często pomocna. Osobiście nie używam już Vima od dłuższego czasu (z takich powodów), ale jestem pewien, że wiele innych osób uzna to za pomocne.
Basteln