Як знайти та замінити у Vim, не вводячи початкове слово?


53

Я хотів би оптимізувати свій робочий процес "знайти та замінити" у Vim. Це те, що я роблю часто, так як я впевнений, що і більшість із вас. Зазвичай щось поряд з рядками - скопіюйте блок і змініть назву змінної в декількох місцях. Я знаю, я знаю, що, ймовірно, спрацьовує ваш рефлекс "чому ти копіюєш і вставляєш код", але не будемо йти по цій дорозі ... Є безліч дійсних випадків використання :)

Я добре знаю команди пошуку та заміни: :sабо :%sмені це не подобається. Це змушує мене вводити як повну назву змінної, яку я шукаю, так і те, що я її змінюю. Можливо, є кращий спосіб виправити кількість набраних текстів :%s? Я часто використовую довгі описові імена змінних, так що це дійсно для мене вимикач угод. Мені також не подобається, як вводити назву змінної з нуля схильне до друку і може забирати час та мозкові сили, щоб виправити помилки. Я дуже вважаю за краще вводити його один раз, а потім копіювати та вставляти, щоб лише уникнути цього, якщо можливо.

У моєму поточному робочому процесі використовується деяка комбінація руху / yank / select / search / put для переміщення по файлу та заміни одного на інший. Це не є великим, але має перевагу уникати введення повних імен змінних. Мені може просто потрібно ввести перші кілька літер /або використовувати іншу команду руху (наприклад fx) залежно від того, що навколо, а потім натиснути, veщоб вибрати все слово. Я також не заперечую, що мені доводиться повторювати для кожного випадку. Я ніколи не здійснюю повну заміну пошуку, не підтверджуючи кожну зміну. Але було б набагато краще, якби я міг повторити дію заміни одним натисканням клавіші (що я не можу зробити цим методом). кожна заміна, як правило , що - щось на зразок nто veтоді p(або навіть гірше "0p)

Чи є швидший шлях?


1
.повторює останній "командний блок" (не впевнений у точному терміні. Наприклад: переміщення + дія + текст. для прикладу: cwtoto<Esc>(зміна від курсору до кінця слова на "тото"), c/foo<enter>bar<Esc>(перехід від курсору до трохи раніше " foo "та замінити на" bar "). Ви можете переміщатись (курсором, hjkl або числом + hjkl (чи не n разів), G (переходити до кінця файлу), / щось (перейти безпосередньо перед" щось ") тощо"), а потім натисніть, .щоб повторити той самий "командний блок"
Олів'є Дулак

Я використовую це зовсім небагато! Однак це не працює, якщо ви хочете вирвати слово, а потім замінити його іншим словом у кількох місцях. Це головний випадок використання, з яким я маю проблеми, і є щось подібне n ve "0p. Що, на жаль, неможливо повторити лише з.
Джоель,

5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Багатий

@Joel ви також можете записати макрос (qa, то все, що ви хочете, потім q: створює макрос a), а потім скористайтеся: @a для його відтворення (і, як завжди, 134 @ a, щоб зробити це 134 рази). макрос може бути настільки розробленим, як потрібно (наприклад: знайти щось, перейти до потрібного місця, потім змінити слово на це, а потім перейти до потрібного місця, щоб можна було знову зателефонувати в кращу позицію)
Олів'є Дулак,

1
Я дізнався більше корисних прийомів VIM з цього питання, ніж з будь-якого іншого питання на сайті. Дякую!
dotancohen

Відповіді:


81

Насправді у мене досить схожий робочий процес на ваш (копіювання та вставлення схожих блоків, потім використання :sдля зміни назв змінних), особливо коли я пишу безліч рядків, схожих, за винятком тієї змінної, яку вони використовують. Але є кілька речей, які я можу зробити вам корисними.

Загальні речі

  1. Перше, що допоможе вам, це зрозуміти, що :s//foo/gви заповните будь-який пошуковий термін, який ви використовували останнім, і замініть його на foo . Це одне може заощадити багато часу, але коли ви поєднаєте його з командою зірочки (шукайте слово під поточним курсором), це економить тонни часу. Наприклад, змінити все fooна spam, а не робити

    :%s/foo/spam/g
    

    Для чого потрібно ввести foo і спам (що може бути довгими змінними), ви можете просто перейти до foo та зробити:

    *:%s//spam/g
    

    Таким чином, ви можете швидко отримати ім’я змінної, яку ви змінюєте, шукаючи її, тоді вам не потрібно буде її вводити. Це також буде добре працювати з вашим поточним робочим процесом використання nвеликої кількості. Також корисно ввімкнути hlsearch, адже тоді ви зможете наочно сказати, де знаходяться змінні, які ви замінюєте.

    (Примітка боку: Команда Зірочки додадуть кордонів слів, тобто \<і \>навколо слова під курсором Якщо ви не хочете цього, використовуйте. g*Замість цього.)

  2. було б набагато краще, якби я міг повторити дію заміни одним натисканням клавіші

    Ви можете використовувати @:для повторної останньої команди заміну. Це добре, якщо ви хочете виконати :sкоманду на багатьох рядках, але не всі вони так :%sне спрацюють. Інший варіант - прапор підтвердження, тобто :%s///gc. Це дозволить вам вводити yчи nв кожному випадку визначати, хочете ви його замінити чи ні.

    Як зазначав Десті , ви також &можете повторно запустити останню команду-заміну, але зауважте, що це не збереже ваші прапори (наприклад, globalабо confirm). Це добре чи погано, залежить від вашої особистої думки. Якщо ви хочете скористатися цим, але якщо він також зберігатиме ваші прапори, ви можете зробити це

    nnoremap & :&&<cr>
    
  3. Якщо ви хочете змінити кожне виникнення в блоці, але не кожне збіг у файлі, ви завжди можете використовувати візуальний вибір блоку, який заповнить

    :'<,'>
    

    на ваш замінник, який обмежує його вибраними рядками. У поєднанні зі зірочним підходом я вважаю це надзвичайно корисним. Крім того, якщо ви робите декілька команд-замінників на одному блоці рядків, ви можете gvповторно вибрати ті самі рядки, щоб виконати іншу команду. (будь то :substituteнормальна команда)

    Якщо ви виконайте іншу команду заміщення, вам не потрібно буде повторно вибирати ті самі рядки, оскільки :'<,'> вони все одно будуть працювати в одних і тих же рядках. Однак це зручніше набирати

    gv:s
    

    а не

    :'<,'>s
    

Плагіни / відображення, які мені подобаються

  1. У мене є таке відображення, .vimrcяке розширює підхід до зірочок, так що мені потрібно лише ввести нове потрібне ім'я:

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

    Щоб скористатися цим, просто наведіть курсор на слово, яке ви хочете змінити, і введіть, <leader>rNewVarName<cr>і все закінчиться. Зауважте, що це замінить усі матчі, не даючи вам можливості підтвердити їх, тому вам це може не сподобатись сильно. Звичайно, ви могли просто змінити це

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

    Щоб дозволити опцію підтвердження.

    правити : Після Обидва відповіді MASS в і коментарі rcorre в , ви могли б також написати це як

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

    Що має перевагу в тому, щоб ваш реєстр без імені був таким же.

  2. Якщо ви одночасно перейменовуєте групи подібних змінних, наприклад, змінюючи

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    до

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    може замінити їх все індивідуально. Саме тут стане в нагоді tpope / vim-Скасувати . Ви можете замінити все це в одній команді на:

    :%S/foo/bar/g
    

    і він піклується про капіталізацію для вас.

Вони повинні вам дуже допомогти, але дайте мені знати, чи не існує чогось у цих підходах, які вам не подобаються чи відчуваєте. Я радий поговорити про більше підходів. :)


Рекомендоване читання:


О, я щойно тут дізнався кілька речей. Приємно! Альтернативний підхід, який ви можете застосувати, - це <leader>rc(або cr) додати це /cнаприкінці. Крім того, я set gdefaultвстановив, тому що майже ніколи не хочу замінювати один єдиний результат, так що це було б просто yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner

2
Яка фантастична відповідь!
Скотт Лундберг

2
Якщо ви використовуєте ctrl+r-ctrl+wдля картографування (як це запропонував @Mass), воно повинно працювати так само, але не зіпсується з вашим реєстром (що може бути, а може і не бажано)
rcorre

1
Одне, що потрібно пам’ятати, працюючи з текстами, які ви виділяєте (перед вирізанням / витріскуванням тощо), - це те, що ви можете ввести gv у звичайному режимі, щоб повторно виділити останній виділений текст.

1
Я написав плагін github.com/hauleth/sad.vim, який працює аналогічно методу 2. hovewer дозволяє використовувати .для подальшої заміни.
Hauleth

20

c_CTRL-RСімейство карт може поліпшити свій робочий процес зовсім небагато:

  1. Не потрібно вводити імена змінних під час використання :s/, просто використовуйте CTRL-R CTRL-Wдля вставки слова під курсором у командний рядок (застереження: це не уникає спеціальних символів, як *це робиться).

  2. Зробивши невелику зміну за допомогою ce, ви можете шукати старе слово за допомогою / CTRL-R -. Потім використовуйте .для повторення зміни.

  3. Якщо вам потрібно внести зміни в шаблон пошуку CTRL-R /, наявний пошук буде розміщено в командному рядку.

  4. Нарешті, ви можете помістити вміст будь-якого реєстру, використовуючи CTRL-R {register}


Влучне зауваження! Я завжди забуваю про Ctrl R. Я побачу, чи зможу я над цим працювати.
Джоель

2
Варто конкретно згадати Ctrl-R /? Я використовую це досить часто в :sкомандах, якщо хочу використовувати пошуковий термін, але майже не зовсім такий, як попередній.
Багатий

12

Є ще кілька методів, про які (на мій подив!) Ще не згадували.

Використання gnкоманди

gnпрацює як nкоманда, за винятком того, що окрім стрибків на матч, вона переходить у візуальний режим, вибравши весь матч.

Отже, щоб змінити слово (або що завгодно, що ви можете зіставити зі звичайним виразом!), Спочатку знайдіть його 1 , а потім натисніть, а потім cgnтекст, з яким потрібно замінити відповідність, і Escповерніться до звичайного режиму.

(Ви згадуєте, що іноді текст заміни, який ви хочете використати, зберігається в "0реєстрі yank - зауважте, що ви можете швидко ввести це без необхідності повторного введення його, натискаючи Ctrl+R0у режимі вставки.)

Ви можете перейти до наступного матчу та повторити зміну одним .натисканням клавіші!

Якщо є місця, де ви не хочете робити заміну, просто натисніть, unщоб скасувати зміну та перейти до наступного матчу.

1: Швидко, використовуючи інші пропозиції на цій сторінці, такі як *, Ctrl+RCtrl+Wтощо. Див. Також коментар Пітера Рінкера для отримання додаткових пропозицій про те, як спочатку заповнити пошук.

Використання режиму вибору

(Я дізнався про це з romainl «и пост на VIM subreddit . Я не знаю , чи придумав він це сам або просто передати його.)

Режим вибору трохи схожий на візуальний режим, за винятком випадків, коли ви починаєте вводити вибраний текст відразу замінюється тим, що ви вводите (наприклад, як працює вибір у більшості інших редакторів).

Таким чином, ви можете використовувати цю трійку відображень для швидкого пошуку / заміни, яку ви можете змінити середній файл, якщо необхідно:

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

Зображення у звичайному режимі : переходить у режим вибору зі словом, що знаходиться найближче до вибраного курсору.

Потім ви можете ввести заміну вибраного слова та ( без натискання Esc) використовувати:

Встановлення відображення режиму : Переходить до наступного матчу та переходить у режим вибору.

Потім ви можете або ввести нову заміну, або натиснути, .щоб використовувати:

Вибір режиму відображення : Повторно вводить останній вставлений текст - по суті, повторює останню вставку.


Я згадував gnметод у кінці своєї відповіді vi.stackexchange.com/a/13696/488 вчора про те, чого воно варте! Однак у вашій відповіді набагато приємніше форматування.
TankorSmash

1
@TankorSmash Так і зробили! Зверніть увагу, як я пропустив це. Зауважте, що фактичний метод, який я використовую, відрізняється від вашого, проте (я посилаюсь лише gnодин раз), і саме конкретний метод (а не лише gnкоманда) мене здивував, ще не згадувався.
Багатий

Я рекомендував би яке - то з «візуального зірка» плагіна для використання з gn. Надає більше варіантів пошуку. Візуальне відображення зірка бідної людини: xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>. Пов'язаний епізод Vimcasts: Операція щодо пошукових матчів за допомогою gn
Пітер Рінкер

1
Я створив плагін sad.vim, який допомагає в такому потоці.
Hauleth

7

Плагін https://github.com/terryma/vim-multiple-cursors здебільшого опікується цією проблемою.

Мій робочий процес зазвичай такий:

  1. Перемістіть курсор на слово для заміни.
  2. Тримайте, ctrl-nпоки все не вибрано.
  3. Натисніть cта почніть вводити заміну.

Зручно в тому, що ви також можете робити більш складні операції, ніж просто пошук-заміна, хоча я вважаю, що якщо ви забираєте це занадто далеко, плагін починає показувати деякі збої.


4

Ще один спосіб робити речі, про які ще не було сказано: Ви можете скористатися вікном командного рядка (відкритим q:у звичайному режимі - див. :help q:Додаткову інформацію), в якому ви маєте повний доступ до функцій автозаповнення Vim ( i_CTRL-N/ i_CTRL-Pта різних i_CTRL-Xпослідовностей) перебуваючи серед них).

У вікні командного рядка ви також можете використовувати команди звичайного режиму для редагування командного рядка, що, наприклад, означає, що ви можете витягувати та вставляти частини попередніх команд у історію вашого командного рядка.

Зауважте, що ваш курсор буде переміщений до вікна командного рядка при його використанні, тобто, CTRL-R CTRL-Wі подібні методи, що передбачають вставлення тексту під курсор, там не працюватимуть, на відміну від звичайного командного рядка.


1
Ви також можете відкрити вікно командного рядка, коли вже перебуваєте на півдорозі, ввівши свою :substituteкоманду, натиснувши<ctrl-f>
Rich

2

Рішення цього в чомусь між іншими відповідями для мене:

Скажіть, у вас 6 однакових рядків, і ви хочете замінити "яблуко" в середині двох:

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

Що я роблю:

  1. Я переходжу до початку тексту, який я хочу замінити, "яблуко"
  2. Натисніть, vщоб перейти у візуальний режим
  3. Переходьте до останнього слова, яке я хочу замінити, в даному випадку на кілька рядків 2j
  4. :s/<C-R><C-W>/який використовує візуальний вибір як діапазон для :s, а потім вставляє слово під курсор
  5. Я натиснув, <C-F>що переводить вас у режим, коли ви можете редагувати свою команду так, ніби це був буфер. Таким чином, ви можете використовувати будь-яке завершення або плагіни, які ви хочете вибрати слово заміни, і ви закінчили.

Все в одному рядку, потік для цього був би, /apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>але це виглядає гірше, ніж є.

У вашому випадку, ви можете приєднати /cдо :sкоманді , щоб пропустити ті , які ви не хочете, якщо діапазон містить кілька ви хочете пропустити: :s/<C-R><C-W/orange/c.

Як альтернативи (і , можливо , більш просто), ви могли б шукати для всього слова , яке ви хочете замінити, cщо висить його, а потім gnі .повторити пошук і застосувати ті ж зміни знову. Здавалося б, /\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.повторити його на три, потім пропустити екземпляр, а потім повторити його останній раз.


Описаний вами метод в кінці не працює для мене (у Vim 7.4, на який посилаються vim -Nu NONE). Це просто звучить, коли я натискаю перший .. Будь-яка ідея, чому це? У будь-якому випадку, було б простіше використовувати ciwзамість цього, viwа nне gnробити зміни таким чином.
Багатий

2

Це не зовсім пов’язано з питанням, але все ж повинно допомогти вибрати слово для заміни.

Поступовий пошук ( set incsearch) дозволяє вводити префікс потрібного слова, і vim автоматично переходитиме до першого появи префікса, коли ви все більше і більше вводитимете текст. Пошук буде підтверджено, якщо натиснути Enter і відкинути його на Esc (в останньому випадку курсор відскочить туди, де був до початку пошуку).

Однак поступовий пошук забезпечує ще одну корисну функцію. Коли ви вже набрали префікс слова і курсор знаходиться в правильному положенні, натисніть, <C-L>щоб додати символи слова під курсором до рядка пошуку. Це дозволяє вводити замінений рядок майже без натискань клавіш.

Так що , якщо я хочу замінити someOddVariableNameз evenOdderVariableName, я

  1. набрати /someOddVта прибути при виникненні someOddVariableName;
  2. натискайте і утримуйте, <C-L>поки повністю не someOddVariableNameз’явиться в рядку пошуку;
  3. натисніть Enter, щоб підтвердити пошук;
  4. :%s//evenOdderVariableName/gабо залежно від необхідної команди; Ви можете скористатися іншими відповідями тут.

2

Ви можете спочатку помістити ім'я змінної у буфер ( ywскопіювати слово), а потім скопіювати його :%s, натиснувши його <C-r>".


2

Якщо ви хочете замінити змінну в новому (вклеєному) блоці, у мене є дві пропозиції:

Автоматизована заміна

  • Перейдіть до першого рядка нового блоку, натисніть ma. Це маркування місця та його ім'я a.

  • Перейдіть до останнього рядка нового блоку, натисніть mb. Тепер у вас є дві названі локації.

  • Тепер замініть між цими двома лініями, наприклад , так: :'a,'bs/oldVarName/newVarName/g.

  • Тобто обсяг заміни знаходиться між вашими двома знаками.

Ручний спосіб

  • Перейдіть до першого рядка нового блоку.

  • Використовуйте /oldVarNameдля пошуку першого екземпляра oldVarName.

  • Змініть його так:, cwnewVarName<Esc>що змінить слово на newVarNameта втечу.

  • Використовуйте nдля переходу до наступного примірника oldVarName.

  • Використовуйте .для повторення останньої зміни (тобто повторення cw).


2

Багато інших відповіді тут вже охоплюють вбудовані способи поліпшення знахідкою і замінити робочий процес , але я хотів би згадати abolish.vim , плагін Тіма Папі. Він робить заміни там, де ви хочете охопити ширший спектр випадків, як правило, спричинених різницею у відмінку, або тому, що вам потрібно мати справу з формами однини та множини слова.

:S/child{,ren}/adult{,s}/gправильно перетворюється childна adult, Childrenв Adultsі CHILDв ADULT.

Якщо ви маєте справу зі словами, що стосуються верблюдів, вам потрібно вписати великі літери, де вони вам потрібні, в оригіналі та слова заміни.

%:S/wordWrap/textBreak/gправильно поводиться і з wordWrapі wordwrap.


0

Як уже вказували інші, якщо у вас курсор розміщений над словом, ви можете використовувати сімейство команд Ctrl + r, оскільки Ctrl + r Ctrl + w, мабуть, є найбільш підходящим для використання.

Цей метод, однак, працює лише для вставки одного слова (того, що знаходиться трохи нижче курсору), але що робити, якщо в буфері є кілька кількох частин тексту, які ми хочемо використати? Як ви не можете перемістити курсор, перебуваючи в командному рядку. Найкращий спосіб зробити це - скопіювати потрібні вам частини тексту в різні регістри, що можна зробити, префіксуючи "{reg_letter} (цитата та лист з реєстру) до оператора копіювання. Таким чином можна буде пізніше вставити різні слова в командний рядок за допомогою Ctrl + r {reg_letter} Це трохи незручно, але працює.

Але повертаючись до попередньої ідеї, було б чудово, якби ми перемістили позицію курсору над буфером під час написання шаблону. Таким чином ми могли "вибрати" текст з різних позицій за допомогою Ctrl + r Ctrl + w, тому я створив плагін, який робить саме це: EXtend.vim У плагіна також є багато інших функцій для здійснення операцій заміни.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.