Jaka jest różnica między atomami „\ zs” i „\ @ <=” w wyrażeniu regularnym Vim?

12

Oto, co otrzymuję z dokumentacji: \zs„rozpoczyna podświetloną część” po dopasowaniu poprzedniego wyrażenia regularnego, i \@<=„rozpoczyna podświetloną część” po dopasowaniu poprzedniego atomu . Ale nie do końca rozumiem subtelności tego, więc czy ktokolwiek może wyjaśnić, czym różnią się nieco bardziej dogłębnie?

Właśnie to mnie zainteresowało: jeśli biegnę

/\_s\zsnnoremap

tzn. wybierz nnoremappoprzedzony spacją lub początkiem linii (tj. nowa linia z poprzedniej linii, stąd \_poprzednia s), a następnie uruchom, gnaby przejść do trybu wizualnego i wizualnie wybierz następne dopasowanie, z jakiegoś powodu tylko pierwszą kolumnę (tj. pierwsze nw nnoremap) jest zaznaczone - pomimo tego, że całe nnoremapsłowo jest podświetlone z :hlsearchwłączonym.

Jeśli jednak zamiast tego uruchomię wyszukiwanie

/\_s\@<=nnoremap

a następnie spróbuj gn, całość nnoremapjest odpowiednio wybrana. Co tu się dzieje? Czy (śmiem powiedzieć) odkryłem jakiś niejasny błąd?

Luke Davis
źródło
Myślę, że tak, :h patternsale moja pamięć sugeruje, że wyrażenia regularne składają się z atomów, jeśli to pomaga wyjaśnić różnicę.
D. Ben Knoble,

Odpowiedzi:

15

Wygląda na to, że rzeczywiście znalazłeś niejasny błąd. Zaimplementowałem gntextobject już w 2012 roku dla Vima 7.3. Zasadniczo działa w następujący sposób:

1) Wyszukuje do tyłu ostatnie dopasowanie bieżącego wyrażenia regularnego.

2) Szuka do przodu następnego dopasowania bieżącego wyrażenia regularnego.

Powinno to wyjaśnić, że kursor będzie na początku następnego meczu, nawet jeśli był tam już na początku 1). Wreszcie

3) szuka końca bieżącego wyrażenia regularnego. i umieszcza tam kursor.

To, co się tutaj dzieje, polega na tym, że wyszukiwanie końca bieżącego dopasowania jest zawijane i przechodzi z powrotem na koniec poprzedniego dopasowania (ponieważ wrapscanjest już ustawione, po wyłączeniu na 1). Następnie ustawia znacznik wizualny na obszar od początku (koniec punktu 2) i obszar przesunięty do następnego elementu wyszukiwania 3).

Przyjrzę się bliżej problemowi i prawdopodobnie prześlę łatkę dla Vima później.

[Aktualizacja 22.05.2018] Napisałem i przesłałem łatkę, aby naprawić to zachowanie.

[Aktualizacja2 22.05.2018] A łatka została scalona jako łatka na poziomie 8.1.0018

[Aktualizacja 22.10.2019] Od poprawki Vima 8.1.629 trzeci krok nie jest już wykonywany. Zamiast tego Vim może teraz określić koniec meczu, znajdując początek meczu (krok 2)

Christian Brabandt
źródło
8

Christian całkowicie zajął się kwestią zachowania błędnego gn, ale nadal istnieją fundamentalne różnice między \zsi \@<=. Największa istota \@<=modyfikuje poprzedni atom, podczas gdy \zssama jest atomem.

Rozważać:

Xnnoremap

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

Regeks 1 pasuje, ponieważ \%1cpasuje do kolumny 1 i tam jest X. \zspowoduje jedynie, że dopasowanie zostanie wznowione w miejscu po X.

Regeks 2 jednak nie pasuje, ponieważ chociaż \%1cpasuje do pierwszej kolumny, X\@<=ma zerową szerokość (jak wspomniano w dokumentacji) i nnoremapzaczyna się od kolumny 2. Nic nie może nadrobić różnicy pozycji między kolumnami 1 i 2.

Regex 3 pasuje od początku nnoremapw kolumnie 2.

Masa
źródło
1
Nie sądzę, aby regex 2 zawiódł, ponieważ nie ma nic, co mogłoby nadrobić różnicę pozycji między kolumnami 1 i 2. Gdyby to był problem, usunięcie nnoremapz wyrażenia regularnego spowodowałoby dopasowanie; ale wyrażenie regularne nadal nie działa nawet bez. Myślę, że to się nie udaje, ponieważ \%1cX\@<=wyraża pozycję, która nie może istnieć. \%1cdopasowuje pozycję w kolumnie 1 i X\@<=prosi o Xdopasowanie znaku przed tym. Ale przed pierwszą kolumną nie może być żadnego znaku. Dlatego nawet jeśli zamienisz Xkropkę (dowolny znak), wyrażenie regularne \%1c.\@<=nadal zawiedzie.
user938271
4

\zsdotyczy całego wyrażenia regularnego i ustawia następny znak jako pierwszy znak całego dopasowania. Coś wcześniejszego \zsnie będzie uwzględnione jako część pasującego tekstu.

\@<=, z drugiej strony, wpływa tylko na atomy bezpośrednio wokół niego, co pozwala określić, że następny atom będzie pasował tylko wtedy, gdy będzie następował po atomie poprzedzającym. Na przykład wyrażenie regularne:

\vbar.*(foo)@<=bar

Dopasuje cały tekst między dwoma wystąpieniami bar(w tym same wystąpienia), ale tylko wtedy, gdy drugi będzie poprzedzony znakiem foo. tzn. będzie pasować:

barbazfoobar

ale nie:

barbazbazbar

Ponieważ \@<=jest on zlokalizowany w ten sposób, możesz nawet użyć \@<=wiele razy w jednym wyrażeniu:

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

Poniższe będzie pasowało do trzech wystąpień bar, ale tylko wtedy, gdy każde z dwóch poprzedzonych będzie znakiem foo.

tzn. biorąc pod uwagę tekst:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Będzie pasować tylko do pierwszej linii.

Bogaty
źródło
Ale można wymieniać z pierwszego lookbehind \zs, czyli ta powinna także działać: \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg
@ KarlYngveLervåg Dobra uwaga. Zredagowałem, aby jaśniej rozróżnić i użyć przykładów, których w \zsogóle nie można zastąpić.
Bogaty
Tak więc, dla mojego zrozumienia, \zsi \zemożna je zastąpić rozejrzeniem się po wzorach wyrażeń regularnych, a są one bardziej wydajne, prawda? Potężniejsze, ponieważ mogą być używane więcej niż jeden raz i mogą być grupowane \(\). A także dlatego, że działają jak Perl rozglądają się po wyrażeniu regularnym. Coś się myliło?
klaus
1
@klaus To brzmi dla mnie dobrze (chociaż nie jestem ekspertem). Pamiętaj jednak, że powinieneś używać \zs/ \zekiedy możesz, ponieważ są one szybsze niż rozglądanie się.
Rich
Zrozumiany. A \zsi \zesą oczywiście bardziej intuicyjne. Dziękuję za wyjaśnienia.
klaus