Search code examples
searchvimpattern-matchingmatchlines

Capture Line numbers of lines matching a search


Example:

search:

/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword

This search searches 6 empty lines between a line ending with "lastword" and a line starting with "startword"

I would like to catch the linenumbers of the empty lines matching this search.

Is there a way to do this in vim?


Solution

  • You can use :g and :number (or :# for short) to print out the lines with line numbers.

    :g/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword/#
    

    To capture this content you have to use :redir to redirect the output to somewhere else. In this case below we redirect it to the unamed register (@")

    :redir @"|execute 'g/pattern/#'|redir END
    

    Note: Must use :execute with :g otherwise redir END will be executed on each matching line with the :g command.

    Now as it is this is going to print via :# the starting line which is not what we want (we want the empty lines between foo and bar). We can use a range with the :# command to accomplish this.

    :redir @"|execute 'g/foo\_.*bar/+,/bar/-#'|redir END
    

    The range is now +,/bar/- which translate to start the next line (+) and the search for bar (/bar/) then subtract one line (-). We can probably simplify this some as we know the number of empty lines should be 6.

    :redir @"|execute 'g/foo\_.*bar/+#6'|redir END
    

    We can take this further by putting the content into a new buffer and remove the extra lines.

    :redir @"|exe 'g/pattern/+#6'|redir END|new|pu|%s/\d\+\zs.*//|%le|g/^$/d
    

    There is a lot going on here, but the idea is we capture the output, open a new buffer, paste the content, and then clean up the output.

    Alternative

    Alternatively awk might make this a bit easier. Run the following on the current Vim buffer:

    :%!awk '/lastword\s*$/,/^\s*startword/ { if($0 ~ /^\s*$/) { print NR} }'
    

    For more help see:

    :h :g
    :h :redir
    :h :exe
    :h :range
    :h :new
    :h :pu
    :h /\zs
    :h :le
    :h :d