Quelle est la différence entre les atomes «\ zs» et «\ @ <=» dans l'expression régulière de Vim?

11

Voici ce que j'obtiens de la documentation: \zs"démarre la partie en surbrillance" après avoir trouvé l'expression rationnelle précédente, et \@<="démarre la partie en surbrillance" après avoir trouvé l' atome précédent . Mais je ne comprends pas exactement les subtilités de cela, alors quelqu'un peut-il expliquer en quoi elles diffèrent un peu plus en profondeur?

C'est ce qui m'a rendu curieux: si je cours

/\_s\zsnnoremap

c'est-à-dire sélectionner nnoremapprécédé d'un espace ou d'un début de ligne (c'est-à-dire la nouvelle ligne de la ligne précédente, donc la \_précédente de la s), puis exécuter gnpour passer en mode visuel et sélectionner visuellement la correspondance suivante, pour une raison quelconque, uniquement la première colonne (c'est-à-dire le premier ndans nnoremap) est sélectionné - malgré le fait que le nnoremapmot entier est mis en surbrillance avec :hlsearchactivé.

Cependant, si je lance plutôt la recherche

/\_s\@<=nnoremap

puis essayez gn, l'ensemble nnoremapest correctement sélectionné. Que pourrait-il se passer ici? Ai-je (oserais-je dire) découvrir un bug obscur?

Luke Davis
la source
Je pense :h patternsque c'est dedans mais ma mémoire suggère que les regex sont composées d'atomes, si cela aide à expliquer la différence.
D. Ben Knoble

Réponses:

15

Il semble que vous ayez en effet trouvé un bug obscur. J'ai implémenté le gntextobject en 2012 pour quelque chose de Vim 7.3. Cela fonctionne essentiellement de la manière suivante:

1) Il recherche en arrière la dernière correspondance de l'expression régulière actuelle.

2) Il recherche la correspondance suivante de l'expression régulière en cours.

Cela devrait indiquer clairement que le curseur sera au début du match suivant, même s'il était déjà là au début de 1). finalement

3) il recherche la fin de l'expression régulière courante. et y place le curseur.

Maintenant, ce qui se passe ici, c'est que la recherche de la fin de la correspondance actuelle se termine et revient à la fin de la correspondance précédente (car elle wrapscanest déjà définie, après avoir été désactivée pour 1)). Il place ensuite le marqueur visuel sur la zone depuis le début (fin du point 2) et la zone vers laquelle se trouve l'élément de recherche suivant 3).

J'examinerai de plus près le problème et soumettrai probablement un correctif pour Vim plus tard.

[Mise à jour 22.05.2018] J'ai écrit et soumis un correctif pour corriger ce problème.

[Update2 22.05.2018] Et le correctif a été fusionné en tant que niveau de correctif 8.1.0018

[Mise à jour 22.10.2019] Depuis le patch 8.1.629 de Vim, la troisième étape n'est plus exécutée. Au lieu de cela, Vim peut maintenant déterminer la fin du match lorsqu'il trouve le début du match (étape 2)

Christian Brabandt
la source
8

Christian a complètement abordé la question du comportement en buggy de gn, mais il existe encore des différences fondamentales entre \zset \@<=. Le plus grand être \@<=modifie un atome précédent, tandis \zsqu'un est un atome en soi.

Considérer:

Xnnoremap

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

Regex 1 correspond, car \%1ccorrespond à la colonne 1 et il y a un X là. \zsfait simplement redémarrer le match à une position après le X.

Regex 2 ne correspond cependant pas, car bien qu'il \%1ccorresponde à la première colonne, il a X\@<=une largeur nulle (comme mentionné dans la documentation) et nnoremapcommence à la colonne 2. Il n'y a rien pour compenser la différence de position entre les colonnes 1 et 2.

Regex 3 correspond depuis le nnoremapdébut à la colonne 2.

Masse
la source
1
Je ne pense pas que l'expression régulière 2 échoue car il n'y a rien pour compenser la différence de position entre les colonnes 1 et 2. Si tel était le problème, la suppression nnoremapde l'expression régulière produirait une correspondance; mais le regex échoue toujours même sans. Je pense qu'il échoue car \%1cX\@<=exprime une position qui ne peut exister. \%1ccorrespond à la position de la colonne 1 et X\@<=demande qu'un caractère Xcorresponde avant cela. Mais il ne peut y avoir aucun caractère avant la première colonne. C'est pourquoi, même si vous le remplacez Xpar un point (n'importe quel caractère), l'expression régulière \%1c.\@<=échoue toujours.
user938271
4

\zss'applique à l'expression régulière entière et définit le caractère suivant comme le premier caractère de la correspondance entière. Tout ce qui précède \zsne sera pas inclus dans le texte correspondant.

\@<=, d'autre part, n'affecte que les atomes directement autour de lui, ce qui vous permet de spécifier que l'atome suivant ne correspondra que s'il suit l'atome précédent. Ainsi, par exemple, l'expression régulière:

\vbar.*(foo)@<=bar

Correspondra à tout le texte entre deux instances de bar(y compris les instances elles-mêmes), mais uniquement si la seconde est précédée de foo. c'est-à-dire qu'il correspondra à:

barbazfoobar

mais non:

barbazbazbar

Parce qu'il \@<=est localisé de cette manière, vous pouvez même l'utiliser \@<=plusieurs fois dans une seule expression:

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

Ce qui suit correspondra à trois instances de bar, mais uniquement si les deux secondes sont chacune précédées de foo.

c'est-à-dire compte tenu du texte:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Il correspondra uniquement à la première ligne.

Riches
la source
Mais vous pouvez échanger avec le premier lookbehind \zs, à savoir, cela devrait aussi fonctionner: \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg
@ KarlYngveLervåg Bon point. J'ai édité pour clarifier la distinction et pour utiliser des exemples où \zsils ne peuvent pas être substitués du tout.
Rich
Donc, pour ma compréhension, \zset \zepeut être remplacé par un regard autour des modèles d'expression régulière, et ils sont plus puissants, non? Plus puissant car ils peuvent être utilisés plus d'une fois et peuvent être regroupés \(\). Et aussi parce qu'ils fonctionnent comme l'apparence de perl autour des regex. Vous vous trompez?
klaus
1
@klaus Cela me semble juste (même si je ne suis pas un expert). Notez que vous devez utiliser \zs/ \zequand vous le pouvez, car ils sont plus rapides que les regards croisés.
Rich
Compris. Et \zset \zesont évidemment plus intuitifs. Merci pour les explications.
klaus