16

I am trying to change the default grep call to avoid unnecessary plugin installation. What I want from vim is to call the external search using the builtin command :grep with custom arguments, this is what I've tried putting in my rc:

set wildignore=*.o,*.obj,*~,*.pyc,.git/**,tags,cscope* let &grepprg='grep -n -R --exclude=' . &wildignore . ' $*' 

usage:

:set verbose grepprg verbose=0 grepprg=grep -n -R --exclude=*.o,*.obj,*~,*.pyc,.git/**,tags,cscope* $* :lgrep "_cast" ./src/* 

output:

:!grep -n -R --exclude=*.o,*.obj,*~,*.pyc,.git/**,tags,cscope* "_cast" ./src/* 2>&1| tee /tmp/nvimzj3oo1/225 zsh:1: no matches found: --exclude=*.o,*.obj,*~,*.pyc,.git/**,tags,cscope* 

if I execute what vim tried:

$ grep -n -R --exclude=*.o,*.obj,*~,*.pyc,.git/**,tags,cscope* "_cast" ./src/* 2>&1| tee /tmp/nvimzj3oo1/225 ./src/daemon/lim_l3_server_mw.cc:160: LimL3Server *self = static_cast<LimL3Server *>(st); 

so, I'm falling to see the problem, any ideas?

0

3 Answers 3

21

If any of the globs has no match, in zsh, the command will be aborted. This means you need to escape your globs.

I suggest you use shellescape() to build your 'grepprg' command.

let &grepprg='grep -n -R --exclude=' . shellescape(&wildignore) . ' $*' 

For more help see:

:h 'grepprg' :h shellescape() man zshexpn 

Alternatives to grep

Have you thought about using a different program for 'grepprg' like ag, the silver searcher, git grep, ack, or ripgrep?

Ag, The Silver Searcher

Ag by default ignores the following files:

  • Ignores binary files e.g. *.o and *.pyc
  • Ignores version control directories e.g. .git, .hg, and .svn
  • Will ignore files that matched your .gitignore files. I imagine your tags and cscope file will be ignored by this
  • Uses PRCE regular expressions

This completely or nearly completely eliminates your excludes.

set grepprg=ag\ --vimgrep\ $* set grepformat=%f:%l:%c:%m 

If vanilla grep settings aren't your thing then you can use Ack.vim which works with ag despite its name.

Using Ripgrep:

Ripgrep is similar to Ag, the silver searcher.

  • Ignores binary files e.g. *.o and *.pyc
  • Ignores version control directories e.g. .git, .hg, and .svn
  • Will ignore files that matched your .gitignore files. I imagine your tags and cscope file will be ignored by this
  • Uses Rust's regular expressions
  • Super fast! Ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}

Setting for you vimrc:

set grepprg=rg\ --vimgrep set grepformat=%f:%l:%c:%m 

Using git grep or :Ggrep

If you are using git and fugitive.vim's :Ggrep which uses git grep. git grep can be a wonderful option because it will by default only search inside of tracked files. Therefore sidestepping the need to add ignores.

Using git grep without fugitive.vim:

set grepprg=git\ --no-pager\ grep\ --no-color\ -n\ $* set grepformat=%f:%l:%m,%m\ %f\ match%ts,%f 

Note: this uses the current working directory as a starting point

Using ack

Ack is a tool like grep, optimized for programmers. Ack is a perl script that can be easier to install on locked down system. It is the forerunner to both Ag and Ripgrep.

  • Ignore version control directories by default
  • Ignore backup files and core dumps by default
  • Can use ackrc file to ignore more files by default
  • Ack 1.* series ignores binary files by default. 2.* does not.
  • Uses Perl's regular expressions
  • Often faster than normal grep because it searches less files by default

:grep settings:

set grepprg=ack\ -s\ -H\ --nopager\ --nocolor\ --nogroup\ --column set grepformat=%f:%l:%c:%m,%f:%l:%m 

If vanilla grep settings aren't your thing then you can use Ack.vim.

4
  • thanks for you answer. I'm don't like to use a external plugin if I have the grep right at my finger tips. lgrep is really great. I found a strange behavior tought, if I don't feed the directories arguments it ignores my exclude pattern, again, I can't reproduce copying and paste on zsh. Commented Jul 21, 2016 at 17:33
  • Thank you for documenting many possible solutions. This answer is extremely helpful and well researched. Can you please explain where the different grepformat option values come from? I recently ran into problems with ,%f in the grepformat string associated with git grep. Commented Oct 19, 2019 at 21:17
  • 'grepformat' uses the same format as 'errorformat'. See :h errorformat for more details. The ,%f is a fallback which will recognize filenames in the case the format doesn't match f:%l:%m or %m\ %f\ match%ts beforehand. It is likely the first format will be the one used %f:%l:%m. Commented Oct 23, 2019 at 14:13
  • vim-ripgrep is a plugin for ripgrep. Commented Dec 25, 2019 at 22:04
2

Sharing my answer adopted from other answers (works and tested on macOSX):

" Grep {{{ " This is only availale in the quickfix window, owing to the filetype " restriction on the autocmd (see below). function! <SID>OpenQuickfix(new_split_cmd) " 1. the current line is the result idx as we are in the quickfix let l:qf_idx = line('.') " 2. jump to the previous window wincmd p " 3. switch to a new split (the new_split_cmd will be 'vnew' or 'split') execute a:new_split_cmd " 4. open the 'current' item of the quickfix list in the newly created buffer " (the current means, the one focused before switching to the new buffer) execute l:qf_idx . 'cc' endfunction augroup grep_augroup autocmd! autocmd QuickFixCmdPost [^l]* copen autocmd QuickFixCmdPost l* lopen autocmd FileType qf nnoremap <buffer> <C-v> :call <SID>OpenQuickfix("vnew")<CR> autocmd FileType qf nnoremap <buffer> <C-x> :call <SID>OpenQuickfix("split")<CR> augroup END " Set grepprg as RipGrep or ag (the_silver_searcher), fallback to grep if executable("rg") set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case set grepformat=%f:%l:%c:%m,%f:%l:%m elseif executable("ag") set grepprg=ag\ --vimgrep\ $* set grepformat=%f:%l:%c:%m else let &grepprg='grep -n -r --exclude=' . shellescape(&wildignore) . ' $* .' endif function s:RipGrepCWORD(bang, ...) abort let search_word = a:1 if search_word == "" let search_word = expand("<cword>") endif echom "Searching for " . search_word " Silent removes the "press enter to continue" prompt, and band (!) is for " not jumping to the first result execute "silent grep" . a:bang ." " . search_word endfunction command! -bang -nargs=? RipGrepCWORD call <SID>RipGrepCWORD("<bang>", "<args>") nnoremap <c-f> :RipGrepCWORD!<Space> " }}} 

It includes:

Command:

:RipGrepCWORD[!] - Search for word, if string specified, search for it, if blank, search for word under cursor. Opens a quickfix list, removing the bang (!) will open the first result automatically.

Mappings:

CTRLf - populates EX-command with :RipGrepCWORD!

On the quickfix:

CTRLv - Opens result in a vsplit window

CTRLx - Opens result in a split window

0

I couldn't really use the proposed solution because shellescape returns a string with simple quotes ' so I couldn't use it inside the --exclude grep opt.

This is what I worked:

let &grepprg="grep -n -r --exclude=\\*.{o,obj,~,pyc,git}\\* --exclude={tags,cscope}\\* $* /dev/null" 

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.