Masukkan nomor yang bertambah ke setiap baris dalam pilihan atau kecocokan

10

Saya memiliki masalah yang saya bisa memikirkan dua pendekatan umum untuk menyelesaikan, tetapi saya tidak tahu secara spesifik untuk kedua pendekatan tersebut.

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

Untuk setiap baris mulai "Level n" Saya ingin memasukkan angka, dimulai dengan "01". Untuk kesederhanaan, mari kita tambahkan dulu jumlahnya.

Pendekatan 1: Pilih secara manual semua garis dengan Level yang sama. Meminta sihir, saya akan segera belajar.

Pendekatan 2: Tulis pencarian dan ganti yang cocok dengan semua baris dengan Level yang diberikan yang pada setiap pertandingan menyertakan angka dalam teks ganti, yang bertambah satu dengan setiap pertandingan.

Saya telah menemukan pertanyaan serupa di StackOverflow atau di situs Vim lainnya, tetapi masing-masing tampaknya memiliki satu atau lebih masalah berikut:

  1. Apakah tentang memasukkan nomor baris saat ini daripada nomor yang sewenang-wenang tetapi bertambah.
  2. Tidak memasukkan angka ke angka nol.
  3. Tidak benar-benar berfungsi untuk pilihan pada Vim 7.4 saya yang berjalan pada Windows 7. (Yang ini menghasilkan kesalahan E481: No range allowed.)

Saya menjalankan gVim di Windows dengan mswin.vim tetapi solusi yang berfungsi pada semua pemasangan vanilla Vim tanpa harus menyesuaikan pengaturan mungkin yang terbaik.

hippietrail
sumber
Tag terbaik yang bisa saya pikirkan untuk pertanyaan tidak ada: pemilihan dan rentang jadi jangan ragu untuk menandai ulang atau membuat salah satu dari tag tersebut.
hippietrail
1
Plugin ini bukan solusi lengkap untuk masalah Anda, tetapi ini sangat berguna untuk menambahkan kolom angka: VisIncr . Dokumen di sini . FWIW.
lcd047

Jawaban:

17

Mirip dengan jawaban di https://vi.stackexchange.com/a/818/227 , Anda dapat menggunakan perintah global.

Dengannya Anda dapat menginstruksikan vim untuk mencari garis yang cocok dengan suatu pola, dan kemudian melakukan perintah di atasnya.

Dalam kasus Anda, Anda ingin menambahkan teks ke baris yang dimulai dengan "Level N:", sehingga perintah global kami bisa

:g/^Level \d:/{COMMANDS}

Menggunakan perintah pengganti (penggantian ekspresi reguler) untuk perintah

Perintahnya lebih menyenangkan. Saya biasanya suka melakukan penggantian ekspresi reguler untuk hal-hal seperti ini, karena mudah digunakan variabel.

Contoh untuk pertanyaan Anda

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Bagaimana itu bekerja

Di bagian penggantian perintah substitusi dapat berupa ekspresi.

Hal pertama yang akan kita lakukan adalah mengatur variabel imenjadi angka awal. Saya memilih 1, tetapi nomor berapa pun akan berhasil.let i = 1

Kemudian kami menjalankan perintah global kami, yang membuat kami melakukan tindakan pada baris yang cocok. g/^Level \d:/

Kami akan meminta perintah global kami memasukkan nilai dan meningkatkan penghitung kami menggunakan perintah substitusi dan perintah let. s/^/\=printf("%02d ", i)/ | let i = i+1

Ekspresi reguler perintah substitusi menemukan awal baris ^dan menggantinya dengan ekspresi, dan ekspresi kita akan menjadi hasil dari cetakan yang diformat. Seperti dalam bahasa C, printf vim mengambil parameter format. %02dberarti mengonversi argumen seolah-olah itu angka desimal d, menempati setidaknya 2 spasi 2dan pad dengan 0 0. Untuk detail dan opsi konversi lainnya (termasuk pemformatan titik mengambang), lihat :help printf. Kami memberikan printf variabel penghitungan kami idan itu memberi kami 01pertama kali, 02kedua kalinya, dll. Ini digunakan oleh perintah substitusi untuk mengganti awal baris, secara efektif memasukkan hasil printf di awal.

Perhatikan bahwa saya menempatkan spasi setelah d: "%02d ". Anda tidak menanyakannya dalam pertanyaan (dan saya tidak melihat contoh output), tetapi saya curiga Anda ingin memisahkan nomor dari kata "Level". Hapus spasi dari string yang diberikan ke printf agar angka yang dimasukkan tepat di sebelah L di Level.

Akhirnya, itu let i = i + 1menambah penghitung kami setelah setiap penggantian.

Ini dapat diterapkan secara umum untuk mengganti bagian-bagian garis yang cocok dengan kriteria lain dengan data fungsional yang arbitrer.

Menggunakan perintah normal gabungan

Ini bagus untuk penyisipan sederhana atau pengeditan kompleks. Seperti halnya dengan pengganti, kami akan menggunakan global untuk mencocokkan, tetapi alih-alih substitusi ekspresi reguler, kami akan menjalankan serangkaian operasi seolah diketik oleh pengguna.

Contoh untuk pertanyaan Anda

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Bagaimana itu bekerja

Nilai yang digunakan sangat mirip dengan pengganti (kami masih menggunakan printf untuk memformat nomor kami untuk membuatnya 0 diisi dengan 2 digit), tetapi operasinya berbeda.

Di sini kita menggunakan perintah eksekusi, yang mengambil string dan menjalankan string sebagai perintah ex ( :help :exe). Kami membuat string yang menggabungkan "normal! I" dengan data kami, yang akan menjadi "normal! I01" pertama kali dan "normal! I02" kedua kalinya, dll.

The normalperintah melakukan operasi seperti dalam mode normal. Dalam contoh ini, perintah normal kita adalah I, yang menyisipkan di awal baris. Jika kita menggunakannya ddakan menghapus baris, oakan membuka baris baru setelah baris yang cocok. Seolah-olah Anda mengetik I(atau operasi lainnya) sendiri dalam mode normal. kami menggunakan !after normaluntuk memastikan tidak ada pemetaan yang menghalangi jalan kami. Lihat :help :normal.

Apa yang dimasukkan kemudian adalah nilai printf kami, seperti pada contoh pertama menggunakan pengganti.

Metode ini bisa lebih bagus daripada regex, karena Anda dapat melakukan hal-hal seperti execute "normal! ^2wy" . i . "th$p", yang akan pergi ke awal teks ^, bergerak maju 2 kata 2w, menarik sampai karakter 'h' y" . i . "th, pindah ke akhir baris $, dan tempel p.

Ini hampir seperti menjalankan makro, tetapi sebenarnya tidak menggunakan register dan dapat menggabungkan string dari ekspresi apa pun. Saya menemukan ini sangat kuat.

Pendekatan di mana setiap level memiliki counter sendiri

Anda mungkin ingin setiap level mendapatkan penghitungnya sendiri. Jika Anda mengetahui jumlah maksimum level sebelumnya, Anda dapat melakukan hal berikut (menambahkan kode tambahan untuk menemukan level terbesar mungkin tidak terlalu sulit, tetapi akan membuat jawaban ini terlalu lama. Ini semakin lama karena itu).

Pertama, mari kita bebaskan, jika kita sudah menggunakannya sebagai integer. Kami tidak dapat mengonversi saya ke daftar, kami harus membuatnya seperti itu.

:unlet! i

Selanjutnya, mari atur i menjadi daftar yang berisi jumlah level. Anda menunjukkan 2 pada pertanyaan Anda, tetapi mari asumsikan 10 untuk bersenang-senang. Karena pengindeksan daftar didasarkan pada 0, dan saya tidak ingin repot mengoreksi untuk 1 berdasarkan daftar Anda, kami hanya akan membuat elemen yang cukup (11) dan tidak pernah menggunakan indeks 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Selanjutnya, kita perlu cara untuk mendapatkan nomor level. Untungnya, pengganti juga tersedia sebagai fungsi, jadi kami akan memberikannya baris kami dan mengekstrak nomor levelsubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Karena saya sekarang adalah daftar 11 1s (setiap indeks adalah penghitung untuk level kami), sekarang kami dapat menyesuaikan salah satu contoh di atas untuk menggunakan hasil dari substitusi ini:

Melalui perintah pengganti:

: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

Melalui perintah normal:

: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

Input contoh:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Contoh output:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More
John O'M.
sumber
Wow, sangat ensiklopedis. Saya hanya membaca bagian pertama dan merasa seperti saya telah belajar lebih banyak tentang Vim dari itu daripada yang saya miliki dalam beberapa tahun terakhir. Ini adalah jenis jawaban yang membuat Stack Exchange lebih baik daripada situs Q&A rata-rata dan juga menunjukkan manfaat memiliki SE khusus Vim.
hippietrail
3

Anda dapat membangun dari https://stackoverflow.com/a/4224454/15934 ke nol pad nomor Anda.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Tetapi untuk menyederhanakan tindakan nomor padding, saya akan pergi untuk sepasang fungsi dan perintah:

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

Daripada, pilih baris dan jenis :PrependNumberAnda (Anda akan lihat :'<,'>PrependNumber. [Catatan: Perintah ini mengambil parameter opsional: pola sebelum nomor tersebut dimasukkan]

Luc Hermitte
sumber
Tetapi tidak line(".")berarti "menggunakan nomor baris saat ini"? Itu adalah masalah saya 1. dengan jawaban sebelumnya yang dapat saya temukan.
hippietrail
1
Ya itu. Itu sebabnya saya mengurangi nomor baris dari baris pertama dalam kisaran.
Luc Hermitte
Ah OK saya belum mengujinya karena saya bahkan tidak ingat bagaimana cara memasukkan skrip di Vim ... harus membacanya terlebih dahulu (-:
hippietrail
1
Saat Anda berada di bawah windows, salin tempel kode ke $HOME/vimfiles/plugin/whatevernameyouwish.vim. Atau bahkan $HOME/_vimrc(nama file windows) Anda jika Anda inginkan (pertama kali). Jika Anda tidak yakin apa $ HOME pada mesin Anda, tanyakan apa yang ia pikirkan ->:echo $HOME
Luc Hermitte
1
Anda bahkan tidak perlu :sourcejika skrip vim dipasang dengan benar di direktori yang benar ({rtp} / plugin, atau {rtp} / ftplugin / {filetype} / ftplugin untuk plugins spesifik filetype). :sourceadalah apa yang harus kami mainkan lebih dari satu setengah dekade yang lalu.
Luc Hermitte
2

Anda bisa mendekatinya dengan merekam makro. Dimulai dengan file asli Anda, tambahkan contoh pertama dengan nomor tersebut.

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

Pindahkan kursor Anda ke 01, pilih dan tarik dengan yiw. Sekarang Anda ingin merekam tindakan Anda dari titik ini.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Mulai makro dalam register q
  • /^Level 1<CR> Cari garis yang dimulai dengan "Level 1"
  • P rekatkan sebelum kursor (berisi nomor Anda)
  • <C-A> menambah nomor
  • A<space><esc> Masukkan spasi setelah nomor
  • 0 Pindah ke awal
  • yiw tarik nomor saat ini
  • q Akhiri makro

Kemudian ulangi makro ini dengan menggunakan @q.

jecxjo
sumber
1
Apakah <C-A>kontrol + a? Itu memilih semua yang ada di Vim di Windows.
hippietrail
Ini adalah Kontrol + a. Dalam vim itu harus menambah angka. Jika Anda telah mengubah ikatan kunci untuk melakukan tindakan Windows alih-alih tindakan Vim, maka Anda tidak akan dapat melakukan sebagian besar hal yang diposkan orang di sini.
jecxjo
Tidak ada versi Vim untuk Windows hadir dengan binding berbeda karena kebijaksanaan seseorang yang tak terbatas. Saya hanya menggunakan ikatan vanilla out-of-the-box. Saya tidak pernah mengubah kunci Vim yang mengikat selama lebih dari dua puluh tahun, yang dapat saya ingat (-:
hippietrail
Seharusnya tidak demikian, instalasi Windows saya memiliki binding vim yang normal. Mungkinkah Anda memasang vim orang lain? Atau bisakah Anda menjalankan vim dalam mode "Mudah"? saya percaya windows menginstal ikon desktop dengan opsi mode normal dan mudah.
jecxjo
3
Tidak, ini karena pemasangan default di windows memuat mswin.vim. Jika Anda membuat vimrc Anda sendiri dan tidak memuat mswin.vim maka Anda mendapatkan binding vim normal. Saya menyimpan vimrc tunggal untuk semua instal saya (Linux, Mac dan Windows) dan tidak pernah berurusan dengan mswin.vim. Untuk info lebih lanjut tentang masalah ini, lihat stackoverflow.com/questions/289681/…
jecxjo