一連のアスタリスクの箇条書きを番号付きリストに置き換えます


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のincrement.vim
SibiCoder

誰もが答えに数字を加えるようにしたのは驚くほどクールです。しかし、Markdownがあなたのためにそれらに番号を付けるので、なぜそれらすべてを作らないの1.ですか?そう:%s/^* /1. /するでしょう。それははるかに少ない仕事のようです。

回答:


14

次のコマンドを試すことができます。

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

最初に変数clet c=0)を初期化し、次にgパターン^*(行の先頭にアスタリスクとスペースが続く)を探すグローバルコマンドを実行します。

このパターンを含む行が見つかると、グローバルコマンドはコマンドを実行し
let c+=1 | s//\=c.'. '
ます。変数clet 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を使用して、置換が行われた最後の行の番号を格納します。
globalコマンドは、現在の行番号(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('.')

キーストロークを保存するために: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

... 以上です :)


(各番号の後にドットが必要な場合は、最初のステップを次のように変更します。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あり、演算子が動作する線をカバーするモーションです。

カスタム演算子を使用するもう1つの利点は、dotコマンドを使用して前回のエディションを繰り返すことができることです。

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.