Эффективный способ рефакторинга класса/метода/строки в каталоге с помощью vim

До сих пор я вручную обновлял код с помощью операции find-and-replace

%s:/stringiwanttoreplace/newstring/g 

в vim.

Но это медленный и трудоемкий процесс, если у меня есть stringiwanttoreplace во многих файлах внутри определенного каталога.

Мой текущий/типичный медленный и трудоемкий процесс включает в себя grep: -

grep -rn "stringiwanttoreplace" .

в моем терминале, чтобы показать все местоположения/имена файлов, в которых присутствует stringiwanttoreplace; и теперь, когда я знаю, какие файлы содержат stringiwanttoreplace, я открою каждый файл один за другим, чтобы выполнить операцию поиска и замены в каждом файле.

Есть ли более эффективный рабочий процесс (в vim), чтобы сделать это?

ПОДТВЕРЖДЕНИЕ: Я предпочел бы решение на основе vim вместо bash script/one-liner.

Ответ 1

Вот полная последовательность команд, которые я буду использовать:

/stringiwanttoreplace
:vimgrep /<c-r>// **
:Qargs
:argdo %s//newstring/g
:argdo update

В первой строке мы ищем целевой шаблон. Это заполняет последний регистр шаблона поиска (:help quote/), что означает, что нам больше не придется вводить его снова.

Команда :vimgrep выполняет поиск по всему проекту для указанного шаблона. Введите <c-r>/ как ctlr + r, за которым следует / - это вставляет содержимое последнего регистра шаблона поиска в командную строку. Первый и последний символы / являются разделителями для поля поиска. Конечный ** сообщает Vim о том, чтобы просмотреть каждый файл и каталог под текущим каталогом.

В этот момент список quickfix будет заполнен поисковыми совпадениями из всех соответствующих файлов. :Qargs - это настраиваемая команда, которая заполняет список аргументов всеми файлами, перечисленными в списке quickfix. Здесь реализация:

command! -nargs=0 -bar Qargs execute 'args ' . QuickfixFilenames()
function! QuickfixFilenames()
  " Building a hash ensures we get each buffer only once
  let buffer_numbers = {}
  for quickfix_item in getqflist()
    let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
  endfor
  return join(values(buffer_numbers))
endfunction

Добавьте это в свой vimrc файл.

Запустив :Qargs, наш список аргументов теперь должен содержать все файлы, содержащие нашу целевую строку. Поэтому мы можем запустить команду подстановки с помощью :argdo, чтобы выполнить команду в каждом файле. Мы можем оставить поле поиска команды подстановки пустым и автоматически использовать самый последний шаблон поиска. Если вы хотите, вы можете включить флаг c, когда вы запустите команду подстановки, после чего вам будет предложено подтвердить.

Наконец, команда :argdo update сохраняет каждый файл, который был изменен.

Как отметил @Peter Rincker, вы должны убедиться, что Vim 'hidden', в противном случае это вызовет ошибку при попытке переключиться на другой буфер, прежде чем записывать какие-либо изменения в активный буфер.

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

:Qargs | argdo %s//replacement/gc | update

Команда :Qargs сжимается от этого ответа (по мне), который, в свою очередь, был вдохновлен этим ответом DrAl. Очень подобное решение было опубликовано @ib, что говорит мне о том, что Vim действительно должен реализовывать что-то вроде :quickfixdo изначально.

Ответ 2

Если вы действительно хотите это сделать в Vim, вы можете следовать рекомендациям здесь.

Ответ 3

Вы можете вызвать это из Vim (:!find ...), но вам не нужно:

find . -type f | xargs sed -i 's/stringiwanttoreplace/newstring/g'

Точная настройка выбора файла с помощью десятков параметров, описанных в

man find

(например, заменить только файлы HTML: -name \*.html)

Это решение попытается выполнить замену во всех файлах. Вы можете отфильтровать это через grep раньше, но это просто делает работу в два раза без усиления.

Кстати: sed использует почти такой же синтаксис для регулярных выражений как Vim (вытекающий из той же истории).

Ответ 4

Вы можете открыть все файлы и напечатать

:bufdo :s/stringiwanttoreplace/newstring/g

Он выполняет поиск/замену во всех ваших буферах.

Ответ 5

Для этого вам не нужно vim, вы можете использовать инструменты командной строки. Используя sed в цикле в списке файлов, сделайте это для вас автоматически. Что-то вроде этого:

for each in `grep -l "stringiwanttoreplace" *` ;
do
    cat $each | sed -e "s/stringiwanttoreplace/newstring/g" > $each
; done

Ответ 6

vim7 имеет рекурсивный grep встроенный

:vimgrep /pattern/[j][g] file file1 file2 ... fileN

результат будет показан в quickfix-окне (: help quickfix)

чтобы сделать поиск рекурсивно, используйте ** -wildcard, например

**/*.c для поиска по текущей папке и рекурсивно через все подкаталоги.