Jak znaleźć i zamienić w Vimie bez konieczności wpisywania oryginalnego słowa?

53

Chciałbym zoptymalizować mój przepływ pracy „znajdź i zamień” w Vimie. Często to robię, bo jestem pewien, że większość z was też. Zwykle coś w stylu - skopiuj blok i zmień nazwę zmiennej w kilku miejscach. Wiem, wiem, to prawdopodobnie wyzwala odruch „dlaczego kopiujesz i wklejasz kod”, ale nie idźmy tą drogą… Jest wiele ważnych przypadków użycia :)

Jestem świadom poszukiwaniu i zastąpienie polecenia: :salbo :%sale ja ich nie lubię. Zmusza mnie do wpisania zarówno pełnej nazwy zmiennej, której szukam, jak i tego, na co ją zmieniam. Może jest lepszy sposób, aby naprawić ilość pisania :%s? Często używam długich opisowych nazw zmiennych, więc jest to dla mnie naprawdę przełom. Nie podoba mi się również to, jak pisanie od podstaw nazwy zmiennej jest podatne na literówki i może pochłaniać czas i siłę roboczą podczas wyszukiwania literówek. Wolę pisać raz, a potem kopiować i wklejać, aby w miarę możliwości całkowicie tego uniknąć.

Mój obecny przepływ pracy wykorzystuje kombinację ruchów / yank / select / search / put, aby poruszać się po pliku i zamieniać jeden po drugim. To nie jest świetne, ale ma tę zaletę, że pozwala uniknąć wpisywania pełnych nazw zmiennych. Być może będę musiał po prostu wpisać kilka pierwszych liter /lub użyć innego polecenia ruchu (np. fx) W zależności od tego, co jest w pobliżu, a następnie naciśnij, veaby zaznaczyć całe słowo. Nie przeszkadza mi również to, że muszę powtarzać za każdym razem. Nigdy nie dokonuję pełnego znalezienia zamiennika bez potwierdzenia każdej zmiany. Byłoby jednak znacznie lepiej, gdybym mógł powtórzyć czynność zastępowania jednym naciśnięciem klawisza (czego nie mogę zrobić z tą metodą). każda wymiana jest zazwyczaj coś w nczym vewtedy p(lub nawet gorzej "0p)

Czy jest szybszy sposób?

Joel
źródło
1
.powtarza ostatni „blok poleceń” (nie jestem pewien dokładnego terminu. na przykład: move + action + text. na przykład: cwtoto<Esc>(zmień kursor na koniec słowa za pomocą „toto”), c/foo<enter>bar<Esc>(zmień kursor na tuż przed „ foo ”i zamień na„ bar ”). następnie możesz przenieść (kursorem, hjkl lub liczbą + hjkl (robi to n razy), G (przejdź na koniec pliku), / coś (przejdź tuż przed„ coś ”) itp.), a następnie naciśnij przycisk, .aby ponownie wykonać ten sam„ blok poleceń ”
Olivier Dulac,
Używam tego całkiem sporo! Jednak to nie działa, jeśli chcesz szarpnąć słowo, a następnie zastąpić je innym słowem w wielu miejscach. Jest to główny przypadek użycia, z którym mam problem i wygląda mniej więcej tak n ve "0p. Które niestety nie da się powielić za pomocą.
Joel
5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Bogaty
@Joel możesz także nagrać makro (qa, potem wszystko, co chcesz, a następnie q: tworzy makro a), a następnie użyj: @a, aby go odtworzyć (i, jak zwykle, 134 @ a, aby zrobić to 134 razy). makro może być tak rozbudowane, jak to konieczne (przykład: znajdź coś, przejdź do odpowiedniego miejsca, a następnie zmień słowo na to, a następnie przejdź do odpowiedniego miejsca, aby móc zostać ponownie wywołany w lepszej pozycji)
Olivier Dulac
1
Nauczyłem się bardziej przydatnych sztuczek VIM z tego pytania niż z jakiegokolwiek innego pytania na stronie. Dziękuję Ci!
dotancohen

Odpowiedzi:

81

W rzeczywistości mam bardzo podobny przepływ pracy (kopiowanie i wklejanie bloków, które są podobne, a następnie używanie :sdo zmiany nazw zmiennych), zwłaszcza gdy piszę wiele wierszy, które są podobne, z wyjątkiem używanych przez nich zmiennych. Ale robię kilka rzeczy, które mogą ci się przydać.

Ogólne rzeczy vim

  1. Pierwszą rzeczą, która pomoże ci, jest uświadomienie sobie, że :s//foo/gwypełnisz wyszukiwane hasło użyte ostatnio i zastąpisz je foo . Samo to może zaoszczędzić dużo czasu, ale jeśli połączysz je z poleceniem gwiazdką (wyszukaj słowo pod bieżącym kursorem), zaoszczędzisz mnóstwo czasu. Na przykład, aby zmienić wszystko foona spam, zamiast robić

    :%s/foo/spam/g
    

    Co wymaga wpisania zarówno foo, jak i spamu (które mogą być długimi zmiennymi), możesz po prostu przejść do foo i wykonać:

    *:%s//spam/g
    

    W ten sposób możesz szybko uzyskać nazwę zmiennej, którą zmieniasz, wyszukując ją, wtedy nie będziesz musiał jej wpisywać. Będzie to również dobrze działać przy bieżącym przepływie pracy przy częstym używaniu n. Pomocne jest również hlsearchwłączenie, ponieważ wtedy możesz wizualnie określić, gdzie znajdują się zmienne, które zastępujesz.

    (dygresja: Komenda gwiazdka doda granic słów, że jest \<i \>wokół słowa pod kursorem Jeśli nie chcesz tego używać. g*zamiast).

  2. byłoby znacznie lepiej, gdybym mógł powtórzyć czynność zastąpienia jednym naciśnięciem klawisza

    Możesz użyć @:do ponownego wykonania ostatniego polecenia zastępczego. To dobrze, jeśli chcesz wykonać :spolecenie na wielu liniach, ale nie wszystkie, więc :%snie będą działać. Inną opcją jest flaga potwierdzenia, tj :%s///gc. Umożliwi to wpisanie ylub nprzy każdym wystąpieniu, aby zdecydować, czy chcesz go zastąpić, czy nie.

    Jak wskazał Desty , możesz również zrobić &ponowne uruchomienie ostatniego polecenia zastępczego, ale pamiętaj, że to nie zachowa twoich flag (takich jak globallub confirm). To, czy to dobrze, czy źle, zależy od Twojej osobistej opinii. Jeśli chcesz tego użyć, ale masz też swoje flagi, możesz to zrobić

    nnoremap & :&&<cr>
    
  3. Jeśli chcesz zmienić każde wystąpienie w bloku, ale nie każde dopasowanie w pliku, zawsze możesz użyć wizualnego wyboru bloku, który wypełni

    :'<,'>
    

    do twojego zamiennika, co ogranicza go do wybranych linii. W połączeniu z podejściem z gwiazdką uważam to za niezwykle przydatne. Ponadto, jeśli tworzysz wiele poleceń zastępczych w tym samym bloku linii, możesz użyć gvtego samego wiersza, aby wykonać inne polecenie. (czy to :substitutepolecenie normalne)

    Jeśli wykonasz inne polecenie zastępcze, nie musisz ponownie wybierać tych samych linii, ponieważ :'<,'> nadal będą działać na tych samych liniach. Jednak wygodniej jest pisać

    gv:s
    

    zamiast

    :'<,'>s
    

Wtyczki / mapowania lubię

  1. Mam w sobie następujące mapowanie, .vimrcktóre rozszerza podejście do gwiazdki, więc muszę tylko wpisać nową pożądaną nazwę:

    "(R)eplace all
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>
    

    Aby tego użyć, po prostu ustaw kursor na słowie, które chcesz zmienić, i wpisz <leader>rNewVarName<cr>i gotowe. Pamiętaj, że spowoduje to zastąpienie wszystkich dopasowań bez możliwości ich potwierdzenia, więc może Ci się to nie podobać. Oczywiście możesz to zmienić

    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>
    

    Aby zezwolić na opcję potwierdzenia.

    edytuj : Po odpowiedzi zarówno Mszy, jak i komentarza rcorre , możesz również napisać to jako

    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>
    

    Co ma tę zaletę, że twój bezimienny rejestr pozostaje taki sam.

  2. Jeśli zmieniasz nazwy grup podobnych zmiennych w tym samym czasie, na przykład zmieniając

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    do

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    zastąpienie ich wszystkich osobno może być uciążliwe. Tutaj przydaje się tpope / vim-abolish . Możesz zamienić je wszystkie w jednym poleceniu na:

    :%S/foo/bar/g
    

    i dba o twoje wielkie litery.

To powinno ci bardzo pomóc, ale daj mi znać, jeśli jest coś w tych podejściach, które ci się nie podobają lub których brakuje. Cieszę się, że mogę mówić o innych podejściach. :)


Rekomendowane lektury:

DJMcMayhem
źródło
Och, właśnie nauczyłem się tutaj kilku rzeczy. Miły! Alternatywnym podejściem, które możesz zastosować jest <leader>rc(lub cr) dodanie tego /cna końcu. Ponadto set gdefaultustawiłem, ponieważ prawie nigdy nie chcę zastąpić jednego wyniku, więc byłoby to po prostu yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner
2
Cóż za fantastyczna odpowiedź!
Scott Lundberg,
2
Jeśli użyjesz ctrl+r-ctrl+wmapowania (jak sugeruje @Mass), powinno działać tak samo, ale nie zadziała z twoim rejestrem yank (co może, ale nie musi być pożądane)
rcorre 26.09.17
1
Jedną z rzeczy, o których należy pamiętać podczas pracy z zaznaczonymi blokami tekstu (przed wycinaniem / szarpaniem itp.) Jest to, że możesz wejść w gv w trybie normalnym, aby ponownie podświetlić ostatnio podświetlony tekst.
1
Pisałem github.com/hauleth/sad.vim plugin, który działa podobnie do metody 2. hovewer pozwala używać .do wykonywania kolejnych podstawień.
Hauleth
20

c_CTRL-RRodzina map może poprawić przepływ pracy sporo:

  1. Podczas używania nie musisz wpisywać nazw zmiennych :s/, po prostu użyj, CTRL-R CTRL-Waby wstawić słowo pod kursorem do wiersza poleceń (zastrzeżenie: nie unika znaków specjalnych, jak to *robi).

  2. Po wprowadzeniu niewielkiej zmiany za pomocą ce, możesz wyszukać stare słowo za pomocą / CTRL-R -. Następnie użyj, .aby powtórzyć zmianę.

  3. Jeśli chcesz wprowadzić zmiany we wzorcu wyszukiwania, umieści CTRL-R /istniejące wyszukiwanie w wierszu polecenia.

  4. Na koniec możesz umieścić zawartość dowolnego rejestru za pomocą CTRL-R {register}

Masa
źródło
Słuszna uwaga! Zawsze zapominam o Ctrl R. Zobaczę, czy też mogę w tym pracować.
Joel
2
Warto szczególnie wspomnieć Ctrl-R /? Używam tego dość często w :spoleceniach, jeśli chcę użyć wyszukiwanego terminu, który jest prawie, ale nie całkiem taki sam, jak poprzedni.
Bogaty
12

Jest jeszcze kilka metod, które (ku mojemu zaskoczeniu!) Nie zostały jeszcze wspomniane.

Korzystanie z gnpolecenia

gndziała jak npolecenie, tyle że oprócz przeskakiwania do meczu wchodzi w tryb wizualny z zaznaczonym całym dopasowaniem.

Aby więc zmienić słowo (lub cokolwiek, co możesz dopasować wyrażeniem regularnym!) Najpierw wyszukaj je 1 , a następnie naciśnij, cgna następnie tekst, który chcesz zastąpić dopasowaniem, i Escpowróć do trybu normalnego.

(Wspominasz, że czasami tekst zastępczy, którego chcesz użyć, jest przechowywany w "0rejestrze szarpnięć - pamiętaj, że możesz go szybko wprowadzić bez konieczności ponownego wpisywania, naciskając Ctrl+R0w trybie wstawiania).

Możesz następnie przejść do następnego meczu i powtórzyć zmianę jednym .naciśnięciem klawisza!

Jeśli są jakieś miejsca, w których nie chcesz dokonywać zamiany, po prostu naciśnij, unaby cofnąć zmianę i przejść do następnego meczu.

1: szybszego, wykorzystując inne sugestie na tej stronie, takich jak *, Ctrl+RCtrl+Witp Zobacz również komentarz Petera Rincker za więcej sugestii o tym, jak początkowo wypełnić swoje wyszukiwanie.

Korzystanie z trybu wyboru

(Nauczyłem się tego od romainl „s postu na subreddita vim . Nie wiem, czy wymyślił to sam lub po prostu przekazania go).

Tryb zaznaczania jest trochę podobny do trybu wizualnego, z tym wyjątkiem, że jeśli zaczniesz pisać zaznaczony tekst zostanie natychmiast zastąpiony tym, co piszesz (np. Jak zaznaczenie działa w większości innych edytorów).

W ten sposób możesz użyć tego trio mapowań, aby szybko znaleźć / zamienić, w razie potrzeby możesz zmienić plik środkowy:

nnoremap § *``gn<C-g>
inoremap § <C-o>gn<C-g>
snoremap <expr> . @.

Mapowanie w trybie normalnym : Wejście do trybu wyboru ze słowem najbliższym wybranym kursorem.

Następnie możesz wpisać zamiennik dla wybranego słowa i ( bez naciskania Esc) użyć:

Wstaw tryb mapowania : Przeskakuje do następnego dopasowania i przechodzi do trybu wyboru.

Następnie możesz wpisać nowy zamiennik lub nacisnąć, .aby użyć:

Wybierz tryb mapowania : Ponownie wprowadza ostatnio wstawiony tekst - w zasadzie powtarza ostatni wstawiony tekst.

Bogaty
źródło
Zrobiłem wspomnieć gnmetodę na końcu moją odpowiedź vi.stackexchange.com/a/13696/488 wczoraj za co jej wartości! Twoja odpowiedź ma jednak znacznie ładniejsze formatowanie.
TankorSmash
1
@TankorSmash Więc zrobiłeś! Zwróć uwagę, jak mi tego brakowało. Zauważ, że faktyczna metoda, której używam, różni się od twojej (wywołuję tylko gnraz) i jest to szczególna metoda (a nie tylko gnpolecenie), która mnie zaskoczyła, nie została jeszcze wspomniana.
Bogaty
Polecam mający jakiś z „star wizualny” wtyczki do korzystania z gn. Daje ci więcej opcji wyszukiwania. Wizualne odwzorowanie gwiazda biedaka: xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Powiązany odcinek Vimcasts: Operacja wyszukiwania wyników za pomocą gn
Peter Rincker
1
Stworzyłem wtyczkę sad.vim, która pomaga w takim przepływie.
Hauleth,
7

Wtyczka https://github.com/terryma/vim-multiple-cursors zajmuje się tym problemem w przeważającej części.

Mój przepływ pracy jest zwykle następujący:

  1. Przesuń kursor do słowa, które chcesz zastąpić.
  2. Przytrzymaj, ctrl-naż wszystko zostanie wybrane.
  3. Naciśnij ci zacznij pisać zamiennik.

Wygodną rzeczą jest to, że możesz wykonywać bardziej skomplikowane operacje niż tylko wyszukiwanie-zamień, chociaż uważam, że jeśli posuniesz się za daleko, wtyczka zacznie wykazywać pewne usterki.

Chiel ten Brinke
źródło
4

Kolejny sposób na zrobienie rzeczy, o których jeszcze nie wspomniano: Możesz użyć okna wiersza poleceń (otwartego q:w trybie normalnym - :help q:więcej informacji), w którym masz pełny dostęp do funkcji autouzupełniania Vima ( i_CTRL-N/ i_CTRL-Pi różnych i_CTRL-Xsekwencji bycie wśród nich).

W oknie wiersza poleceń możesz także używać poleceń trybu normalnego do edycji wiersza poleceń, co oznacza na przykład, że możesz szarpać i wklejać części poprzednich poleceń w historii wiersza poleceń.

Zauważ, że podczas korzystania z niego kursor zostanie przeniesiony do okna wiersza polecenia, co oznacza, że CTRL-R CTRL-Wi podobne metody polegające na wstawianiu tekstu pod kursorem nie będą tam działać, w przeciwieństwie do zwykłego wiersza poleceń.

Slade
źródło
1
Możesz także otworzyć okno wiersza polecenia, gdy jesteś już w połowie wprowadzania :substitutepolecenia, naciskając<ctrl-f>
Rich
2

Dla mnie rozwiązanie jest nieco pomiędzy innymi odpowiedziami:

Załóżmy, że masz 6 identycznych wierszy i chcesz zastąpić „jabłko” w środkowych dwóch:

I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days

To co robię to:

  1. Przechodzę do początku tekstu, który chcę zastąpić „jabłko”
  2. Naciśnij, vaby przejść do trybu wizualnego
  3. Przejdź do ostatniego słowa, które chcę zastąpić, w tym przypadku kilka wierszy, więc 2j
  4. :s/<C-R><C-W>/który wykorzystuje zaznaczenie wizualne jako zakres :s, a następnie wstawia słowo pod kursorem
  5. Uderzyłem, <C-F>co powoduje przejście do trybu, w którym możesz edytować swoje polecenie tak, jakby to był bufor. Możesz więc użyć dowolnego uzupełnienia lub wtyczek, aby wybrać słowo zastępcze i gotowe.

Wszystko w jednej linii, przepływ byłby taki, /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>ale wygląda to gorzej niż jest w rzeczywistości.

W twoim przypadku można dołączyć /cdo :spolecenia, aby pominąć te, których nie chcesz, jeśli zakres zawiera kilka chcesz pominąć: :s/<C-R><C-W/orange/c.

Alternatywnie (a może prościej), można szukać całego wyrazu, który chcesz zastąpić, cmień, a potem gni .powtórzyć szukanie i stosują tę samą zmianę ponownie. Wygląda na to, że /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.powtarza się trzy, następnie pomija instancję, a następnie powtarza ją po raz ostatni.

TankorSmash
źródło
Metoda, którą opisujesz na końcu, nie działa dla mnie (w Vimie 7.4, wywoływanym z vim -Nu NONE). Po pierwszym naciśnięciu wydaje sygnał dźwiękowy .. Masz pomysł, dlaczego tak jest? W każdym razie łatwiej byłoby używać ciwzamiast viwi nzamiast gnwprowadzać zmiany w ten sposób.
Bogaty
2

Nie jest to całkowicie związane z pytaniem, ale nadal powinno pomóc w wyborze słowa do zastąpienia.

Przyrostowe wyszukiwanie ( set incsearch) pozwala wpisać prefiks słowa, które musisz znaleźć, a vim automatycznie przeskoczy do pierwszego wystąpienia tego słowa, gdy będziesz pisać coraz więcej. Wyszukiwanie zostanie potwierdzone, jeśli naciśniesz Enter i odrzucisz klawisz Esc (w tym drugim przypadku kursor wróci do miejsca, w którym był przed rozpoczęciem wyszukiwania).

Jednak wyszukiwanie przyrostowe zapewnia jeszcze jedną przydatną funkcję. Gdy już wpiszesz prefiks słowa, a kursor znajdzie się we właściwej pozycji, naciśnij, <C-L>aby dodać znaki ze słowa pod kursorem do wyszukiwanego ciągu. Pozwala to wprowadzić zastąpiony ciąg bez prawie żadnych naciśnięć klawiszy.

Tak więc, jeśli chcę wymienić someOddVariableNamez evenOdderVariableName, ja

  1. wpisz /someOddVi dotrzyj do wystąpienia someOddVariableName;
  2. naciśnij i przytrzymaj, <C-L>someOddVariableNamepojawi się całkowicie w linii wyszukiwania;
  3. naciśnij Enter, aby potwierdzić wyszukiwanie;
  4. :%s//evenOdderVariableName/glub cokolwiek zastąpi potrzebne polecenie; możesz tutaj skorzystać z innych odpowiedzi.
Iwan Smirnow
źródło
2

Możesz najpierw umieścić nazwę zmiennej w buforze ( ywaby skopiować słowo), a następnie skopiować i wkleić ją :%s, naciskając <C-r>".

vdudouyt
źródło
2

Jeśli chcesz zastąpić zmienną w nowym (wklejonym) bloku, mam dwie sugestie:

Zautomatyzowana wymiana

  • Przejdź do pierwszego wiersza nowego bloku, naciśnij ma. Oznacza to lokalizację i nadanie jej nazwy a.

  • Przejdź do ostatniego wiersza nowego bloku, naciśnij mb. Teraz masz dwie nazwane lokalizacje.

  • Teraz wymienić między dwiema liniami, tak: :'a,'bs/oldVarName/newVarName/g.

  • Oznacza to, że zakres zamiany znajduje się między dwoma znakami.

Sposób ręczny

  • Przejdź do pierwszego wiersza nowego bloku.

  • Użyj, /oldVarNameaby znaleźć pierwsze wystąpienie oldVarName.

  • Zmień to w ten sposób:, cwnewVarName<Esc>co zmieni słowo na newVarNamei uderzy w ucieczkę.

  • Użyj, naby przejść do następnej instancji oldVarName.

  • Użyj, .aby powtórzyć ostatnią zmianę (tj. Powtarza to cw).

Ukryty gość
źródło
2

Wiele innych odpowiedzi tutaj obejmuje już wbudowane sposoby poprawy przepływu pracy znajdowania i zastępowania, ale chciałem wspomnieć o abolish.vim , wtyczce Tima Pope'a. Dokonuje zamienników tam, gdzie chcesz objąć szerszy zakres przypadków, zwykle spowodowanych przez różnice w przypadkach lub ponieważ musisz radzić sobie zarówno z pojedynczą, jak i mnogą formą słowa.

:S/child{,ren}/adult{,s}/gpoprawnie konwertuje childna adult, Childrenna Adultsi CHILDna ADULT.

Jeśli masz do czynienia ze słowami wielbłąda, musisz zamienić stolice tam, gdzie są potrzebne, w słowach oryginalnych i zastępczych.

%:S/wordWrap/textBreak/gpoprawnie obsługuje zarówno wordWrapi wordwrap.

Haegin
źródło
0

Jak już zauważyli inni, jeśli kursor znajduje się nad słowem, możesz użyć rodziny poleceń Ctrl + r, ponieważ Ctrl + r Ctrl + w jest prawdopodobnie najbardziej odpowiednim do użycia.

Ta metoda działa jednak tylko w przypadku wstawienia jednego słowa (tego tuż pod kursorem), ale co zrobić, jeśli w buforze znajduje się kilka części tekstu, których chcemy użyć? Gdy nie możesz poruszać kursorem w wierszu polecenia. Najlepszym sposobem na to jest skopiowanie potrzebnych fragmentów tekstu do różnych rejestrów, co można zrobić, poprzedzając operatora „{reg_letter} (cytat i litera z rejestru) operatorowi kopiowania. W ten sposób będzie można wstaw różne słowa w wierszu poleceń później za pomocą Ctrl + r {reg_letter}. To trochę dziwne, ale działa.

Ale wracając do poprzedniego pomysłu, byłoby wspaniale, gdybyśmy przesuwali kursor nad buforem podczas pisania wzoru. W ten sposób mogliśmy „wybierać” tekst z różnych pozycji za pomocą Ctrl + r Ctrl + w, więc stworzyłem wtyczkę, która to robi: EXtend.vim Wtyczka ma również wiele innych funkcji do wykonywania operacji podstawiania.

Saul Axel Martinez Ortiz
źródło