Skip to content

Instantly share code, notes, and snippets.

@Konfekt
Last active April 2, 2026 20:18
Show Gist options
  • Select an option

  • Save Konfekt/d6bf2941abd810768992368b4f069fb3 to your computer and use it in GitHub Desktop.

Select an option

Save Konfekt/d6bf2941abd810768992368b4f069fb3 to your computer and use it in GitHub Desktop.
Set &grepprg to git-grep inside repo and fall back rg, ugrep or grep outside of it
" Set &grepprg to rg, ugrep, git grep or grep otherwise.
" Assumes a global ignore file `$XDG_CONFIG_HOME/grep/ignore`; create it by,
" say `touch ~/.config/grep/ignore` to ensure its existence.
augroup vimrcGrep
autocmd!
augroup END
" Scheduling niceness on Linux.
let s:scheduler = (has('unix') && executable('chrt') && executable('ionice'))
\ ? 'chrt --idle 0 ionice -c2 -n7 '
\ : ''
" stderr to null, respecting common Windows shells.
let s:nullerr = has('win32')
\ ? ((&shell =~? '\v(pwsh|powershell)') ? ' 2>$null' : ' 2> nul')
\ : ' 2> /dev/null'
" let s:xdg_config = !empty($XDG_CONFIG_HOME) ? expand($XDG_CONFIG_HOME) : expand('~/.config')
" let s:ignorefile = s:xdg_config .. '/grep/ignore'
let s:ignorefile = expand($XDG_CONFIG_HOME) .. '/grep/ignore'
if !filereadable(s:ignorefile)
call mkdir(fnamemodify(s:ignorefile, ':h'), 'p')
call writefile(['.git', 'tags', '!tags/'], s:ignorefile)
endif
let s:case_flag = &ignorecase
\ ? (&smartcase ? '--smart-case' : '--ignore-case')
\ : '--case-sensitive'
setglobal grepformat=%f:%l:%c:%m,%f:%l:%m
if executable('rg')
let &g:grepprg = s:scheduler
\ .. 'rg --multiline --follow --no-heading'
\ .. ' --ignore-file=' .. shellescape(s:ignorefile)
\ .. ' --with-filename --line-number --column'
\ .. ' ' .. s:case_flag
\ .. ' --color=never $*'
\ .. s:nullerr
elseif executable('ugrep')
" From https://github.com/Genivia/ugrep#using-ugrep-within-vim
let &g:grepprg = s:scheduler
\ .. 'ugrep -R -n --ignore-binary --ungroup --tabs=1'
\ .. ' --ignore-files=' .. shellescape(s:ignorefile)
\ .. ' --column-number'
\ .. ' ' .. s:case_flag
\ .. ' --color=never --no-messages -- $*'
\ .. s:nullerr
setglobal grepformat=%f:%l:%c:%m,%f+%l+%c+%m,%-G%f\\\|%l\\\|%c\\\|%m
elseif executable('git')
let cmd = (&ignorecase && &smartcase && executable('git-grep'))
\ ? 'git-grep'
\ : (&ignorecase ? 'git grep --ignore-case' : 'git grep')
let &g:grepprg = s:scheduler
\ .. cmd
\ .. ' --no-index --no-recurse-submodules--exclude-standard --column --line-number -I --untracked --extended-regexp --no-color $*'
\ .. s:nullerr
elseif executable('grep')
let &g:grepprg = s:scheduler
\ .. 'grep -rnHIsE ' .. (&ignorecase ? '-i ' : '')
\ .. '$* --exclude=tags --exclude-dir=.git'
\ .. s:nullerr
endif
" Vimgrep completion and jump to selected match.
if has('patch-9.1.1329')
let s:selected = ''
function! s:BuildGrepCmd(args) abort
if empty(a:args)
return ''
endif
let args = shellescape(escape(a:args, '\'))
return (&grepprg =~# '\$\*') ? substitute(&grepprg, '\$\*', args, '') : (&grepprg .. ' ' .. args)
endfunction
function! s:CompleteLiveGrep(arglead, cmdline, cursorpos) abort
let args = trim(matchstr(strpart(a:cmdline, 0, a:cursorpos), '\v^\s*Vimgrep\s+\zs.*$'))
if strlen(args) < 2
return []
endif
let cmd = s:BuildGrepCmd(args)
if empty(cmd)
return []
endif
let out = systemlist(cmd)
let l:max = &lines - &cmdheight - 1
return l:max > 0 ? out[: l:max - 1] : []
endfunction
function! s:VisitFile() abort
let item = getqflist(#{lines: [s:selected]}).items[0]
execute 'buffer' item.bufnr
call setpos('.', [0, item.lnum, item.col, 0])
call setbufvar(item.bufnr, '&buflisted', 1)
endfunction
function! s:CaptureLiveGrepSelection() abort
let info = cmdcomplete_info()
if empty(get(info, 'matches', [])) || getcmdline() !~# '^\s*Vimgrep\s'
return
endif
let s:selected = info.matches[info.selected >= 0 ? info.selected : 0]
call setcmdline(info.cmdline_orig)
endfunction
function! s:Vimgrep(args) abort
if !empty(s:selected)
call s:VisitFile()
return
endif
let cmd = s:BuildGrepCmd(a:args)
if empty(cmd)
return
endif
let out = systemlist(cmd)
if !empty(out)
let s:selected = out[0]
call s:VisitFile()
endif
endfunction
command! -nargs=+ -complete=customlist,<SID>CompleteLiveGrep Vimgrep call <SID>Vimgrep(<q-args>)
nnoremap q<space> :<c-u>Vimgrep<space>
autocmd vimrcGrep CmdlineEnter : let s:selected = ''
" Store selected completion item on leaving the command-line and restore the typed command.
autocmd vimrcGrep CmdlineLeavePre : call <SID>CaptureLiveGrepSelection()
" Trigger completion automatically for :Vimgrep once typing starts.
if has('patch-9.0.1576')
augroup vimrcWildTriggerGrep
autocmd!
augroup END
function! s:DisableWildTrigger() abort
unlet! s:wildtrigger_active
let &wildmode = s:wildmode_saved
augroup vimrcWildTriggerGrep
autocmd!
augroup END
endfunction
function! s:EnableWildTrigger() abort
let s:wildmode_saved = &wildmode
set wildmode=noselect,full
let s:wildtrigger_active = 1
augroup vimrcWildTriggerGrep
autocmd!
autocmd CmdlineChanged : call wildtrigger()
autocmd CmdlineLeavePre : ++once call <SID>DisableWildTrigger()
augroup END
endfunction
autocmd vimrcGrep CmdlineChanged :
\ if getcmdline() =~# '\v^\s*Vimgrep\s\S' && !exists('s:wildtrigger_active') |
\ call <SID>EnableWildTrigger() |
\ endif
endif
endif
@Konfekt

Konfekt commented Sep 17, 2025

Copy link
Copy Markdown
Author

@Konfekt

Konfekt commented Sep 17, 2025

Copy link
Copy Markdown
Author

See this perl script gist for smart-case capable git-grep

@Konfekt

Konfekt commented Feb 21, 2026

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment