Jawaban:
Mungkin ada metode yang lebih sederhana tetapi mungkin Anda bisa mencoba yang berikut ini.
Katakanlah Anda akan menggunakan register quntuk merekam makro rekursif Anda.
Di awal rekaman, ketikkan:
:let a = line('.')
Kemudian, di akhir rekaman, alih-alih menekan @quntuk membuat makro rekursif, ketikkan perintah berikut:
:if line('.') == a | exe 'norm @q' | endif
Akhirnya akhiri rekaman makro dengan q.
Perintah terakhir yang Anda ketikkan akan memutar ulang makro q( exe 'norm @q') tetapi hanya jika nomor baris saat ini ( line('.')) sama dengan yang awalnya disimpan dalam variabel a.
The :normalperintah memungkinkan Anda untuk mengetik perintah normal (seperti @q) dari mode Ex.
Dan alasan mengapa perintah dibungkus menjadi string dan dieksekusi oleh perintah :executeadalah untuk mencegah :normaldari mengkonsumsi (mengetik) sisa perintah ( |endif).
Contoh penggunaan.
Katakanlah Anda memiliki buffer berikut:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Dan Anda ingin menambah semua angka dari garis arbitrer dengan makro rekursif.
Anda bisa mengetik 0untuk memindahkan kursor ke awal baris kemudian mulai merekam makro:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqqmembersihkan isi register qsehingga ketika Anda awalnya menyebutnya selama definisi makro, itu tidak akan menggangguqq mulai merekam:let a=line('.') menyimpan nomor baris saat ini di dalam variabel aw memindahkan kursor ke nomor berikutnya:if line('.')==a|exe 'norm @q'|endif ingat makro tetapi hanya jika nomor baris tidak berubahq menghentikan rekamanSetelah Anda mendefinisikan makro, jika Anda menempatkan kursor di baris ketiga, tekan 0untuk memindahkannya ke awal baris, lalu tekan @quntuk memutar ulang makro q, itu hanya akan mempengaruhi baris saat ini dan bukan yang lain:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Buat makro rekursif setelah perekaman
Jika mau, Anda bisa membuat makro rekursif setelah pencatatan menggunakan fakta bahwa itu disimpan dalam string di dalam register dan bahwa Anda dapat menyatukan dua string dengan .operator titik .
Ini akan memberi Anda beberapa manfaat:
@qakan ditambahkan di makro setelah didefinisikan, dan setelah Anda menimpa konten lama apa pun yang ada di sanaJika Anda merekam makro seperti biasa (non-rekursif), Anda dapat membuatnya rekursif setelahnya dengan perintah berikut:
let @q = @q . "@q"
Atau bahkan lebih pendek: let @q .= "@q"
.=adalah operator yang memungkinkan untuk menambahkan string ke yang lain.
Ini harus menambahkan 2 karakter @qdi akhir urutan penekanan tombol yang disimpan di dalam register q. Anda juga bisa mendefinisikan perintah khusus:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Ini mendefinisikan perintah :RecursiveMacroyang menunggu nama register sebagai argumen (karena -registeratribut dilewatkan ke :command).
Ini perintah yang sama seperti sebelumnya, satu-satunya perbedaan adalah Anda mengganti setiap kemunculan qdengan <reg>. Ketika perintah akan dieksekusi, Vim akan secara otomatis memperluas setiap kemunculan <reg>dengan nama register yang Anda berikan.
Sekarang, yang harus Anda lakukan adalah merekam makro Anda seperti biasa (non-rekursif), lalu ketik :RecursiveMacro quntuk membuat makro yang tersimpan di dalam register qrekursif.
Anda bisa melakukan hal yang sama untuk membuat makro rekursif pada kondisi tetap di baris saat ini:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Ini persis sama dengan yang dijelaskan di awal posting, kecuali kali ini Anda melakukannya setelah rekaman. Anda baru saja menggabungkan dua string, satu sebelum dan satu setelah penekanan tombol apa pun yang qsaat ini berisi register:
let @q = mendefinisikan kembali isi register q":let a=line('.')\r"menyimpan nomor baris saat ini di dalam variabel asebelum makro melakukan tugasnya \rdiperlukan untuk memberitahu Vim untuk menekan Enter dan menjalankan perintah, lihat :help expr-quotedaftar karakter khusus yang serupa, . @q .menyatukan isi qregister saat ini dengan string sebelumnya dan yang berikutnya,":if line('.')==a|exe 'norm @q'|endif\r"ingat makro qdengan syarat bahwa garis tidak berubahSekali lagi, untuk menyimpan beberapa penekanan tombol, Anda dapat mengotomatiskan proses dengan mendefinisikan perintah khusus berikut:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
Dan sekali lagi, yang harus Anda lakukan adalah merekam makro Anda seperti biasa (non-rekursif), lalu ketik :RecursiveMacroOnLine quntuk membuat makro yang tersimpan di dalam register qrekursif pada kondisi tetap pada baris saat ini.
Gabungkan 2 perintah
Anda juga dapat mengubah :RecursiveMacrosehingga mencakup 2 kasus:
Untuk melakukan ini, Anda bisa meneruskan argumen kedua ke :RecursiveMacro. Yang terakhir hanya akan menguji nilainya dan, tergantung pada nilainya, akan menjalankan salah satu dari 2 perintah sebelumnya. Ini akan memberikan sesuatu seperti ini:
command! -register -nargs=1 RecursiveMacro if <args> | let @<reg> .= "@<reg>" | else | let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r" | endif
Atau (menggunakan garis lanjutan / garis miring terbalik agar lebih mudah dibaca):
command! -register -nargs=1 RecursiveMacro
\ if <args> |
\ let @<reg> .= "@<reg>" |
\ else |
\ let @<reg> = ":let a = line('.')\r" .
\ @<reg> .
\ ":if line('.')==a | exe 'norm @<reg>' | endif\r" |
\ endif
Itu sama seperti sebelumnya, kecuali kali ini Anda harus memberikan argumen ke-2 :RecursiveMacro(karena -nargs=1atribut).
Ketika perintah baru ini akan dieksekusi, Vim akan secara otomatis berkembang <args>dengan nilai yang Anda berikan.
Jika argumen ke-2 ini bukan nol / true ( if <args>) versi pertama dari perintah akan dieksekusi (salah satu yang membuat makro rekursif tanpa syarat), jika tidak maka nol / salah maka versi kedua akan dieksekusi (yang membuat makro rekursif pada kondisi tetap pada baris saat ini).
Jadi kembali ke contoh sebelumnya, itu akan memberikan hal berikut:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq mulai merekam makro di dalam register q<C-a> menambah angka di bawah kursorw memindahkan kursor ke nomor berikutnyaq mengakhiri rekaman:RecursiveMacro q 0membuat makro yang tersimpan di dalam register q
rekursif tetapi hanya sampai akhir baris (karena argumen kedua 0)3G memindahkan kursor Anda ke garis arbitrer (3 misalnya)0@q memutar ulang makro rekursif dari awal barisIni harus memberikan hasil yang sama seperti sebelumnya:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Tapi kali ini Anda tidak harus mengetik perintah yang mengganggu selama perekaman makro Anda, Anda bisa fokus membuat yang berfungsi.
Dan selama langkah 5, jika Anda telah memberikan argumen non-nol ke perintah, yaitu jika Anda telah mengetik dan :RecursiveMacro q 1bukannya :RecursiveMacro q 0, makro qakan menjadi rekursif tanpa syarat, yang akan memberikan buffer berikut:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Kali ini makro tidak akan berhenti di akhir baris ke-3 tetapi di akhir buffer.
Untuk informasi lebih lanjut, lihat:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10, saya mendapatkan 2 3 4 5 6 7 8 9 10 12alih-alih 2 3 4 5 6 7 8 9 10 11. Saya tidak tahu mengapa, mungkin saya salah ketik. Lagi pula sepertinya lebih canggih daripada pendekatan sederhana saya, dan itu melibatkan regex untuk menggambarkan di mana makro harus memindahkan kursor, serta daftar lokasi yang saya belum pernah lihat digunakan dengan cara ini. Saya sangat menyukainya!
\d\+untuk menggambarkan beberapa angka digit.
:lv ...perintah, :llaperintah dapat digunakan untuk melompat ke pertandingan terakhir dan :lpperintah tersebut dapat digunakan untuk memajukan pertandingan dengan urutan terbalik.
Makro rekursif akan berhenti segera setelah menemukan perintah yang gagal. Oleh karena itu, untuk berhenti di akhir baris, Anda memerlukan perintah yang akan gagal di akhir baris.
Secara default *, lperintahnya adalah perintah seperti itu, sehingga Anda dapat menggunakannya untuk menghentikan makro rekursif. Jika kursor tidak berada di akhir baris, maka Anda hanya perlu memindahkannya kembali setelah itu dengan perintah h.
Jadi, dengan menggunakan contoh makro yang sama dengan saginaw :
qqqqq<c-a>lhw@qq
Rusak:
qqq: Kosongkan daftar q,qq: Mulai merekam makro di qregister,<c-a>: Tambahkan angka di bawah kursor,lh: Jika kita berada di akhir baris, batalkan makro. Kalau tidak, jangan lakukan apa pun.w: Maju ke kata berikutnya di telepon.@q: Perulanganq: Berhenti merekam.Anda kemudian dapat menjalankan makro dengan 0@qperintah yang sama seperti yang dijelaskan oleh saginaw.
* 'whichwrap'Opsi ini memungkinkan Anda untuk menentukan tombol gerakan mana yang akan membungkus ke baris berikutnya ketika Anda berada di awal atau akhir garis (Lihat :help 'whichwrap'). Jika Anda telah lmengatur opsi ini, maka itu akan merusak solusi yang dijelaskan di atas.
Namun, ada kemungkinan bahwa Anda hanya menggunakan salah satu dari tiga standar perintah yang normal-mode untuk memajukan satu karakter ( <Space>, l, dan <Right>), jadi jika Anda telah ltermasuk dalam Anda 'whichwrap'pengaturan, Anda dapat menghapus salah satu yang Anda tidak gunakan dari 'whichwrap'opsi, misalnya untuk <Space>:
:set whichwrap-=s
Kemudian Anda bisa mengganti lperintah di langkah 4 makro dengan <Space>perintah.
virtualedit=onemoreakan mengganggu penggunaan luntuk mendeteksi end-of-line, meskipun tidak seserius itu whichwrap=l.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@qAkan menambah semua angka pada baris 3. Mungkin ada cara untuk membuat solusi ini kurang rapuh?