Вставьте инкрементный номер в каждую строку в выделении или совпадении

10

У меня есть проблема, я могу думать о двух общих подходах к решению, но я не знаю специфику для любого подхода.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Для каждой строки, начинающейся с «Level n», я хочу вставить число, начинающееся с «01». Для простоты давайте предварительно добавим число.

Подход 1: вручную выбрать все линии с одинаковым уровнем. Призывай магию, я скоро научусь.

Подход 2: Запишите поиск и замену, которая соответствует всем строкам с данным уровнем, который при каждом совпадении включает в текст замены значение, которое увеличивается на единицу с каждым совпадением.

Я нашел похожие вопросы в StackOverflow или на других сайтах Vim, но у каждого из них, похоже, есть одна или несколько из следующих проблем:

  1. Речь идет о вставке номера текущей строки, а не произвольного, но увеличивающегося числа.
  2. Не дополняет нулями номер.
  3. На самом деле не работает для выбора на моем Vim 7.4, работающем на Windows 7. (Эти приводят к ошибке E481: No range allowed.)

Я запускаю gVim в Windows с mswin.vim, но решение, которое работает на всех установках vanilla Vim без необходимости настройки, может быть лучшим.

hippietrail
источник
Лучшие теги, которые я мог придумать для этого вопроса, не существуют: выбор и диапазон, поэтому не стесняйтесь повторно пометить или создать один из этих тегов.
Hippietrail
1
Этот плагин не является полным решением для вашей проблемы, но он чрезвычайно полезен для добавления столбцов чисел: VisIncr . Документы здесь . FWIW.
lcd047

Ответы:

17

Аналогично ответу на https://vi.stackexchange.com/a/818/227 , вы можете использовать глобальную команду.

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

В вашем случае вы хотите добавить текст к строкам, начинающимся с «Level N:», чтобы наша глобальная команда могла быть

:g/^Level \d:/{COMMANDS}

Использование команды замены (замена регулярного выражения) для команды

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

Пример для вашего вопроса

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Как это работает

В разделе замены команды подстановки может быть выражение.

Первое, что мы сделаем, это установим переменную iв качестве начального номера. Я выбрал 1, но подойдет любое число.let i = 1

Затем мы запускаем нашу глобальную команду, которая настраивает нас на выполнение действий с согласованными строками. g/^Level \d:/

Наша глобальная команда вставит значение и увеличит наш счетчик с помощью команды подстановки и команды let. s/^/\=printf("%02d ", i)/ | let i = i+1

Регулярное выражение команды подстановки находит начало строки ^и заменяет его выражением, и наше выражение будет результатом отформатированной печати. Как и в языке Си, vim's printf принимает параметры форматирования. %02dсредство преобразование аргумента , как если бы это было десятичное число d, занимающее по меньшей мере , 2 места 2и колодку с 0 0. Подробности и другие параметры преобразования (включая форматирование с плавающей запятой) см :help printf. Мы даем printf нашу переменную подсчета, iи она дает нам 01первый раз, 02второй раз и т. Д. Это используется командой замены для замены начала строки, эффективно вставляя результат printf в начало.

Обратите внимание , что я ставлю пробел после d: "%02d ". Вы не спрашивали об этом в вопросе (и я не видел пример вывода), но я подозревал, что вы хотите отделить число от слова «Уровень». Удалите пробел из строки, заданной для printf, чтобы вставленный номер был рядом с буквой L на уровне.

Наконец, это let i = i + 1увеличивает наш счетчик после каждой замены.

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

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

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

Пример для вашего вопроса

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Как это работает

Используемые значения очень похожи на замену (мы все еще используем printf, чтобы отформатировать наш номер, чтобы он был дополнен 0 до 2 цифр), но операция отличается.

Здесь мы используем команду execute, которая принимает строку и запускает строку как команду ex ( :help :exe). Мы создаем строку, которая объединяет «normal! I» с нашими данными, которые будут «normal! I01» в первый раз и «normal! I02» во второй раз и т. Д.

Команда normalвыполняет операции как в обычном режиме. В этом примере наша обычная команда I, которая вставляет в начало строки. Если бы мы использовали ddего, то удалили бы строку, oоткрыли бы новую строку после совпавшей строки. Это как если бы вы печатали I(или любые другие операции) самостоятельно в обычном режиме. мы используем !после, normalчтобы убедиться, что никакие отображения не мешают нам. См :help :normal.

Затем вставляется значение нашего printf, как в первом примере использования замены.

Этот метод может быть сложнее, чем регулярное выражение, потому что вы можете делать такие вещи, как execute "normal! ^2wy" . i . "th$p"переход к началу текста ^, перемещение вперед на 2 слова 2w, рывок до i-го символа «h» y" . i . "th, переход к концу строки $и вставка p.

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

