Qual è la differenza tra gli atomi '\ zs' e '\ @ <=' in Vim regex?


11

Questo è ciò che ottengo dalla documentazione: \zs"avvia la parte evidenziata" dopo aver abbinato la regex precedente e \@<="avvia la parte evidenziata" dopo aver abbinato l' atomo precedente . Ma non capisco esattamente le sottigliezze di questo, quindi qualcuno può spiegare come differiscono un po 'più in profondità?

Questo è ciò che mi ha incuriosito: se corro

/\_s\zsnnoremap

cioè selezionare nnoremappreceduto da uno spazio o un inizio di riga (ovvero la nuova riga dalla riga precedente, quindi la \_precedente la s) e quindi eseguire gnper accedere alla modalità visiva e selezionare visivamente la corrispondenza successiva, per qualche motivo solo la prima colonna (cioè il primo nin nnoremap) è selezionato - nonostante il fatto che l'intera nnoremapparola sia evidenziata con :hlsearchattivata.

Tuttavia, se invece eseguo la ricerca

/\_s\@<=nnoremap

e quindi provare gn, l'intero nnoremapè selezionato correttamente. Cosa potrebbe succedere qui? Ho (osato dire) scoperto qualche insetto oscuro?


Penso che sia dentro :h patternsma la mia memoria suggerisce che le regex sono composte da atomi, se ciò aiuta a spiegare la differenza.
D. Ben Knoble,

Risposte:


15

Sembra che tu abbia davvero trovato un bug oscuro. Ho implementato gntextobject nel 2012 per Vim 7.3 qualcosa. Funziona sostanzialmente nel modo seguente:

1) Cerca all'indietro l'ultima corrispondenza dell'espressione regolare corrente.

2) Cerca in avanti la corrispondenza successiva dell'espressione regolare corrente.

Ciò dovrebbe chiarire che il cursore si troverà all'inizio della prossima partita, anche se era già presente all'inizio di 1). Finalmente

3) cerca la fine dell'espressione regolare corrente. e posiziona il cursore lì.

Ora quello che succede qui è che la ricerca della fine della partita corrente si avvolge e torna alla fine della partita precedente (perché wrapscanè già impostata, dopo essere stata disabilitata per 1)). Quindi imposta il marcatore visivo sull'area dall'inizio (fine del punto 2) e l'area spostata dall'elemento di ricerca successivo 3).

Esaminerò più da vicino il problema e probabilmente invierò una patch per Vim in seguito.

[Aggiornamento 22.05.2018] Ho scritto e inviato una patch per correggere questo comportamento.

[Update2 22.05.2018] E la patch è stata unita come livello di patch 8.1.0018

[Aggiornamento 22.10.2019] A partire dalla patch Vim 8.1.629 il terzo passaggio non viene più eseguito. Invece Vim ora può determinare la fine della partita quando trova l'inizio della partita (Passaggio 2)


8

Christian ha affrontato completamente la questione del comportamento buggy di gn, ma ci sono ancora differenze fondamentali tra \zse \@<=. L'essere più grande \@<=modifica un atomo precedente, mentre \zsè un atomo in se stesso.

Prendere in considerazione:

Xnnoremap

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

Regex 1 corrisponde, poiché \%1ccorrisponde alla colonna 1 e vi è una X lì. \zscausa semplicemente il riavvio della partita in una posizione dopo la X.

Regex 2 tuttavia non corrisponde, perché sebbene \%1ccorrisponda alla prima colonna, ha X\@<=una larghezza pari a zero (come indicato nella documentazione) e nnoremapinizia dalla colonna 2. Non c'è nulla che compensi la differenza di posizione tra le colonne 1 e 2.

Regex 3 corrisponde da quando nnoremapinizia alla colonna 2.


1
Non credo che il regex 2 fallisca perché non c'è nulla che possa compensare la differenza di posizione tra le colonne 1 e 2. Se questo fosse il problema, la rimozione nnoremapdal regex produrrebbe una corrispondenza; ma la regex fallisce ancora anche senza. Penso che fallisca perché \%1cX\@<=esprime una posizione che non può esistere. \%1ccorrisponde alla posizione nella colonna 1 e X\@<=chiede che un personaggio Xcorrisponda prima di quello. Ma non ci può essere alcun personaggio prima della prima colonna. Ecco perché, anche se si sostituisce Xcon un punto (qualsiasi carattere), la regex \%1c.\@<=fallisce ancora.
user938271

4

\zssi applica all'intera espressione regolare e imposta il carattere successivo come primo carattere dell'intera corrispondenza. Tutto ciò che precede \zsnon verrà incluso come parte del testo corrispondente.

\@<=d'altra parte, influenza solo gli atomi direttamente attorno ad esso, consentendo di specificare che l'atomo successivo corrisponderà solo se segue l'atomo precedente. Quindi, ad esempio, l'espressione regolare:

\vbar.*(foo)@<=bar

Corrisponderà a tutto il testo tra due istanze di bar(comprese le istanze stesse), ma solo se la seconda è preceduta da foo. cioè, corrisponderà:

barbazfoobar

ma no:

barbazbazbar

Perché \@<=è localizzato in questo modo, puoi persino usare \@<=più volte in una singola espressione:

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

Di seguito corrisponderanno tre istanze di bar, ma solo se le seconde due sono precedute da foo.

cioè dato il testo:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Corrisponderà solo alla prima riga.


Ma si può scambiare la prima lookbehind con \zs, per esempio, questo dovrebbe funzionare anche: \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg

@ KarlYngveLervåg Un buon punto. Ho modificato per rendere più chiara la distinzione e per usare esempi in cui \zsnon è possibile sostituirli affatto.
Ricco

Quindi, per la mia comprensione, \zse \zepuò essere sostituito con uno sguardo intorno ai modelli regex, e sono più potenti, giusto? Causa più potente possono essere utilizzati più di una volta e possono essere raggruppati \(\). E anche perché funzionano come lo sguardo di perl intorno a regex. Qualcosa non va?
klaus

1
@klaus Mi sembra giusto (anche se non sono un esperto). Nota che dovresti usare \zs/ \zequando puoi, perché sono più veloci degli sguardi.
Rich

Inteso. E \zse \zesono ovviamente più intuitivi. Grazie per le spiegazioni
klaus
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.