Apa perbedaan antara atom '\ zs' dan '\ @ <=' di Vim regex?

11

Inilah yang saya dapatkan dari dokumentasi: \zs"memulai bagian yang disorot" setelah mencocokkan regex sebelumnya, dan \@<="memulai bagian yang disorot" setelah mencocokkan atom sebelumnya . Tapi saya tidak benar-benar memahami seluk-beluk ini, jadi adakah yang bisa menjelaskan bagaimana perbedaannya sedikit lebih dalam?

Inilah yang membuat saya penasaran: apakah saya lari

/\_s\zsnnoremap

yaitu pilih nnoremapdidahului oleh spasi atau garis awal (yaitu baris baru dari garis sebelumnya, maka \_sebelumnya s) dan kemudian jalankan gnuntuk masuk ke Mode Visual dan pilih secara visual pertandingan berikutnya, untuk beberapa alasan hanya kolom pertama (yaitu pertama nmasuk nnoremap) dipilih - terlepas dari kenyataan bahwa seluruh nnoremapkata disorot dengan :hlsearchdihidupkan.

Namun, jika saya malah menjalankan pencarian

/\_s\@<=nnoremap

dan kemudian coba gn, keseluruhannya nnoremapdipilih dengan benar. Apa yang mungkin terjadi di sini? Apakah saya (berani saya katakan) menemukan beberapa bug yang tidak jelas?

Luke Davis
sumber
Saya pikir itu dalam :h patternstetapi ingatan saya menunjukkan bahwa regex terdiri dari atom, jika itu membantu menjelaskan perbedaannya.
D. Ben Knoble

Jawaban:

15

Sepertinya Anda memang menemukan bug yang tidak dikenal. Saya telah mengimplementasikan objek gnteks pada tahun 2012 untuk Vim 7.3 sesuatu. Ini pada dasarnya bekerja dengan cara berikut:

1) Pencarian mundur untuk kecocokan terakhir dari persamaan reguler saat ini.

2) Ia mencari maju untuk kecocokan berikutnya dari persamaan reguler saat ini.

Ini harus menjelaskan, bahwa kursor akan berada di awal pertandingan berikutnya, bahkan jika sudah ada di awal 1). Akhirnya

3) ia mencari akhir dari ekspresi reguler saat ini. dan meletakkan kursor di sana.

Sekarang yang terjadi di sini adalah bahwa pencarian untuk akhir pertandingan saat ini membungkus dan bergerak kembali ke akhir pertandingan sebelumnya (karena wrapscansudah ditetapkan, setelah dinonaktifkan untuk 1)). Ini kemudian menetapkan penanda Visual ke area dari awal (akhir poin 2) dan area dipindahkan ke oleh item pencarian berikutnya 3).

Saya akan melihat masalah ini lebih dekat dan mungkin akan mengirimkan tambalan untuk Vim nanti.

[Pembaruan 22.05.2018] Saya telah menulis dan mengirimkan tambalan untuk memperbaiki perilaku ini.

[Pembaruan2 22.05.2018] Dan tambalan telah digabung sebagai tambalan level 8.1.0018

[Pembaruan 22.10.2019] Pada Vim patch 8.1.629 langkah ketiga tidak dilakukan lagi. Sebaliknya Vim sekarang dapat menentukan akhir pertandingan ketika menemukan awal pertandingan (Langkah 2)

Christian Brabandt
sumber
8

Christian telah sepenuhnya menjawab pertanyaan tentang perilaku kereta gn, tetapi masih ada perbedaan mendasar antara \zsdan \@<=. Makhluk terbesar \@<=memodifikasi atom sebelumnya, sedangkan \zsatom adalah diri sendiri.

Mempertimbangkan:

Xnnoremap

\%1cX\zsnnoremap     (regex 1)
\%1cX\@<=nnoremap    (regex 2)
\%2cX\@<=nnoremap    (regex 3)

Regex 1 cocok, karena \%1ccocok dengan kolom 1 dan ada X di sana. \zshanya menyebabkan pertandingan dimulai kembali pada posisi setelah X.

Namun Regex 2 tidak cocok, karena meskipun \%1ccocok dengan kolom pertama, X\@<=adalah lebar nol (seperti yang disebutkan dalam dokumentasi) dan nnoremapdimulai pada kolom 2. Tidak ada yang membuat perbedaan posisi antara kolom 1 dan 2.

Regex 3 cocok sejak nnoremapdimulai pada kolom 2.

Massa
sumber
1
Saya tidak berpikir regex 2 gagal karena tidak ada yang membuat perbedaan posisi antara kolom 1 dan 2. Jika itu masalahnya, menghapus nnoremapdari regex akan menghasilkan kecocokan; tetapi regex masih gagal bahkan tanpa. Saya pikir itu gagal karena \%1cX\@<=menyatakan suatu posisi yang tidak mungkin ada. \%1ccocok dengan posisi di kolom 1, dan X\@<=meminta karakter yang Xcocok sebelum itu. Tetapi tidak ada karakter apa pun sebelum kolom pertama. Itulah sebabnya, bahkan jika Anda mengganti Xdengan titik (karakter apa pun), regex \%1c.\@<=masih gagal.
user938271
4

\zsberlaku untuk seluruh ekspresi reguler, dan menetapkan karakter berikutnya menjadi karakter pertama dari seluruh pertandingan. Apa pun sebelum \zstidak akan dimasukkan sebagai bagian dari teks yang cocok.

\@<=, di sisi lain, hanya memengaruhi atom-atom langsung di sekitarnya, memungkinkan Anda menentukan bahwa atom berikutnya hanya akan cocok jika mengikuti atom sebelumnya. Jadi misalnya, ekspresi reguler:

\vbar.*(foo)@<=bar

Akan mencocokkan semua teks antara dua instance dari bar(termasuk instance itu sendiri), tetapi hanya jika yang kedua didahului oleh foo. yaitu, itu akan cocok dengan:

barbazfoobar

tapi tidak:

barbazbazbar

Karena \@<=dilokalisasi dengan cara ini, Anda bahkan dapat menggunakan \@<=beberapa kali dalam satu ekspresi:

\vbar.*(foo)@<=bar.*(foo)@<=bar

Berikut ini akan cocok dengan tiga contoh bar, tetapi hanya jika dua yang kedua masing-masing didahului oleh foo.

yaitu diberi teks:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Ini akan cocok dengan baris pertama saja.

Kaya
sumber
Tapi Anda bisa menukar lookbehind pertama dengan \zs, yaitu, ini juga harus bekerja: \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg
@ KarlYngveLervåg Poin bagus. Saya telah mengedit untuk memperjelas perbedaannya, dan untuk menggunakan contoh-contoh di mana \zstidak dapat diganti sama sekali.
Kaya
Jadi, untuk pengertian saya, \zsdan \zebisa diganti dengan melihat-lihat pola regex, dan mereka lebih kuat, bukan? Penyebab yang lebih kuat dapat digunakan lebih dari sekali dan dapat dikelompokkan \(\). Dan juga karena mereka bekerja seperti perl melihat-lihat regex. Adakah yang salah?
klaus
1
@klaus Kedengarannya benar bagi saya (meskipun saya bukan ahli). Perhatikan bahwa Anda harus menggunakan \zs/ \zeketika Anda bisa, karena mereka lebih cepat daripada melihat-lihat.
Rich
Dimengerti Dan \zsdan \zejelas lebih intuitif. Terima kasih atas penjelasannya.
klaus