Подход, где каждый уровень имеет свой счетчик

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

Во-первых, давайте освободим i, если мы уже использовали его как целое число. Мы не можем преобразовать i в список, мы должны создать его таким образом.

:unlet! i

Далее, давайте установим i в список, содержащий количество уровней. Вы показали 2 в своем вопросе, но давайте примем 10 для удовольствия. Поскольку индексирование списка основано на 0, и я не хочу беспокоиться о корректировке для 1, как ваш список, мы просто создадим достаточно элементов (11) и никогда не будем использовать индекс 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Далее нам нужен способ получить номер уровня. К счастью, замена также доступна как функция, поэтому мы дадим ей нашу строку и извлечем номер уровняsubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Поскольку теперь я - список из 11 1с (каждый индекс является счетчиком для нашего уровня), теперь мы можем настроить любой из приведенных выше примеров, чтобы использовать результат этой замены:

С помощью команды замены:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

С помощью обычной команды:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Пример ввода:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Пример вывода:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More
Джон О'М.
источник
Вау, очень энциклопедический. Я только прочитал первую часть и чувствую, что узнал о Виме больше, чем за последние несколько лет. Это тот тип ответа, который делает Stack Exchange лучше, чем обычные сайты вопросов и ответов, а также показывает преимущества наличия Vim-ориентированной SE.
hippietrail
3

Вы можете построить с https://stackoverflow.com/a/4224454/15934, чтобы заполнить нулями свои номера.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Но чтобы упростить действие заполнения чисел, я бы пошел для пары функций и команды:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Затем выберите свои строки и введите :PrependNumber(вы увидите :'<,'>PrependNumber. [Примечание: команда принимает необязательный параметр: шаблон, перед которым будет вставлен номер]

Люк Эрмитт
источник
Но не line(".")означает "использовать текущий номер строки"? Это было моей проблемой 1. с предыдущими ответами, которые я смог найти.
hippietrail
1
Да, это так. Вот почему я вычитаю номер строки первой строки в диапазоне.
Люк Эрмит
Ах, хорошо, я еще не проверял это, потому что я даже не могу вспомнить, как ввести сценарий в Vim ... должен сначала прочитать об этом (-:
hippietrail
1
Когда вы находитесь под Windows, скопируйте и вставьте код в $HOME/vimfiles/plugin/whatevernameyouwish.vim. Или даже ваш $HOME/_vimrc(имя файла Windows), если вы предпочитаете (в первый раз). Если вы не уверены, что $ HOME находится на вашем компьютере, спросите vim, что он думает ->:echo $HOME
Luc Hermitte
1
Вам даже не нужно, :sourceесли скрипт vim правильно установлен в нужном каталоге ({rtp} / plugin или {rtp} / ftplugin / {filetype} / ftplugin для специфических плагинов для файловых типов). :sourceэто то, с чем нам пришлось играть более полутора десятилетий назад.
Люк Эрмит
2

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

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Наведите курсор на 01, выберите и дерните его с помощью yiw. Теперь вы хотите записать свои действия с этого момента.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Запустить макрос в регистре q
  • /^Level 1<CR> Поиск строки, начинающейся с «Уровень 1»
  • P вставить перед курсором (содержит ваш номер)
  • <C-A> увеличивает число
  • A<space><esc> Вставьте пробел после числа
  • 0 Перейти к началу
  • yiw дергать текущий номер
  • q Конец макроса

Затем повторите этот макрос, используя @q.

jecxjo
источник
1
Является ли <C-A>контроль + а? Это выбирает все в Vim на Windows.
hippietrail
Это Контроль + а. В vim он должен увеличивать число. Если вы изменили свои сочетания клавиш для выполнения действий Windows вместо действий Vim, то вы не сможете выполнять большинство вещей, которые публикуют здесь.
Jecxjo
Нет версии Vim для Windows с разными привязками из-за чьей-то бесконечной мудрости. Я просто использую ванильные готовые крепления. Я никогда не менял связывание ключей Vim более двадцати лет, что я могу вспомнить (-:
hippietrail
Не должно быть, моя установка Windows имеет нормальные привязки vim. Возможно ли, что вы установили чужую сборку vim? Или вы можете запустить vim в режиме "Easy"? Я считаю, что Windows устанавливает значки на рабочем столе с обычным и простым режимом.
Jecxjo
3
Нет, потому что установка по умолчанию в windows загружает mswin.vim. Если вы создаете свой собственный vimrc и не загружаете mswin.vim, вы получаете обычные привязки vim. Я держу один vimrc для всех моих установок (Linux, Mac и Windows) и никогда не имею дело с mswin.vim. Для получения дополнительной информации по этому вопросу см stackoverflow.com/questions/289681/...
jecxjo