Как быстро изменить имена переменных в Vim?

97

Я использую Vim для чтения большого количества кода C и Perl, содержащего много однобуквенных имен переменных.

Было бы неплохо иметь какую-нибудь команду для изменения имени переменной на что-то более значимое, пока я читаю код, чтобы я мог быстрее прочитать остальную часть.

Есть ли в Vim какая-нибудь команда, которая позволит мне сделать это быстро?

Я не думаю, что регулярные выражения будут работать, потому что:

  1. одно и то же однобуквенное имя может иметь разные цели в разных блоках области видимости; а также

  2. та же комбинация букв может быть частью другого более длинного имени переменной, строкового литерала или комментария.

Есть какие-нибудь известные решения?

Адитья Мукхерджи
источник

Ответы:

200

Ниже показано, как переименовать переменную, которая определена в текущей области. {} .

Переместите курсор к использованию переменной. Нажмите gd. Что означает - подведите курсор к определению. Теперь нажмите [{- это приведет вас к началу обзора. Нажмите V- включит выбор Visual Line. Нажатие %- перейдет к противоположному, }таким образом выберет всю область. Нажать :s/- запуск замещающей команды. <C-R>/- вставит шаблон, соответствующий имени переменной (то имя, которое вы использовали до нажатия gd). /newname/gc<CR>- будет инициировать поиск и заменять с подтверждением при каждом совпадении.

Теперь вам нужно записать макрос, а еще лучше - сопоставить ключ.

Вот окончательные сопоставления:

" For local replace
nnoremap gr gd[{V%::s/<C-R>///gc<left><left><left>

" For global replace
nnoremap gR gD:%s/<C-R>///gc<left><left><left>

Поместите это в свой .vimrcили просто выполните. После этого нажатие grна локальную переменную приведет вас к :sкоманде, где вы просто должны ввести new_variable_nameи нажать Enter.

Николай Голубев
источник
40
+1 за изучение более 5 новых приемов Vim, которые я должен был знать раньше. Спасибо
Кенни Мейер
6
Примечание: gd не помогает найти переменную в непосредственной области определения в режиме Javascript в macvim. Это приводит меня к первому использованию имени переменной в некоторой функции в файле :( Если вы находитесь в какой-то области и хотите выбрать эту область, "va {" - "визуальный выбор вокруг {" - это путь Я думаю.
Srikumar
3
Немного улучшенная версия: gist.github.com/048616a2e3f5d1b5a9ad запрашивает пользователя, показывает старое имя, восстанавливает положение курсора после замены
Энди Рэй
7
+1 очень хороший вимжиниринг (кажется, я только что придумал новый термин). Примечание: мне действительно нужно было :дважды нажать двоеточие ( ). Первый раз нажал, увидел :'<,'>. Если бы я просто печатал s/оттуда, это не сработало; Мне пришлось ввести еще одно двоеточие перед s/.
Kelvin
6
Обратите внимание, что вам не нужна <C-R>команда замены, просто пропуск поиска приведет к тому, что vim будет использовать последний найденный шаблон, который в этом случае был создан из gdкоманды. Так что :s//<newname>/gcдостаточно просто делать .
shangxiao
12

Я знаю, что это старый вопрос, и способ @mykola-golubyev, очевидно, является лучшим ответом для конкретного случая в вопросе OP (который, я полагаю, проходит через запутанный код, где у вас, вероятно, будет несколько блоков с одинаковыми именами var) ; но с таким названием вопроса многие люди, приходящие сюда из поисков Google, вероятно, ищут менее зависящие от ситуации способы переименования переменных в VIM - и они могут быть более краткими

Я удивлен, что никто не предложил такой способ:

* :s// НОВОЕ ИМЯ /gc

То *же, что иgn - он ищет следующее вхождение слова под курсором и становится последним искомым шаблоном, поэтому, когда вы опускаете шаблон поиска в команде замены, VIM предполагает, что это шаблон для поиска.

Для небольшого количества копий var еще более быстрый:

* cw NEWNAME, <esc> затем повторите n.для других случаев

Поиск вхождения, cwэто команда для изменения слова , nпереходит к следующему вхождению последнего искомого термина и .повторяет последнюю команду (которая представляет собой изменение слова на NEWNAME )

(Благодарности за то, что я все это знаю, идут на @doomedbunnies на Reddit )

Еще один крутой трюк: ( кредиты на @ nobe4 )

* cgn NEWNAME, <esc> затем повторите .для других случаев

cgnэто «изменить то, что является результатом (найти следующее вхождение)». Теперь, когда это последняя команда , вам не нужно nпереходить к следующему вхождению, поэтому снова меньше штрихов и, что более важно, нет необходимости чередовать nи. . Но, очевидно, у этого есть недостаток, заключающийся в том, что у него нет возможности пропустить событие.

Вот некоторые преимущества:

  • нет сопоставления, нет .vimrc (или init.vim), поэтому вы можете использовать его в любой копии VIM, с которой столкнетесь (например, быстрая задача на каком-то VPS или машине вашего друга, где настройка VIM по-своему нарушит цель `` быстрого '' )
  • использование *или gnдля выбора слова выполняется очень быстро - всего одно нажатие клавиши (ну, скажем, 1,5)
  • с помощью *или gnубедитесь, что вы не найдете совпадений внутри других слов, как и :%s/<C-R>//gcделает. Лучше печатать :%s/\<OLDNAME\>/NEWNAME/gcот руки: лично я часто забываю использовать\< вещи, чтобы ограничить совпадение только целыми словами.
  • Неиспользование области видимости приведет только к нескольким дополнительным штрихам, nчтобы пропустить нежелательные совпадения - возможно, даже меньше, чем дополнительные штрихи, необходимые для ограничения области действия. В обычных условиях ваши переменные, скорее всего, так или иначе локализованы для определенного блока кода.
Иван Барцов
источник
10

AFAIK, в VIM нет реальной поддержки рефакторинга. При переименовании с целью рефакторинга я обычно принимаю следующие меры предосторожности:

  1. Ограничьте объем изменения моими знаками использования.
  2. При вводе регулярного выражения заключите имя в скобки с \ <и>. Это приведет к совпадению всего слова, что уменьшит количество возможных неправильных переименований.
  3. Не выполняйте многострочную замену, чтобы снизить вероятность плохой замены
  4. Внимательно просмотрите код diff, если это что-то кроме небольшого изменения.

Мое окончательное изменение выглядит примерно так

:'a,'bs/\<foo\>/bar

Я хотел бы ошибиться в том, что для VIM не существует инструмента рефакторинга, но я его не видел.

ДжаредПар
источник
В Perl вы также можете добавить тип в шаблон поиска. т.е. $ для скаляров, @ для массивов,% для хэшей
Натан Феллман
@Nathan: к сожалению, в Perl (5.X) это немного сложнее, так как сигил массивов и хешей меняется при использовании:% hash -> весь хеш, $ hash {key} -> одно значение, @hash { qw / ab /} хэш-фрагмент.
user55400 02
9

Поместите это в свой .vimrc

" Function to rename the variable under the cursor
function! Rnvar()
  let word_to_replace = expand("<cword>")
  let replacement = input("new name: ")
  execute '%s/\(\W\)' . word_to_replace . '\(\W\)/\1' . replacement . '\2/gc'
endfunction

Назовите это с :call Rnvar()

expand("<cword>")получает слово под курсором. Строка поиска использует %для файла-сферы, а также \(\W\)шаблоны поиска символов без слов на границе слова , чтобы заменить, и сохранять их в переменных \1и \2так, чтобы быть повторно вставлен в шаблоне замены.

Майк
источник
3

Вы можете использовать модификатор «c» в глобальном поиске и замене, который будет запрашивать подтверждение для каждой замены. Это займет больше времени, но может сработать для небольшого файла кода:

%s/\$var/\$foo/gc

C означает подтверждение.

Аднан
источник
I означает игнорирование регистра. Вы хотите c, подтверждайте каждое изменение.
Майкл Кристофик
упс .. извините за это .. Исправлен ответ
Аднан
Ответ до сих пор не исправлен полностью;)
user55400 02
ты имел ввиду сбежавший $? Это что-то вроде моих личных предпочтений. $ работает без escape, а также с и, поскольку в мире регулярных выражений и в vi $ имеет особое значение, я предпочитаю избегать его просто для ясности.
Аднан
0

В c вы можете добиться некоторого прогресса с помощью cscope. Он пытается понять синтаксис, поэтому у него будет шанс узнать, когда буква была переменной.

Ким Рис
источник
0

Если это связано с несколькими файлами, вы можете подумать о том, чтобы взглянуть на sed. Используйте find для получения файлов и xargs плюс sed для замены. Скажем, вы хотите заменить a на a_better_name во всех файлах, соответствующих * .c, вы можете сделать

найти . -name "* .c" | xargs sed -i -e 's / a / a_better_name / g'

Имейте в виду, что это заменит ВСЕ вхождения a, поэтому вам может потребоваться более надежное регулярное выражение.

Дрю Олсон
источник