До кожного рядка вставляйте наростаючий номер у виділенні чи збігу


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
...

Для кожного рядка, що починається з "Рівень n", я хочу вставити число, починаючи з "01". Для простоти давайте додамо число.

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

Підхід 2: Напишіть пошук та замініть, який відповідає всім рядкам із заданим рівнем, який у кожному матчі включає число у тексті заміни, що збільшується по одному з кожним збігом.

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

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

Я запускаю gVim у Windows з mswin.vim, але рішення, яке працює на всіх встановленнях ванілі Vim, не потребуючи налаштування налаштування, може бути найкращим.


Найкращі теги, які я міг би придумати для питання, не існують: вибір і діапазон, тож не соромтесь повторно позначити тег або створити один із цих тегів.
hippietrail

1
Цей плагін не є повним рішенням вашої проблеми, але надзвичайно корисний для додавання стовпців чисел: VisIncr . Документи тут . FWIW.
lcd047

Відповіді:


17

Подібно до відповіді на https://vi.stackexchange.com/a/818/227 , ви можете використовувати глобальну команду.

З його допомогою ви можете доручити vim шукати рядки, що відповідають шаблону, а потім виконувати команди на ньому.

У вашому випадку ви хочете додати текст до рядків, що починаються з "Рівень 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:/

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

Регулярний вираз команди заміщення знаходить початок рядка ^і замінює його виразом, і наш вираз буде результатом форматованого друку. Як і в мові C, 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, яка приймає рядок і виконує рядок як колишню команду ( :help :exe). Ми побудуємо рядок, який поєднує "нормальне! Я" з нашими даними, яке буде "нормальним! I01" в перший раз і "нормальним! I02" вдруге і т.д.

У normalкоманді виконує операції , якщо в нормальному режимі. У цьому прикладі наша звичайна команда I, яка вставляє на початку рядка. Якби ми використовували, ddвона видалила б рядок, oвідкрила б новий рядок після відповідного рядка. Це як би ви ввели I(або будь-які інші операції) себе в звичайному режимі. ми використовуємо !після, normalщоб переконатися, що ніякі відображення не заважають. Див :help :normal.

Потім вставляється значення нашого printf, як у першому прикладі використання замінника.

Цей спосіб може бути більш фантазійним, ніж регулярний вираз, тому що ви можете робити такі речі execute "normal! ^2wy" . i . "th$p", які перейдуть на початок тексту ^, просуньте два слова вперед 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

Нічого собі, дуже енциклопедичний. Я прочитав лише першу частину і відчуваю, що я дізнався більше про Вім саме з цього, ніж я був за останні кілька років. Це такий варіант відповіді, який робить обмін стеками кращим, ніж середні веб-сайти з питань запитання, а також показує переваги наявності 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
Коли ви знаходитесь під вікном, скопіюйте вставити код у $HOME/vimfiles/plugin/whatevernameyouwish.vim. Або навіть ваше $HOME/_vimrc(ім'я файлу Windows), якщо вам зручніше (в перший раз). Якщо ви не впевнені, що $ HOME знаходиться на вашій машині, запитайте vim, що він думає, що це ->:echo $HOME
Люк Ермітт

1
Вам навіть не потрібно, :sourceякщо скрипт vim правильно встановлений у правильному каталозі ({rtp} / плагін, або {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.


1
Чи <C-A>контроль + a? Це вибирає все в Vim для Windows.
hippietrail

Це Control + a. У 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.