Gregory Kuhn
Gregory Kuhn

Reputation: 1697

vim quoting and escaping in command mode

I'm trying to use ag (silver searcher) within Vim (https://thoughtbot.com/blog/faster-grepping-in-vim). I'm trying to be a bit more fancy and create a custom command to only search through files matching certain file extentions. I.e. use the -G regex pattern that ag supports.

This works except that trying to create a mapping for this has shown me that I don't have a good handle on quoting in this context.

The linked article shows a mapping such as this: nnoremap K :grep! "\b<C-R><C-W>\b"<CR>:cw<CR>

What I'd like is a second one which just checks the C files in a project. Something like this except that this doesn't work because I can't figure out the required quoting:

nnoremap ,k :grep! "\b<C-R><C-W>\b" . '-G \.+(h<Bar>c)$'<CR>:cw<CR>

Upvotes: 0

Views: 193

Answers (1)

romainl
romainl

Reputation: 196546

Welcome to this particularly confusing hell.

Let's start with the right command to use in the shell:

$ ag "\bfoo\b" -G '\.+(h|c)$'

Assuming we have the following line in our vimrc, as instructed in the linked post:

set grepprg=ag\ --nogroup\ --nocolor

If we try to use that -G flag as-is in :help :grep:

:grep! "\bfoo\b" -G '\.+(h|c)$'

we get this, from Vim:

:!ag --vimgrep "\bfoo\b" -G '\.+(h 2>&1| tee /var/folders/_d/bdwh8scx4hb1v2w__l0x6nk19ndqk1/T/vcaYrBc/6

this, from bash:

/bin/bash: -c: line 0: unexpected EOF while looking for matching `''
/bin/bash: -c: line 1: syntax error: unexpected end of file

and finally this, from Vim:

E426: tag not found: c)$

which tells us a) that the pattern is cut short before it reaches ag and b) that some part of the command even reaches Vim after the external command is executed, which is not desirable at this point.

The problem is the | in h|c, which is interpreted by Vim as a command separator so we send one part of the command to the shell and another part to Vim and everything is broken because both programs choke on what we send to them.

This is easy enough to fix, by escaping the bar:

:grep! "\bfoo\b" -G '\.+(h\|c)$'

And it works!

But not in a mapping:

nnoremap ,k :grep! "\b<C-r><C-w>\b" -G '\.+(h\|c)$'<CR>:cw<CR>

where we need yet another backslash:

nnoremap ,k :grep! "\b<C-r><C-w>\b" -G '\.+(h\\|c)$'<CR>:cw<CR>

because \| is expanded to | in this context so a second backslash is required to make sure we get an actual \|. See :help map-bar.

And we are done.


Note that you can do --cc as an alternative to -G '\.+(h|c)$'.

Upvotes: 1

Related Questions