Как найти и заменить в Vim, не вводя оригинальное слово?

53

Я бы хотел оптимизировать рабочий процесс поиска и замены в Vim. Это то, чем я занимаюсь часто, и я уверен, что большинство из вас тоже. Обычно что-то в этом роде - копирование блока и изменение имени переменной в нескольких местах. Я знаю, я знаю, что это, вероятно, вызывает ваш рефлекс «почему вы копируете и вставляете код», но давайте не будем идти по этому пути ... Есть много действительных вариантов использования :)

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

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

Есть ли более быстрый способ?

Joel
источник
1
.повторяет последний «командный блок» (не уверен в точном термине. Например: Move + Action + Text. Например: cwtoto<Esc>(переход от курсора к концу слова с помощью «toto»), 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 и spam (которые могут быть длинными переменными), вы можете просто перейти к 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>
    

    Чтобы разрешить опцию подтверждения.

    редактировать : следуя как ответу Мессы, так и комментарию rcorre , вы также можете написать это как

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

    Который имеет то преимущество, что ваш неназванный регистр остается неизменным.

  2. Если вы переименовываете группы одинаковых переменных одновременно, например, меняете

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    в

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

    это может быть боль, чтобы заменить их всех по отдельности. Здесь пригодится tpope / vim-abolish . Вы можете заменить все это одной командой:

    :%S/foo/bar/g
    

    и он заботится о капитализации для вас.

Это должно вам очень помочь, но дайте мне знать, если что-то в этих подходах вам не нравится или вам не хватает. Я рад поговорить о других подходах. :)


Рекомендуемое чтение:

DJMcMayhem
источник
О, я только что узнал несколько вещей здесь. Приятно! Альтернативный подход, который вы можете использовать, это <leader>rc(или cr) добавить это /cв конце. Кроме того, я set gdefaultустановил, потому что я почти никогда не хочу заменять один единственный результат, так что это будет просто yiw:%s/\<<C-r>"\>//<left><left>
Уэйн Вернер
2
Какой фантастический ответ!
Скотт Лундберг,
2
Если вы используете ctrl+r-ctrl+wотображение (как предложено @Mass), оно должно работать так же, но не будет портиться с вашим регистром янки (что может или не может быть желательно)
rcorre
1
Одна вещь, которую следует помнить при работе с выделенными блоками текста (перед вырезанием / рывком и т. Д.), Это то, что вы можете ввести gv в обычном режиме, чтобы повторно выделить последний выделенный текст.
1
Я написал плагин github.com/hauleth/sad.vim, который работает аналогично методу 2. hovewer позволяет использовать его .для выполнения дальнейших замен.
Хаулет
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регистре янки - обратите внимание, что вы можете быстро ввести его, не вводя его повторно, нажав Ctrl+R0в режиме вставки.)

Затем вы можете перейти к следующему совпадению и повторить изменение одним .нажатием клавиши!

Если есть места, где вы не хотите производить замену, просто нажмите, unчтобы отменить изменения и перейти к следующему совпадению.

1: Быстро, используя другие предложения на этой странице, такие как *, Ctrl+RCtrl+Wи т. Д. См. Также комментарий Питера Ринкера для получения дополнительных предложений о том, как изначально заполнить ваш поиск.

Использование режима выбора

(Я это из узнал romainl «s запись на 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
Peter Rincker
1
Я создал плагин sad.vim, который поможет с таким потоком.
Хаулет
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.повторить это на трех, затем пропустить экземпляр, а затем повторить в последний раз.

TankorSmash
источник
Метод, который вы описали в конце, не работает для меня (в 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>".

vdudouyt
источник
2

Если вы хотите заменить переменную в новом (вставленном) блоке, у меня есть два предложения:

Автоматическая замена

  • Перейти к первой строке нового блока, нажать ma. Это отмечает место и называет его a.

  • Перейти к последней строке нового блока, нажмите mb. Теперь у вас есть два названных местоположения.

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

  • То есть объем замены находится между вашими двумя отметками.

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

  • Перейти к первой строке нового блока.

  • Используйте, /oldVarNameчтобы найти первый экземпляр oldVarName.

  • Измените это так:, cwnewVarName<Esc>который изменит слово на newVarNameи ударит escape.

  • Используйте nдля перехода к следующему экземпляру oldVarName.

  • Используйте, .чтобы повторить последнее изменение (т.е. повторяет это cw).

Скрытый гость
источник
2

Многие другие ответы здесь уже охватывают встроенные способы улучшить рабочий процесс поиска и замены, но я хотел бы упомянуть abolish.vim , плагин от Tim Pope. Это делает замены там, где вы хотите охватить более широкий диапазон падежей, как правило, из-за различий в падеже или потому, что вам нужно иметь дело как с единственной, так и с множественной формой слова.

:S/child{,ren}/adult{,s}/gправильно конвертирует childв adult, Childrenв Adultsи CHILDв ADULT.

Если вы имеете дело со словами в верблюжьих словах, вам нужно указать заглавные буквы там, где они вам нужны, в исходных и заменяющих словах.

%:S/wordWrap/textBreak/gправильно обрабатывает как wordWrapи wordwrap.

Haegin
источник
0

Как уже отмечали другие, если вы поместите курсор поверх своего слова, вы можете использовать семейство команд Ctrl + r, наиболее подходящим для использования является Ctrl + r Ctrl + w.

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

Но, возвращаясь к предыдущей идее, было бы здорово, если бы мы переместили позицию курсора над буфером во время написания паттерна. Таким образом, мы могли бы «выбирать» текст из разных позиций с помощью Ctrl + r Ctrl + w, поэтому я создал плагин, который делает именно это: EXtend.vim Плагин также имеет много других функций для выполнения операций замещения.

Сол Аксель Мартинес Ортиз
источник