¿Cuál es la diferencia entre los átomos '\ zs' y '\ @ <=' en Vim regex?

11

Esto es lo que obtengo de la documentación: \zs"comienza la parte resaltada" después de hacer coincidir la expresión regular anterior, y \@<="comienza la parte resaltada" después de hacer coincidir el átomo anterior . Pero no entiendo exactamente las sutilezas de esto, entonces, ¿alguien puede explicar cómo difieren un poco más en profundidad?

Esto es lo que me hizo sentir curiosidad: si corro

/\_s\zsnnoremap

es decir, seleccione nnoremapprecedido por un espacio o un inicio de línea (es decir, la nueva línea desde la línea anterior, de ahí la \_anterior s) y luego ejecute gnpara ingresar al Modo Visual y seleccione visualmente la siguiente coincidencia, por alguna razón solo la primera columna (es decir la primera nen nnoremap) se selecciona - a pesar del hecho de que toda la nnoremappalabra se destacó con el :hlsearchencendido.

Sin embargo, si en su lugar ejecuto la búsqueda

/\_s\@<=nnoremap

e intente gn, todo nnoremapestá correctamente seleccionado. ¿Qué podría estar pasando aquí? ¿Descubrí (me atrevo a decir) algún error oscuro?

Luke Davis
fuente
Creo que está dentro, :h patternspero mi memoria sugiere que las expresiones regulares están compuestas de átomos, si eso ayuda a explicar la diferencia.
D. Ben Knoble

Respuestas:

15

Parece que de hecho has encontrado un error oscuro. He implementado el objeto de gntexto en 2012 para Vim 7.3 algo. Básicamente funciona de la siguiente manera:

1) Busca hacia atrás la última coincidencia de la expresión regular actual.

2) Busca la próxima coincidencia de la expresión regular actual.

Esto debería dejar en claro que el cursor estará en el inicio de la próxima partida, incluso si ya estaba allí al comienzo de 1). Finalmente

3) busca el final de la expresión regular actual. y pone el cursor allí.

Ahora, lo que sucede aquí es que la búsqueda del final de la coincidencia actual se ajusta y regresa al final de la coincidencia anterior (porque wrapscanya está establecida, después de estar deshabilitada para 1)). Luego establece el marcador Visual en el área desde el inicio (final del punto 2) y el área movida por el siguiente elemento de búsqueda 3).

Analizaré más de cerca el problema y probablemente enviaré un parche para Vim más tarde.

[Actualización 22.05.2018] He escrito y enviado un parche para corregir este comportamiento.

[Actualización2 22.05.2018] Y el parche se ha fusionado como parche nivel 8.1.0018

[Actualización 22.10.2019] A partir del parche Vim 8.1.629, el tercer paso ya no se realiza. En cambio, Vim ahora puede determinar el final de la partida al encontrar el comienzo de la partida (Paso 2)

Christian Brabandt
fuente
8

Christian ha abordado por completo la cuestión del comportamiento defectuoso de gn, pero todavía hay diferencias fundamentales entre \zsy \@<=. El ser más grande \@<=modifica un átomo precedente, mientras que \zses un átomo en sí mismo.

Considerar:

Xnnoremap

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

Regex 1 coincide, ya que \%1ccoincide con la columna 1 y hay una X allí. \zssimplemente hace que el partido se reinicie en una posición después de la X.

Sin embargo, Regex 2 no coincide, porque aunque \%1ccoincide con la primera columna, X\@<=tiene un ancho cero (como se menciona en la documentación) y nnoremapcomienza en la columna 2. No hay nada que compense la diferencia de posición entre las columnas 1 y 2.

Regex 3 coincide desde que nnoremapcomienza en la columna 2.

Masa
fuente
1
No creo que la expresión regular 2 falle porque no hay nada que compense la diferencia de posición entre las columnas 1 y 2. Si ese fuera el problema, eliminar nnoremapde la expresión regular produciría una coincidencia; pero la expresión regular todavía falla incluso sin ella. Creo que falla porque \%1cX\@<=expresa una posición que no puede existir. \%1ccoincide con la posición en la columna 1, y X\@<=pide un personaje Xque coincida antes de eso. Pero no puede haber ningún personaje antes de la primera columna. Es por eso que, incluso si lo reemplaza Xcon un punto (cualquier carácter), la expresión regular \%1c.\@<=aún falla.
user938271
4

\zsse aplica a toda la expresión regular y establece que el siguiente carácter sea el primer carácter de toda la coincidencia. Cualquier cosa anterior al \zsno se incluirá como parte del texto correspondiente.

\@<=, por otro lado, solo afecta a los átomos directamente a su alrededor, lo que le permite especificar que el siguiente átomo solo coincidirá si sigue al átomo anterior. Entonces, por ejemplo, la expresión regular:

\vbar.*(foo)@<=bar

Emparejará todo el texto entre dos instancias de bar(incluidas las instancias mismas), pero solo si la segunda está precedida por foo. es decir, coincidirá con:

barbazfoobar

pero no:

barbazbazbar

Debido a que \@<=está localizado de esta manera, incluso puede usar \@<=varias veces en una sola expresión:

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

Lo siguiente coincidirá con tres instancias de bar, pero solo si las dos últimas están precedidas por cada una foo.

es decir, dado el texto:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Solo coincidirá con la primera línea.

Rico
fuente
Pero se puede intercambiar la primera búsqueda hacia atrás con \zs, es decir, esto también debería funcionar: \vfoo\zsbar.*(foo)@<=bar.
Karl Yngve Lervåg
@ KarlYngveLervåg Buen punto. He editado para aclarar la distinción y para usar ejemplos donde \zsno se puede sustituir en absoluto.
Rico
Entonces, para mi comprensión, \zsy \zepuede ser reemplazado por mirar alrededor de los patrones de expresiones regulares, y son más potentes, ¿verdad? Porque son más potentes porque se pueden usar más de una vez y se pueden agrupar \(\). Y también porque funcionan como la mirada de Perl alrededor de la expresión regular. Algo equivocado?
Klaus
1
@klaus Eso me parece correcto (aunque no soy un experto). Sin embargo, tenga en cuenta que debe usar \zs/ \zecuando pueda, porque son más rápidos que las miradas.
Rich
Entendido. Y \zsy \zeobviamente son más intuitivos. Gracias por las explicaciones
Klaus