Can vimgrep be used with lookaround assertions?

143 Views Asked by At

I'm trying automate some text operations on a large JavaScript project, and wish to jump to the matches using vimgrep, which I'll follow with various macro invocations.

My target lines look like this:

getText("foo", 1)

But, not like this:

getText("foo")
getText("foo", [1])

I only want to match usages of getText that have more than one parameter, which is not enclosed in an array.

This search query (/), which uses a negative look-ahead, seems to work:

getText(.*",\(.*[\)\@!

But, running this regular expression with vimgrep:

:vimgrep /getText(.*",\(.*[\)\@!/ project/src/**/*.js

...fails with this message:

E682: Invalid search pattern or delimiter

I suspect that the look-ahead part of my regex is the cause, as the error goes away when I remove it.

Is there a way to utilize lookaround assertions with vimgrep?

2

There are 2 best solutions below

0
Ingo Karkat On BEST ANSWER

:vimgrep loads each searched file into a Vim buffer and then applies Vim's regular expression search to it. So, unlike external tools as 'grepprg' / :grep, you can use the full Vim regular expression syntax here.

As @Matt already commented, the E682 is caused by failing to escape the [. When you use the expression in a regular / search, Vim just treats the [ as a literal character, as the collection isn't properly closed - your search works as expected.

In :vimgrep, the {pattern} has to be enclosed by /.../ delimiters, however. Apparently the parser continues to look for the ending /, but cannot find it as the not-yet closed [ collection keeps consuming characters, and this causes the error. This inconsistency isn't nice, and it obviously made you stumble, but it's not necessarily wrong. Now you know :-)

Just properly escape the [ character as \[, and it'll work:

:vimgrep /getText(.*",\(.*\[\)\@!/ project/src/**/*.js

There are special regexp atoms (\v and \V) that change the amount of escaping; some people like to use them (to avoid excessive escaping, or for better readability) - I mostly find the necessary shift in interpretation distracting.

0
Peter Rincker On

@IngoKarkat answered your question directly. However, :vimgrep can be slow on large projects so it would be good to have an alternative if the need arises.

:grep and 'grepprg'

Vim's :grep is similar to :vimgrep except it uses an external program (like grep) to do the searching. You can even set 'grepprg' to a different tool like: ripgrep, the silver searcher, or git-grep. Using these tools can often speed up searching considerably.

set grepprg=rg\ --vimgrep

However, each tool uses their own regex syntax and may not provide positive/negative look-ahead/behind assertions. One way to do this is to search for all the criteria with :grep and then filter out the unwanted results via :Cfilter.

:grep -t js 'getText' project/src/

:Cfilter

:Cfilter /{pat}/ will allow you to filter the quickfix results with a pattern. This pattern will use Vim's regex syntax so you can use your assertions as you normally would.

:Cfilter /getText(.*",\(.*\[\)\@!/

You can use ! to negate the filter, e.g. :Cfilter! /{pat}/, then entries not matching {pat} are used.

Note: :Cfilter is an optional package and you will need to do :packadd cfilter to load the plugin.

More help

For more help see:

:h :grep
:h 'grepprg'
:h :Cfilter