Ganti serangkaian titik peluru asterisk dengan daftar bernomor

16

Bayangkan saya memiliki teks berikut:

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

Saya ingin mengganti peluru asterisk dengan angka, seperti:

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

Bagaimana ini bisa dilakukan dalam vim?

Brennan Vincent
sumber
Mengapa Anda tidak mencari plugin? Yang serupa adalah increment.vim di Github
SibiCoder
Sangat menakjubkan dan keren sehingga semua orang membuat jawaban mereka menambah angka tetapi karena Markdown akan memberi nomor untuk Anda mengapa tidak membuat semuanya saja 1.? Jadi :%s/^* /1. /akan melakukannya. Sepertinya itu kurang berhasil.
anak ayam

Jawaban:

14

Anda dapat mencoba perintah berikut:

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

Pertama ia menginisialisasi variabel c( let c=0), kemudian mengeksekusi perintah global gyang mencari pola ^*(awal garis, diikuti oleh tanda bintang dan spasi).

Setiap kali baris yang mengandung pola ini ditemukan, perintah global mengeksekusi perintah:
let c+=1 | s//\=c.'. '
Itu menambah variabel c( let c+=1), lalu ( |) menggantikan ( s) pola pencarian sebelumnya ( //) dengan evaluasi ekspresi ( \=):
isi variabel cdigabungkan ( .) dengan string'. '


Jika Anda tidak ingin mengubah semua baris dari buffer Anda, tetapi hanya paragraf tertentu, Anda bisa melewati rentang ke perintah global. Misalnya, untuk memodifikasi hanya garis yang jumlahnya antara 5 dan 10:

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

Jika Anda memiliki file yang berisi beberapa daftar serupa yang ingin Anda konversi, misalnya sesuatu seperti ini:

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                

Anda dapat melakukannya dengan perintah berikut:

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

Itu hanya varian dari perintah sebelumnya, yang me-reset variabel cketika Anda beralih ke daftar lain. Untuk mendeteksi apakah Anda berada di daftar lain, variabel ddigunakan untuk menyimpan nomor baris terakhir tempat substitusi dibuat.
Perintah global membandingkan nomor baris saat ini ( line('.')) dengan d+1. Jika mereka sama, itu berarti kita berada di daftar yang sama seperti sebelumnya jadi cbertambah ( c+1), kalau tidak berarti kita berada di daftar yang berbeda, begitu cjuga reset ( 1).

Di dalam suatu fungsi, perintah let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]dapat ditulis ulang seperti ini:

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

Atau seperti ini:

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

Untuk menyimpan beberapa penekanan tombol, Anda juga bisa menentukan perintah khusus :NumberedLists, yang menerima rentang dengan nilai default adalah 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.'. '

Kapan :NumberedListsakan dieksekusi, <line1>dan <line2>akan secara otomatis diganti dengan rentang yang Anda gunakan.

Jadi, untuk mengonversi semua daftar di buffer, Anda harus mengetik: :NumberedLists

Hanya daftar di antara baris 10 dan 20: :10,20NumberedLists

Hanya pemilihan visual: :'<,'>NumberedLists


Untuk informasi lebih lanjut, lihat:

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

Ini hanya berfungsi dengan versi Vim terbaru (yang memiliki :h v_g_CTRL-A):

  1. Blok-pilih daftar peluru ( *) dan menggantinya dengan 0(kursor pada pertama *): Ctrl-v j j r 0.
  2. Pilih kembali blok dan kenaikan sebelumnya dengan penghitung :gv g Ctrl-a

... dan hanya itu :)


(Jika Anda ingin memiliki titik setelah setiap nomor, mengubah langkah 1 ke: Ctrl-v j j s 0 . Esc)

VanLaser
sumber
9

Pilih garis secara visual dan jalankan perintah substitusi ini:

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

Lihat :help sub-replace-expression, :help line(), dan :help '<.

Untuk menghindari harus memilih garis, pencarian mundur dan maju dengan offset dapat digunakan untuk menentukan rentang substitusi seperti ini:

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

Lihat :help cmdline-ranges

djjcast
sumber
2

Cara lain:

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

Anda juga dapat menentukan operator khusus

Anda bisa memetakannya ke urutan kunci '*dan '#. Tanda *dan #tidak ada, sehingga Anda tidak akan menimpa fungsionalitas default apa pun. Alasan untuk memilih 'sebagai awalan adalah untuk mendapatkan semacam mnemonik. Anda menambahkan tanda / tanda di depan beberapa baris. Dan biasanya untuk pergi ke tanda Anda menggunakan awalan '.

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>

Ini juga berfungsi dari mode visual.
Perintah Ex baik untuk scripting, tetapi untuk penggunaan interaktif, operator normal mungkin lebih baik, karena Anda dapat menggabungkannya dengan gerakan atau objek teks apa pun.

Misalnya, Anda dapat beralih daftar diawali dengan tanda bintang atau tanda minus di dalam paragraf saat ini dengan menekan '*ip. Di sini, '*adalah operator dan ipmerupakan objek teks yang berfungsi.

Dan lakukan hal yang sama untuk daftar yang diawali dengan angka di 10 baris berikutnya dengan menekan '#10j. Di sini, '#adalah operator lain dan 10jmerupakan gerakan yang menutupi garis-garis di mana operator bekerja.

Manfaat lain menggunakan operator kustom, adalah Anda dapat mengulangi edisi terakhir Anda dengan perintah dot.

masukkan deskripsi gambar di sini

pengguna9433424
sumber