Заменить серию звездочек с нумерованным списком


16

Представьте, что у меня есть следующий текст:

some random stuff
* asdf
* foo
* bar
some other random stuff

Я хочу заменить звездочку пулями на цифры, например так:

some random stuff
1. asdf
2. foo
3. bar
some other random stuff

Как это можно сделать в vim?


Почему бы вам не пойти на плагины? Аналогичным является increment.vim в Github
SibiCoder

Это так удивительно и круто, что все заставили свои ответы увеличивать числа, но поскольку Markdown будет их нумеровать для вас, почему бы не сделать их все 1.? Так :%s/^* /1. /бы и сделал. Это похоже на гораздо меньшую работу.
цыплята

Ответы:


14

Вы можете попробовать следующую команду:

:let c=0 | g/^* /let c+=1 | s//\=c.'. '

Сначала он инициализирует переменную c( let c=0), затем выполняет глобальную команду, gкоторая ищет шаблон ^*(начало строки, затем звездочка и пробел).

Всякий раз, когда строка, содержащая этот шаблон, найдена, глобальная команда выполняет команду:
let c+=1 | s//\=c.'. '
она увеличивает переменную c( let c+=1), затем ( |) заменяет ( s) предыдущий искомый шаблон ( //) оценкой выражения ( \=):
содержимое переменной cсцепляется ( .) со строкой'. '


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

:let c=0 | 5,10g/^* /let c+=1 | s//\=c.'. '

Если у вас есть файл, содержащий несколько похожих списков, которые вы хотите преобразовать, например, что-то вроде этого:

some random stuff                 some random stuff                      
* foo                             1. foo                                 
* bar                             2. bar                                 
* baz                             3. baz                                 
some other random stuff           some other random stuff                
                           ==>                                                
some random stuff                 some random stuff                      
* foo                             1. foo                                 
* bar                             2. bar                                 
* baz                             3. baz                                 
* qux                             4. qux                                 
some other random stuff           some other random stuff                

Вы можете сделать это с помощью следующей команды:

:let [c,d]=[0,0] | g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '

Это просто вариант предыдущей команды, которая сбрасывает переменную cпри переключении на другой список. Чтобы определить, находитесь ли вы в другом списке, переменная dиспользуется для хранения номера последней строки, где была сделана замена.
Глобальная команда сравнивает текущий номер строки ( line('.')) с d+1. Если они совпадают, это означает, что мы находимся в том же списке, что и раньше, то cесть incremental ( c+1), в противном случае это означает, что мы находимся в другом списке, то cесть reset ( 1).

Внутри функции команду let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]можно переписать так:

let c = line('.') == d+1 ? c+1 : 1
let d = line('.')

Или вот так:

if line('.') == d+1
    let c = c+1
else
    let c = 1
endif
let d = line('.')

Чтобы сохранить некоторые нажатия клавиш, вы также можете определить пользовательскую команду :NumberedLists, которая принимает диапазон со значением по умолчанию 1,$( -range=%):

command! -range=% NumberedLists let [c,d]=[0,0] | <line1>,<line2>g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '

Когда :NumberedListsбудет выполнено, <line1>и <line2>будет автоматически заменено диапазоном, который вы использовали.

Итак, чтобы преобразовать все списки в буфере, вы должны набрать: :NumberedLists

Только списки между строкой 10 и 20: :10,20NumberedLists

Только визуальный подбор: :'<,'>NumberedLists


Для получения дополнительной информации см .:

:help :range
:help :global
:help :substitute
:help sub-replace-expression
:help list-identity    (section list unpack)
:help expr1
:help :command

9

Это работает только с последней версией Vim (которая имеет :h v_g_CTRL-A):

  1. Блок-выбор пуль списка ( *) и заменить их 0(курсор находится на первом *) Ctrl-v j j r 0.
  2. Снова выберите предыдущий блок и увеличьте его с помощью счетчика :gv g Ctrl-a

... вот и все :)


(Если вы хотите , чтобы иметь точку после каждого номера, изменение 1 - ый шаг: Ctrl-v j j s 0 . Esc)


9

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

:'<,'>s/*/\=line('.') - line("'<") + 1 . '.'

См :help sub-replace-expression, :help line()и :help '<.

Чтобы избежать необходимости выбирать строки, можно использовать поиск назад и вперед со смещением, чтобы указать диапазон замены следующим образом:

:?^[^*]?+1,/^[^*]/-1s/*/\=line('.') - search('^[^[:digit:]]', 'bn') . '.'

Видеть :help cmdline-ranges



0

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

Вы можете сопоставить их с последовательностями клавиш '*и '#. Метки *и #не существуют, так что вы не будете переопределять любую функциональность по умолчанию. Причиной выбора 'в качестве префикса является получение какой-то мнемоники. Вы добавляете знак / знак перед некоторыми строками. И обычно для перехода к отметке вы используете префикс '.

fu! s:op_list_bullet(...) abort range

    if a:0
        let [lnum1, lnum2] = [line("'["), line("']")]
    else
        let [lnum1, lnum2] = [line("'<"), line("'>")]
    endif

    if !empty(matchstr(getline(lnum1), '^\s*\d\s*\.'))
        let pattern     = '\d\s*\.\s\?'
        let replacement = '* '

    elseif count(['-', '*'], matchstr(getline(lnum1), '\S'))
        let pattern     = '\v\S\s*'
        let replacement = ''

    else
        let pattern     = '\v\ze\S'
        let replacement = '* '
    endif

    let cmd = 'keepj keepp %s,%s s/%s/%s'

    sil exe printf(cmd, lnum1, lnum2, pattern, replacement)
endfu

fu! s:op_list_digit(...) abort range
    let l:c = 0

    if a:0
        let [lnum1, lnum2] = [line("'["), line("']")]
    else
        let [lnum1, lnum2] = [a:firstline, a:lastline]
    endif

    if count(['-', '*'], matchstr(getline(lnum1), '\S'))
        let pattern     = '\S\s*'
        let replacement = '\=l:c.". "'

    elseif !empty(matchstr(getline(lnum1), '^\s*\d\s*\.'))
        let pattern     = '\d\s*\.\s\?'
        let replacement = ''

    else
        let pattern     = '\v^\s*\zs\ze\S'
        let replacement = '\=l:c.". "'
    endif

    let cmd = 'keepj keepp %s,%s g/%s/let l:c = line(".") == line("'']")+1 ?
                                                \ l:c+1 : 1 |
                                                \ keepj keepp s/%s/%s'

    sil exe printf(cmd, lnum1, lnum2, pattern, pattern, replacement)
endfu

nno <silent> '*     :<C-U>set opfunc=<SID>op_list_bullet<CR>g@
nno <silent> '**    :<C-U>set opfunc=<SID>op_list_bullet
                    \<Bar>exe 'norm! ' . v:count1 . 'g@_'<CR>
xno <silent> '*     :call <SID>op_list_bullet()<CR>

nno <silent> '#     :<C-U>set opfunc=<SID>op_list_digit<CR>g@
nno <silent> '##    :<C-U>set opfunc=<SID>op_list_digit
                    \<Bar>exe 'norm! ' . v:count1 . 'g@_'<CR>
xno <silent> '#     :call <SID>op_list_digit()<CR>

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

Например, вы можете переключать список с префиксом звездочки или минус внутри текущего абзаца, нажав '*ip. Здесь '*оператор и ipтекстовый объект, над которым он работает.

И сделайте то же самое для списка с префиксом чисел в следующих 10 строках, нажав '#10j. Здесь '#другой оператор и 10jдвижение, покрывающее линии, на которых работает оператор.

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

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.