Last active
September 27, 2025 19:26
-
-
Save Konfekt/d6bf2941abd810768992368b4f069fb3 to your computer and use it in GitHub Desktop.
Set &grepprg / &findfunc to git-grep / git-ls-files inside repo and fall back rg, ugrep or grep / fd, ... outside of it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
" Set &grepprg to git-grep inside repo, fall back rg, ugrep 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 vimrcFindGrep | |
autocmd! | |
augroup END | |
if has('unix') && executable('chrt') && executable('ionice') | |
let s:scheduler = 'chrt --idle 0 ionice -c2 -n7 ' | |
" " hard getting escaping right | |
" elseif has('win32') | |
" let s:scheduler = (&shell =~? '\v(^|\\)cmd\.exe$' ? '' : 'cmd.exe ') .. 'start /B /LOW ' | |
else | |
let s:scheduler = '' | |
endif | |
let s:nul = ' 2> ' .. (has('win32') ? 'nul' : '/dev/null') | |
let s:smartcase = &ignorecase && &smartcase ? '--smart-case' : (&ignorecase ? '--ignore-case' : '--case-sensitive') | |
set grepformat=%f:%l:%c:%m,%f:%l:%m | |
if executable('rg') | |
let &g:grepprg = s:scheduler .. 'rg --multiline --follow --no-heading --ignore-file=' .. expand('$XDG_CONFIG_HOME/grep/ignore') .. ' --with-filename --line-number --column ' .. s:smartcase .. ' --color never $*' .. s:nul | |
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=' .. expand('$XDG_CONFIG_HOME/grep/ignore') .. ' --column-number ' .. s:smartcase .. ' --color=never --no-messages -- $*' .. s:nul | |
setglobal grepformat=%f:%l:%c:%m,%f+%l+%c+%m,%-G%f\\\|%l\\\|%c\\\|%m | |
elseif executable('grep') | |
let &g:grepprg = s:scheduler .. 'grep -rnHIsE $* --exclude=tags --exclude-dir=.git' .. s:nul | |
endif | |
if has('patch-9.1.0810') " = if exists('&findfunc') | |
if executable('fd') | |
let g:findcmd = 'fd --type file --full-path --ignore-file='..expand('$XDG_CONFIG_HOME/grep/ignore')..' --color never '..(has('win32') ? '--fixed-strings ' : '')..' ""' | |
elseif executable('rg') | |
let g:findcmd = 'rg --files --hidden --ignore-file='..expand('$XDG_CONFIG_HOME/grep/ignore')..' --no-ignore --color never --glob ""' | |
elseif executable('ugrep') | |
let g:findcmd = 'ugrep -Rl -I --ignore-files='..expand('$XDG_CONFIG_HOME/grep/ignore')..' --color=never -3 ""' | |
else | |
let g:findcmd = '' | |
" if has('win32') | |
" let g:findcmd = 'dir . /s/b/a:-d-h' | |
" elseif has('unix') | |
" let g:findcmd = 'find . -path "*/.git" -prune -o -type f -print' | |
" endif | |
endif | |
" See :help live-grep | |
func Find(arg, _) | |
if empty(s:filescache) | |
if empty(g:findcmd) | |
let s:filescache = globpath('.', '**', 1, 1) | |
call filter(s:filescache, '!isdirectory(v:val)') | |
call map(s:filescache, "fnamemodify(v:val, ':.')") | |
else | |
let s:filescache = systemlist(s:scheduler .. g:findcmd .. s:nul) | |
endif | |
endif | |
return empty(a:arg) ? s:filescache : matchfuzzy(s:filescache, a:arg) | |
endfunc | |
autocmd vimrcFindGrep CmdlineEnter : let s:filescache = [] | |
set findfunc=Find | |
endif | |
if executable('git') | |
function! s:outside_repo() | |
if exists('*FugitiveGitDir') | |
return empty(FugitiveGitDir()) | |
else | |
" let outside_repo = empty(finddir('.git', getcwd().';')) && empty(findfile('.git', getcwd().';')) | |
silent let repo = system('git rev-parse --is-inside-work-tree' .. s:nul .. ' 1>&2') | |
return v:shell_error != 0 | |
endif | |
endfunction | |
let s:grepprg = &g:grepprg | |
let s:grepformat = &g:grepformat | |
let s:git_grep = &ignorecase && &smartcase && executable('git-grep') ? | |
\ 'git-grep' : (&ignorecase ? 'git grep --ignore-case' : 'git grep') | |
let s:git_grep = s:scheduler .. s:git_grep .. ' --exclude-standard --column --line-number -I --untracked --extended-regexp --no-color $*' .. s:nul | |
function! s:SetGrepPrg() | |
if s:outside_repo() | |
let &g:grepformat= s:grepformat | |
let &g:grepprg = s:grepprg | |
else | |
setglobal grepformat=%f:%l:%c:%m | |
let &g:grepprg = s:git_grep | |
endif | |
endfunction | |
autocmd vimrcFindGrep VimEnter,DirChanged * call <SID>SetGrepPrg() | |
if has('patch-9.1.0810') " = if exists('&findfunc') | |
let s:findcmd = g:findcmd | |
let s:git_ls = s:scheduler .. 'git ls-files . --exclude-from=' .. expand('$XDG_CONFIG_HOME/grep/ignore') .. ' --exclude-standard --cached --others' .. s:nul | |
function! s:SetFindCmd() | |
let g:findcmd = s:outside_repo() ? s:findcmd : s:git_ls | |
endfunction | |
autocmd vimrcFindGrep VimEnter,DirChanged * call <SID>SetFindCmd() | |
endif | |
endif | |
if has('patch-9.1.1329') " = if exists('##CmdlineLeavePre') | |
" See :help live-grep | |
" Consider an Alias such as :Alias l LiveGrep | |
command! -nargs=+ -complete=customlist,<SID>Grep LiveGrep call <SID>VisitFile() | |
func s:Grep(arglead, cmdline, cursorpos) | |
if match(&grepprg, '\$\*') == -1 | let &grepprg .= ' $*' | endif | |
let cmd = substitute(&grepprg, '\$\*', shellescape(escape(a:arglead, '\')), '') | |
return len(a:arglead) > 1 ? systemlist(cmd) : [] | |
endfunc | |
func s:VisitFile() | |
let item = getqflist(#{lines: [s:selected]}).items[0] | |
let pos = '[0,\ item.lnum,\ item.col,\ 0]' | |
exe $':b +call\ setpos(".",\ {pos}) {item.bufnr}' | |
call setbufvar(item.bufnr, '&buflisted', 1) | |
endfunc | |
" Automatically select the first item in the completion list when leaving the | |
" command-line, and for `:Grep`, add the typed pattern to the command-line history: | |
autocmd vimrcFindGrep CmdlineLeavePre : | |
\ if get(cmdcomplete_info(), 'matches', []) != [] | | |
\ let s:info = cmdcomplete_info() | | |
\ if getcmdline() =~ '^\s*fin\%[d]\s' && s:info.selected == -1 | | |
\ call setcmdline($'find {s:info.matches[0]}') | | |
\ endif | | |
\ if getcmdline() =~# '^\s*LiveGrep\s' | | |
\ let s:selected = s:info.selected != -1 | |
\ ? s:info.matches[s:info.selected] : s:info.matches[0] | | |
\ call setcmdline(s:info.cmdline_orig) | | |
\ endif | | |
\ endif | |
" autocomplete matches for :find and :LiveGrep | |
if has('patch-9.0.1576') " = if exists('*wildtrigger') | |
augroup vimrcWildTrigger | |
autocmd! | |
augroup END | |
autocmd vimrcFindGrep CmdlineChanged : | |
\ if getcmdline() =~# '\v^\s*%(fin%[d]|LiveGrep)\s\S' && !exists('s:wildtrigger') | call s:wildtrigger() | endif | |
function! s:wildtrigger() | |
let s:wildmode = &wildmode | |
set wildmode=noselect,full | |
autocmd vimrcWildTrigger CmdlineChanged : call wildtrigger() | |
let s:wildtrigger = 1 | |
autocmd vimrcWildTrigger CmdlineLeavePre : ++once | |
\ unlet! s:wildtrigger | let &wildmode = s:wildmode | | |
\ autocmd! vimrcWildTrigger CmdlineChanged : | |
endfunction | |
endif | |
endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See also https://gist.github.com/andlrc/c8e1a3b9c1ec5c761111ea0e49bda6c4