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.
    – MaikoID
    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"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.