Jawaban:
Mungkin ada metode yang lebih sederhana tetapi mungkin Anda bisa mencoba yang berikut ini.
Katakanlah Anda akan menggunakan register q
untuk merekam makro rekursif Anda.
Di awal rekaman, ketikkan:
:let a = line('.')
Kemudian, di akhir rekaman, alih-alih menekan @q
untuk 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 :normal
perintah memungkinkan Anda untuk mengetik perintah normal (seperti @q
) dari mode Ex.
Dan alasan mengapa perintah dibungkus menjadi string dan dieksekusi oleh perintah :execute
adalah untuk mencegah :normal
dari 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 0
untuk 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
qqq
membersihkan isi register q
sehingga ketika Anda awalnya menyebutnya selama definisi makro, itu tidak akan menggangguqq
mulai merekam:let a=line('.')
menyimpan nomor baris saat ini di dalam variabel a
w
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 0
untuk memindahkannya ke awal baris, lalu tekan @q
untuk 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:
@q
akan 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 @q
di 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 :RecursiveMacro
yang menunggu nama register sebagai argumen (karena -register
atribut dilewatkan ke :command
).
Ini perintah yang sama seperti sebelumnya, satu-satunya perbedaan adalah Anda mengganti setiap kemunculan q
dengan <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 q
untuk membuat makro yang tersimpan di dalam register q
rekursif.
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 q
saat ini berisi register:
let @q =
mendefinisikan kembali isi register q
":let a=line('.')\r"
menyimpan nomor baris saat ini di dalam variabel a
sebelum makro melakukan tugasnya \r
diperlukan untuk memberitahu Vim untuk menekan Enter dan menjalankan perintah, lihat :help expr-quote
daftar karakter khusus yang serupa, . @q .
menyatukan isi q
register saat ini dengan string sebelumnya dan yang berikutnya,":if line('.')==a|exe 'norm @q'|endif\r"
ingat makro q
dengan 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 q
untuk membuat makro yang tersimpan di dalam register q
rekursif pada kondisi tetap pada baris saat ini.
Gabungkan 2 perintah
Anda juga dapat mengubah :RecursiveMacro
sehingga 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=1
atribut).
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 0
membuat 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 1
bukannya :RecursiveMacro q 0
, makro q
akan 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 12
alih-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, :lla
perintah dapat digunakan untuk melompat ke pertandingan terakhir dan :lp
perintah 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 *, l
perintahnya 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 q
register,<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@q
perintah 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 l
mengatur 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 l
termasuk 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 l
perintah di langkah 4 makro dengan <Space>
perintah.
virtualedit=onemore
akan mengganggu penggunaan l
untuk mendeteksi end-of-line, meskipun tidak seserius itu whichwrap=l
.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q
Akan menambah semua angka pada baris 3. Mungkin ada cara untuk membuat solusi ini kurang rapuh?