В чем разница между атомами '\ zs' и '\ @ <=' в регулярном выражении Vim?

11

Вот что я получаю из документации: \zs«запускает выделенную часть» после сопоставления с предыдущим регулярным выражением и \@<=«запускает выделенную часть» после сопоставления с предыдущим атомом . Но я не совсем понимаю тонкости этого, так кто-нибудь может объяснить, как они отличаются немного глубже?

Это то, что заставило меня любопытно: если я бегу

/\_s\zsnnoremap

т. е. выберите nnoremapпредваряющий пробел или начало строки (т. е. новую строку из предыдущей строки, следовательно, \_предшествующей s) и затем запустите, gnчтобы войти в визуальный режим и визуально выбрать следующее совпадение, по какой-то причине только первый столбец (т. е. выбрано первое nв nnoremap) - несмотря на то, что все nnoremapслово выделено при :hlsearchвключенном.

Тем не менее, если я вместо этого запустить поиск

/\_s\@<=nnoremap

а затем попробуйте gn, все nnoremapправильно выбрано. Что здесь может происходить? Я (смею сказать) обнаружил какую-то неясную ошибку?

Люк Дэвис
источник
Я думаю, что это, :h patternsно моя память предполагает, что регулярные выражения состоят из атомов, если это помогает объяснить разницу.
Д. Бен Кнобл

Ответы:

15

Похоже, вы действительно нашли неясную ошибку. Я реализовал gntextobject еще в 2012 году для Vim 7.3. В основном это работает следующим образом:

1) Он ищет в обратном порядке последнее совпадение текущего регулярного выражения.

2) Ищет следующее совпадение текущего регулярного выражения.

Это должно прояснить, что курсор будет в начале следующего матча, даже если он уже был в начале 1). в заключение

3) он ищет конец текущего регулярного выражения. и помещает курсор туда.

Теперь то, что происходит здесь, заключается в том, что поиск конца текущего совпадения завершается и возвращается к концу предыдущего совпадения (потому что wrapscanоно уже установлено, после того как отключено для 1)). Затем он устанавливает визуальный маркер в область от начала (конца точки 2) и область, в которую перемещается следующий элемент поиска 3).

Я более подробно рассмотрю проблему и, возможно, позже отправлю патч для Vim.

[Обновление 22.05.2018] Я написал и отправил патч, чтобы исправить это поведение.

[Update2 22.05.2018] И патч был объединен как патч уровня 8.1.0018

[Обновление 22.10.2019] Начиная с патча Vim 8.1.629, третий шаг больше не выполняется. Вместо этого Vim теперь может определять конец матча, когда находит начало матча (шаг 2).

Кристиан Брабандт
источник
8

Кристиан полностью рассмотрел вопрос о багги поведения gn, но все еще есть существенные различия между \zsи \@<=. Самое большое существо \@<=модифицирует предшествующий атом, в то время \zsкак это атом в самом себе.

Рассматривать:

Xnnoremap

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

Соответствует регулярное выражение 1, так как \%1cсоответствует столбцу 1 и там есть X. \zsпросто вызывает перезапуск матча с позиции после X.

Регулярное выражение 2, однако, не соответствует, потому что, хотя \%1cсоответствует первому столбцу, X\@<=имеет нулевую ширину (как упомянуто в документации) и nnoremapначинается со столбца 2. Ничто не может компенсировать разницу в положении между столбцами 1 и 2.

Регулярное выражение 3 совпадает, так как nnoremapначинается в столбце 2.

масса
источник
1
Я не думаю , что регулярное выражение 2 терпит неудачу , потому что нет ничего , чтобы компенсировать разницу в позиции между колонками 1 и 2. Если это был вопрос, удаление nnoremapиз регулярных выражений будет производить спичку; но регулярное выражение до сих пор не удается даже без него . Я думаю , что он не потому , что \%1cX\@<=выражает позицию , которая не может существовать. \%1cсоответствует положению в колонке 1, и X\@<=просит характер , Xчтобы соответствовать до этого. Но не может быть никакого символа перед первым столбцом. Именно поэтому, даже если заменить Xс точкой (любой символ), регулярное выражение до \%1c.\@<=сих пор не удается.
user938271
4

\zsприменяется ко всему регулярному выражению и устанавливает следующий символ как первый символ всего совпадения. Все, что до \zs, не будет включено как часть соответствующего текста.

\@<=, С другой стороны, влияет только на атомы непосредственно вокруг него, что позволяет указать, что следующий атом будет соответствовать только если он следует за предыдущий атом. Так, например, регулярное выражение:

\vbar.*(foo)@<=bar

Будет сопоставлять весь текст между двумя экземплярами bar(включая сами экземпляры), но только если перед вторым предшествует foo. то есть будет соответствовать:

barbazfoobar

но нет:

barbazbazbar

Поскольку \@<=локализована таким образом, вы можете даже использовать \@<=несколько раз в одном выражении:

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

Ниже будет соответствовать три экземпляра bar, но только тогда , когда второй два, каждый предшествует foo.

т.е. с учетом текста:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Это будет соответствовать только первой строке.

Богатый
источник
Но вы можете обменять первый с просмотром назад \zs, то есть, это также должно работать: \vfoo\zsbar.*(foo)@<=bar.
Карл Ингве Лервог
@ KarlYngveLervåg Хороший вопрос. Я отредактировал, чтобы прояснить различие и использовать примеры, \zsкоторые вообще нельзя заменить.
Богатый
Итак, для моего понимания, \zsи \zeможет быть заменено взглянуть на шаблоны регулярных выражений, и они более мощные, верно? Более мощные причины, они могут использоваться более одного раза и могут быть сгруппированы \(\). А также потому, что они работают, как Perl, глядя вокруг регулярных выражений. Что-нибудь не так?
клаус
1
@klaus Это звучит как раз для меня (хотя я не эксперт). Обратите внимание, что вы должны использовать \zs/, \zeкогда вы можете, потому что они быстрее, чем осмотры.
Рич
Понял. А \zsи \ze, очевидно , более интуитивным. Спасибо за объяснения.
клаус