用编号列表替换一系列星号项目符号点


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中完成此操作?


你为什么不去寻找插件?类似的是在Github中的增量
.vim

真是太神奇了,很酷,每个人都让他们的答案使数字递增,但是既然Markdown会为您编号,那么为什么不把它们全都给1.呢?这样:%s/^* /1. /就可以了。看来工作量要少得多。
小鸡

Answers:


14

您可以尝试以下命令:

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

首先,它初始化变量clet c=0),然后执行g用于查找模式的全局命令^*(行的开头,后跟一个星号和一个空格)。

每当找到包含此模式的行时,global命令都会执行以下命令:
let c+=1 | s//\=c.'. '
递增变量clet c+=1),然后(|)将(s)先前搜索的模式(//)替换为表达式(\=)的求值():
变量的内容c串联(.)与字符串'. '


如果您不想修改缓冲区中的所有行,而只想修改特定的段落,则可以将范围传递给global命令。例如,仅修改数字在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增加了(c+1),否则意味着我们在不同的列表中,因此c将其重置了(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('.')

要保存一些击键,您还可以定义custom命令: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. 重新选择上一个块并使用counter递增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


2

另一种方式:

:let n = 1 | g/^* /s//\=printf('%d. ', n)/g | let n = n + 1

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是覆盖该操作员所从事的工作的动作。

使用自定义运算符的另一个好处是,您可以使用dot命令重复上一个版本。

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.