Matching words within X lines

13 views
Skip to first unread message

A. Wik

unread,
Dec 6, 2020, 1:12:20 PM12/6/20
to vim...@googlegroups.com
Hi all,

I'm trying to match a couple (maybe more later) of words occurring
within 3 lines of each other in any order. Is there a way to do this
with a Vim regex?

-Albert.

Tim Chase

unread,
Dec 6, 2020, 9:23:17 PM12/6/20
to vim...@googlegroups.com
The short answer is "yes", but the specifics depend on what you mean.
Can the words come in any order? Is this for just 2 words? Or more
than two words? If they're sets of words, does it count to find the
*same* word within N lines?

For two words in a fixed order (HI followed by BYE in the subsequent
3 lines), you can do something like

/\<HI\>\%(.*\n\)\{,2}.*\<BYE\>

where the "2" is the N-lines-minus-1

For HI and BYE withing 3 lines but the order might be reversed (BYE
then HI), it's likely easiest to do the same thing with the "or"
conjunction:

/\<HI\>\%(.*\n\)\{,2}.*\<BYE\>\|\<BYE\>\%(.*\n\)\{,2}.*\<HI\>

For arbitrary sets of words or patterns, followed by up to 3 lines,
such as HEY, HI, and BYE

/\<\%(HI\|HEY\|BYE\)\>\%(.*\_.\)\{,2}.*\<\%(HEY\|HI\|BYE\)\>

though that could find instances where the first and second words are
the same such as "HI...HI". If you want to make sure they are
different, you might use this monster

/\<\(HI\|HEY\|BYE\)\>\%(.*\_.\)\{,2}.*\<\%(\1\>\)\@!\%(HEY\|HI\|BYE\)\>

which uses the "the thing we captured as the first word can't match
at the point where we find the second word" "\@!" token.

They're ugly, but vim will at least let you do them.

-tim




Tim Chase

unread,
Dec 6, 2020, 9:29:16 PM12/6/20
to vim...@googlegroups.com
On 2020-12-06 20:23, Tim Chase wrote:
> They're ugly, but vim will at least let you do them.

Oh, one other caveat: it only finds the bookends and starts the next
search after the closing bookend. So if there is the possiblity that
the matches overlap, such as searching for "A...B" and you have

A text A text B text B

it will find the first A through its corresponding B, but won't find
the second A because it has been eaten as part of the first match.
Not a grievous concern, but at least something to be aware of if
you're using it to do some sanity-/duplicate-checking on your
documents.

-tim


A. Wik

unread,
Dec 7, 2020, 8:00:10 AM12/7/20
to vim...@googlegroups.com
Thank you.

I'm using it to help find information in my diary. Eg.: "For future
reference ... X". The X is a keyword suggesting what the future
reference pertains to.

Now, I want to do the following

:redir @m
:g/future.*ref.*\n.*\n
:redir END

Alternatively, instead of redir, I can use [...]/y M at the end of the
:g command.

Alas, :g includes only the line matching "future.*ref". I want to
capture the whole match. Is there a way to do that?

-aw

Gabriele F

unread,
Dec 9, 2020, 2:35:54 PM12/9/20
to vim...@googlegroups.com
On 07/12/2020 13.59, A. Wik wrote:
> Now, I want to do the following
> :redir @m
> :g/future.*ref.*\n.*\n
> :redir END
>
> Alternatively, instead of redir, I can use [...]/y M at the end of the
> :g command.
>
> Alas, :g includes only the line matching "future.*ref". I want to
> capture the whole match. Is there a way to do that?

It would seem that what's wrong with the first command is simply that
:p, which is the command used by default by :g, by itself prints only
*one* line. If you instead explicitly use :p {count}, e.g.
":g/future.*ref.*\n.*\n/p 3", it seems to work.

But, was this what you meant to ask in the first question, or an
additional issue?
«trying to match a couple (maybe more later) of words occurring within 3
lines of each other in any order» seems to mean more what Tim helped you
with...?


Gabriele

A. Wik

unread,
Dec 10, 2020, 10:29:58 AM12/10/20
to vim_use
On Wed, 9 Dec 2020 at 19:35, Gabriele F <gb...@tiscali.it> wrote:
>
> On 07/12/2020 13.59, A. Wik wrote:
> > Now, I want to do the following
> > :redir @m
> > :g/future.*ref.*\n.*\n
> > :redir END
> >
> > Alternatively, instead of redir, I can use [...]/y M at the end of the
> > :g command.
> >
> > Alas, :g includes only the line matching "future.*ref". I want to
> > capture the whole match. Is there a way to do that?
>
> It would seem that what's wrong with the first command is simply that
> :p, which is the command used by default by :g, by itself prints only
> *one* line. If you instead explicitly use :p {count}, e.g.
> ":g/future.*ref.*\n.*\n/p 3", it seems to work.

Yes, but I want to collect the text that was *matched* by the pattern,
so, with ":g/future.*ref.*\n.*\n", I want the "future" until the end
of the next line. Actually, what I want is the whole line containing
the match, as well as the whole of the next line, but that is a simple
matter of adding a "^.*".

> But, was this what you meant to ask in the first question, or an
> additional issue?

An additional issue.

> «trying to match a couple (maybe more later) of words occurring within 3
> lines of each other in any order» seems to mean more what Tim helped you
> with...?

That was the question he helped me with. I ran into the additional issue later.

-aw

Gabriele F

unread,
Dec 10, 2020, 11:32:49 AM12/10/20
to vim...@googlegroups.com
On 10/12/2020 16.29, A. Wik wrote:
> On Wed, 9 Dec 2020 at 19:35, Gabriele F <gb...@tiscali.it> wrote:
>> On 07/12/2020 13.59, A. Wik wrote:
>>> Alas, :g includes only the line matching "future.*ref". I want to
>>> capture the whole match. Is there a way to do that?
>> It would seem that what's wrong with the first command is simply that
>> :p, which is the command used by default by :g, by itself prints only
>> *one* line. If you instead explicitly use :p {count}, e.g.
>> ":g/future.*ref.*\n.*\n/p 3", it seems to work.
> Yes, but I want to collect the text that was *matched* by the pattern,
> so, with ":g/future.*ref.*\n.*\n", I want the "future" until the end
> of the next line. Actually, what I want is the whole line containing
> the match, as well as the whole of the next line, but that is a simple
> matter of adding a "^.*".

Well if you want only from "future" I don't know, but if instead you
want "the whole line containing
the match, as well as the whole of the next line"
":g/future.*ref.*\n.*\n/p 2" seems to give that result, and only if
there is a match (yes, I put "p 3" by mistake in the other mail).
Of course inelegant and inflexible, I don't know if there are better ways

A. Wik

unread,
Dec 10, 2020, 11:57:14 AM12/10/20
to vim_use
On Thu, 10 Dec 2020 at 16:32, Gabriele F <gb...@tiscali.it> wrote:
>
> Well if you want only from "future" I don't know, but if instead you
> want "the whole line containing
> the match, as well as the whole of the next line"
> ":g/future.*ref.*\n.*\n/p 2" seems to give that result, and only if
> there is a match (yes, I put "p 3" by mistake in the other mail).
> Of course inelegant and inflexible, I don't know if there are better ways

You are right. And in this particular case, the regex can be
simplified to "/future.*ref/".

-aw

Gabriele F

unread,
Dec 10, 2020, 12:06:19 PM12/10/20
to vim...@googlegroups.com
On 10/12/2020 17.56, A. Wik wrote:
> You are right. And in this particular case, the regex can be
> simplified to "/future.*ref/".

Sure

Reply all
Reply to author
Forward
0 new messages