gVim найти / заменить счетчиком - PullRequest
15 голосов
/ 10 мая 2011

Есть ли способ вставить значение из некоторой переменной счетчика в поиск / замену gVim?

например. преобразовать этот документ:

<SomeElement Id="F" ... />
<SomeElement Id="F" ... />
<SomeElement Id="F" ... />

к этому итоговому документу:

<SomeElement Id="1" ... />
<SomeElement Id="2" ... />
<SomeElement Id="3" ... />

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

:%s/^\(\s*<SomeElement Id="\)F\(".*\)$/\1<insert-counter-here>\2/g

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

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

Ответы [ 6 ]

18 голосов
/ 10 мая 2011

Можно увеличить счетчик, используя замену с особенность выражения (см. :help sub-replace-\=). Поскольку конструкция \= разрешает только выражения, команду :let запрещено использовать, и поэтому переменная не может быть установлена ​​обычным способом. Тем не менее, есть простой трюк, чтобы изменить значение переменной в выражении, если это Переменная - это список или словарь. В этом случае его содержимое может быть изменено функцией map(). Таким образом, замена для случая описанный в вопросе будет выглядеть следующим образом. 1

:let n=[0] | %s/Id="F"/\='Id="'.map(n,'v:val+1')[0].'"'/g

Или лучше, 2

:let n=[0] | %s/Id="\zsF\ze"/\=map(n,'v:val+1')/g

Этот короткий однострочник полностью решает проблему.

Для частых замен, таких как приведенная выше, можно определить вспомогательную функция

function! Inc(x)
    let a:x[0] += 1
    return a:x[0]
endfunction

и сделать команды подстановки еще короче,

:%s/Id="\zsF\ze"/\=Inc(n)/g

1 Хитрая часть здесь находится в замещающей части замена. Поскольку он начинается с \=, остальная часть интерпретируется как выражение от Vim. Таким образом, 'Id="'.map(n, 'v:val+1').'"' является обычным выражение. Здесь объединяется строковый литерал 'Id="' (с использованием . оператор) с возвращаемым значением вызова функции map(n, 'v:val+1'), и с другая строка, '"'. Функция map ожидает два аргумента: список (как в в этом случае) или словарь, и строка, содержащая выражение, которое должно быть оценивается для каждого из элементов в данном списке или словаре. Специальный переменная v:val обозначает отдельный элемент списка. Итак, строка 'v:val+1' будет оцениваться как элемент списка, увеличенный на единицу.

2 Атомы паттернов \zs и \ze используются для установки начала и конец шаблона для замены соответственно (см. :help /\zs и :help /\ze). Таким образом, вся поисковая часть замещающей команды совпадает, но заменяется только часть между \zs и \ze. Это позволяет избежать неуклюжий конкатенации в выражении замены.

12 голосов
/ 10 мая 2011

Хм, это немного сложно.Вот что я получил до сих пор.Попробуйте выполнить эти 2 команды map в сеансе vim:

:nmap %% :let X=1<cr>1G!!
:nmap !! /^\s*<SomeElement Id="F"<cr>:s/F"/\=X.'"'/<cr>:let X=X+1<cr>!!

Как только это произойдет, нажмите %%, чтобы начать забавную часть :)

Это сделает ваш файл следующим образом:

<SomeElement Id="1" ... />
<SomeElement Id="2" ... />
<SomeElement Id="3" ... />
<SomeElement Id="4" ... />

Объяснение:

Первая команда nmap отображает следующие последовательности на нажатия клавиш %%:

  • инициализация переменной X в 1
  • перемещение к началу первого файла
  • вызов другого сопоставленного нажатия клавиши !!

Вторая команда nmap отображает следующие последовательности на нажатия клавиш !!:

  • Поиск следующего вхождения шаблона ^\s*<SomeElement Id="F"
  • Если вышеуказанный шаблон найден, найдите и замените F" на переменную X и цитату "
  • увеличить переменную vim X на 1
  • Рекурсивно вызвать себя, сделав вызов !!
  • Одиночная точка . используется для объединения строк в vim, очень похоже наphp

Этот рекурсивный вызов останавливается, когда шаблон ^\s*<SomeElement Id="F" больше не найден вЭлектронный файл.

7 голосов
/ 17 ноября 2016

Инструкции Vim wiki , кажется, самое простое решение (по крайней мере, для меня).

Пример ниже заменяет все вхождения PATTERN на REPLACE_[counter] (REPLACE_1, REPLACE_2 и т. Д.):

:let i=1 | g/PATTERN/s//\='REPLACE_'.i/ | let i=i+1

Чтобы ответить на вопрос, это может выглядеть так:

:let i=1 | g/SomeElement Id="F"/s//\='SomeElement Id="'.i.'"'/ | let i=i+1
6 голосов
/ 04 июля 2013

Очень простое решение.Я должен был сделать это несколько раз.

:let i=1 | g/^\(\s*<SomeElement Id="\)F\(".*\)$/s//\=submatch(1).i.submatch(2)/ | let i=i+1

Исходя из следующего совета.http://gdwarner.blogspot.com/2009/01/vim-search-and-replace-with-increment.html

1 голос
/ 13 июня 2015

Поместите это в ваш vimrc или выполните в текущем сеансе:

function! Inc(x) 
  let a:x[0] += 1 
  return a:x[0] 
endfunction

function IncReplace(pos, behind, ahead, rep) 
  let poss=a:pos-1 
  let n=[poss] 
  execute '%s/' . a:behind . '\zs' . a:rep . '\ze' . a:ahead . '/\=Inc(n)/g' 
endfunction

Затем выполните :call IncReplace(1, 'Id="', '"', 'F')

Первый аргумент - это число, с которого вы хотите начать, второйэто то, что вы хотите сопоставить за номером, третье - это то, что вы хотите сопоставить перед номером, а четвертое - то, что вы действительно хотите заменить.

0 голосов
/ 10 ноября 2011

Может быть плагин increment.vim поможет

...