I have the following in my .vimrc
which (I believe) makes :grep
within Vim use rg
:
if executable('rg')
set grepprg=rg\ --no-heading\ --vimgrep\ --hidden\ --case-sensitive\ --ignore-vcs\ --glob\ '!.git'\ --glob\ '!node_modules'
endif
I want to search for all definitions of functions named render...
. If I do
rg -e \(const\|let\)\ render .
on the command line, I get what I'm looking for.
But
:grep -e \(const\|let\)\ render
in vim results in
zsh:1: command not found: let) render
regex parse error:
(const
^
error: unclosed group
I've tried some other combos of \
, putting the whole query in /.../
, can't quite get it working.
How do I use the alternation operator in ripgrep in vim?
There are three pieces of machinery involved, here, each with its own idiosyncrasies: Vim, your shell, and RipGrep.
Ideally, this is how your pattern should look with RipGrep's syntax:
(let|const) render
If you try it as-is:
:grep (let|const) render
you should get a cascade of messages (irrelevant lines removed):
:!rg (let 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/26
/opt/local/bin/bash: -c: line 1: syntax error near unexpected token `let'
/opt/local/bin/bash: -c: line 1: `rg (let 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/26'
shell returned 2
E40: Can't open errorfile
/var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/26
The first line:
:!rg (let 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/26
^^^^^^^
tells you that the command executed under the hood is:
rg (let
which is obviously incomplete. That is because Vim thinks that the |
is a command separator (:help :bar
) so it tries to execute the broken :grep (let
. If you want your |
to pass through, you must escape it:
:grep (let\|const) render
OK, all the arguments are now passed to rg
:
:!rg (let|const) render 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/27
^^^^^^^^^^^^^^^^^^^^^
You are not done yet, though:
/opt/local/bin/bash: -c: line 1: syntax error near unexpected token `let'
/opt/local/bin/bash: -c: line 1: `rg (let|const) render 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/27'
shell returned 2
E40: Can't open errorfile /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/27
Your pattern includes a capture group delimited with parentheses, which confuses the hell out of your shell because it looks like an attempt to execute the command let|const
in a subshell, which is bound to fail anyway, but in a context where it can't be done.
You can try to solve those problems by escaping things with backslashes but you are entering an escaping arms race between the shell and Vim. That is the kind of race where there is no winner.
A better approach is to wrap your whole pattern with single quotes:
:grep '(let\|const) render'
which tells your shell to treat what is between the quotes literally, without trying to be smart.
You can check what arguments are passed to rg
by forcing an error:
:grep '(let\|const) render' foobar
which should show you this:
:!rg '(let|const) render' foo 2>&1| tee /var/folders/q4/8ckdmdb136z10l1nh7ss_hsw0000gn/T/vphU3gH/29
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Well done!
Without the single quotes, RipGrep wouldn't know that render
is part of the pattern so it treats it as a filename and you get errors because that filename doesn't exist.
Wrapping the pattern in single quotes killed two birds with one stone: your shell expansion issue is solved and RipGrep knows where your pattern ends.
NOTE: While it is inconsequential, here, the -e
flag is not necessary because your pattern doesn't start with a -
.