GVim найти/заменить счетчиком

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

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

<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 из предоставленного вами установщика. Я предпочитаю не устанавливать какие-либо дополнительные инструменты.

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

Ответ 1

Vim wiki instructions кажется самым простым решением (по крайней мере для меня).

Пример ниже заменяет все вхождения 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

Ответ 2

Возможно использование счетчика с добавлением с помощью (см. :help sub-replace-\=). Поскольку конструкция \= допускается использование только выражений, команда :let запрещена к использованию, и поэтому переменная не может быть установлена ​​обычным способом. Однако существует простой трюк, чтобы изменить значение переменной в выражении, если это variable - это список или словарь. В этом случае его содержимое может быть измененный функцией 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. Это позволяет избежать неуклюжих конкатенации в замещающем выражении.

Ответ 3

Хм, это немного сложно. Вот что я получил до сих пор. Попробуйте эти две команды карты в сеансе 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" больше не найден в файле.

Ответ 5

Поместите это в свой 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')

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