Bagaimana cara menghentikan makro rekursif di akhir baris?


13

Bagaimana saya bisa membuat makro rekursif sehingga hanya berjalan sampai akhir baris?

Atau bagaimana menjalankan makro rekursif sampai akhir baris saja?

Jawaban:


11

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
  1. qqqmembersihkan isi register qsehingga ketika Anda awalnya menyebutnya selama definisi makro, itu tidak akan mengganggu
  2. qq mulai merekam
  3. :let a=line('.') menyimpan nomor baris saat ini di dalam variabel a
  4. Ctrl+ amenambah angka di bawah kursor
  5. w memindahkan kursor ke nomor berikutnya
  6. :if line('.')==a|exe 'norm @q'|endif ingat makro tetapi hanya jika nomor baris tidak berubah
  7. q menghentikan rekaman

Setelah 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:

  • tidak perlu menghapus register sebelum perekaman, karena karakter @qakan ditambahkan di makro setelah didefinisikan, dan setelah Anda menimpa konten lama apa pun yang ada di sana
  • tidak perlu mengetik apa pun yang tidak biasa selama perekaman, Anda bisa fokus membuat makro yang sederhana dan berfungsi
  • kemungkinan mengujinya sebelum membuatnya rekursif untuk melihat bagaimana perilakunya

Jika 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:

  1. let @q = mendefinisikan kembali isi register q
  2. ":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,
  3. . @q .menyatukan isi qregister saat ini dengan string sebelumnya dan yang berikutnya,
  4. ":if line('.')==a|exe 'norm @q'|endif\r"ingat makro qdengan syarat bahwa garis tidak berubah

Sekali 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:

  • membuat rekursif makro tanpa syarat,
  • membuat makro rekursif pada kondisi tetap di baris saat ini

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
  1. qq mulai merekam makro di dalam register q
  2. <C-a> menambah angka di bawah kursor
  3. w memindahkan kursor ke nomor berikutnya
  4. q mengakhiri rekaman
  5. :RecursiveMacro q 0membuat makro yang tersimpan di dalam register q rekursif tetapi hanya sampai akhir baris (karena argumen kedua 0)
  6. 3G memindahkan kursor Anda ke garis arbitrer (3 misalnya)
  7. 0@q memutar ulang makro rekursif dari awal baris

Ini 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

2
Daftar lokasi dapat digunakan untuk maju di atas kecocokan pencarian di makro, selama makro tidak mengubah posisi pertandingan, mis. :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?
djjcast

@djjcast Anda dapat mempostingnya sebagai jawaban, saya sudah mencobanya dan berfungsi sangat bagus. Hanya ada satu kasus yang saya tidak mengerti, ketika saya menjalankan makro pada baris berikut 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!
Saginaw

@ Djjcast Maaf, saya baru mengerti, masalahnya datang hanya dari regex saya, saya seharusnya digunakan \d\+untuk menggambarkan beberapa angka digit.
Saginaw

@ Djjcast Ah sekarang saya mengerti apa yang Anda maksud ketika Anda mengatakan makro tidak boleh mengubah posisi pertandingan. Tapi saya tidak tahu bagaimana menyelesaikan masalah ini. Satu-satunya ide yang saya miliki adalah memperbarui daftar lokasi dari dalam makro tetapi saya tidak terbiasa dengan daftar lokasi, terlalu rumit bagi saya, sangat menyesal.
Saginaw

1
@saginaw Iterasi atas pertandingan dalam urutan terbalik sepertinya akan menyelesaikan masalah dalam kebanyakan kasus karena sepertinya kecil kemungkinan bagi makro untuk mengubah posisi pertandingan sebelumnya. Jadi setelah :lv ...perintah, :llaperintah dapat digunakan untuk melompat ke pertandingan terakhir dan :lpperintah tersebut dapat digunakan untuk memajukan pertandingan dengan urutan terbalik.
djjcast

9

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:

  1. qqq: Kosongkan daftar q,
  2. qq: Mulai merekam makro di qregister,
  3. <c-a>: Tambahkan angka di bawah kursor,
  4. lh: Jika kita berada di akhir baris, batalkan makro. Kalau tidak, jangan lakukan apa pun.
  5. w: Maju ke kata berikutnya di telepon.
  6. @q: Perulangan
  7. q: 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.


1
Perhatikan juga bahwa pengaturan virtualedit=onemoreakan mengganggu penggunaan luntuk mendeteksi end-of-line, meskipun tidak seserius itu whichwrap=l.
Kevin

@Kevin Poin bagus! Saya akan memperbarui jawaban saya untuk menyebutkan've'
Kaya
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.