I\'d like to search for text in all files currently open in vim and display all results in a single place. There are two problems, I guess:
Like the answer of Waz, I have written custom commands for that, published in my GrepCommands plugin. It allows to search over buffers (:BufGrep
), visible windows (:WinGrep
), tabs, and arguments.
(But like all the other answers, it doesn't handle unnamed buffers yet.)
Or
:bufdo vimgrepadd threading % | copen
The quickfix window may not look good for you but it's a hell of a lot more functional than ST2's "results panel" if only because you can keep it open and visible while jumping to locations and interact with it if it's not there.
ack and Ack.vim handle this problem beautifully. You can also use :help :vimgrep
. For example:
:bufdo AckAdd -n threading
will create a nice quickfix window that lets you hop to the cursor position.
An improved (on steroids) version of Waz's answer, with better buffer searching and special case handling, can be found below (The moderators wouldn't let me update Waz's answer anymore :D). A more fleshed out version with binds for arrow keys to navigate the QuickFix list and F3 to close the QuickFix window can be found here: https://pastebin.com/5AfbY8sm (When i feel like figuring out how to make a plugin i'll update this answer. I wanted to expedite sharing it for now)
" Looks for a pattern in the buffers.
" Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]
" If pattern is not specified then usage instructions will get printed.
" If matchCase = '1' then exclude matches that do not have the same case. If matchCase = '0' then ignore case.
" If prefix == 'c' then put results in the QuickFix list. If prefix == 'l' then put results in the location list for the current window.
function! s:GrepBuffers(...)
if a:0 > 4
throw "Too many arguments"
endif
if a:0 >= 1
let l:pattern = a:1
else
echo 'Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]'
return
endif
let l:matchCase = 0
if a:0 >= 2
if a:2 !~ '^\d\+$' || a:2 > 1 || a:2 < 0
throw "ArgumentException: matchCase value '" . a:2 . "' is not in the bounds [0,1]."
endif
let l:matchCase = a:2
endif
let l:matchWholeWord = 0
if a:0 >= 3
if a:3 !~ '^\d\+$' || a:3 > 1 || a:3 < 0
throw "ArgumentException: matchWholeWord value '" . a:3 . "' is not in the bounds [0,1]."
endif
let l:matchWholeWord = a:3
endif
let l:prefix = 'c'
if a:0 >= 4
if a:4 != 'c' && a:4 != 'l'
throw "ArgumentException: prefix value '" . a:4 . "' is not 'c' or 'l'."
endif
let l:prefix = a:4
endif
let ignorecase = &ignorecase
let &ignorecase = l:matchCase == 0
try
if l:prefix == 'c'
let l:vimgrep = 'vimgrep'
elseif l:prefix == 'l'
let l:vimgrep = 'lvimgrep'
endif
if l:matchWholeWord
let l:pattern = '\<' . l:pattern . '\>'
endif
let str = 'silent ' . l:vimgrep . ' /' . l:pattern . '/'
for buf in getbufinfo()
if buflisted(buf.bufnr) " Skips unlisted buffers because they are not used for normal editing
if !bufexists(buf.bufnr)
throw 'Buffer does not exist: "' . buf.bufnr . '"'
elseif empty(bufname(buf.bufnr)) && getbufvar(buf.bufnr, '&buftype') != 'quickfix'
if len(getbufline(buf.bufnr, '2')) != 0 || strlen(getbufline(buf.bufnr, '1')[0]) != 0
echohl warningmsg | echomsg 'Skipping unnamed buffer: [' . buf.bufnr . ']' | echohl normal
endif
else
let str = str . ' ' . fnameescape(bufname(buf.bufnr))
endif
endif
endfor
try
execute str
catch /^Vim\%((\a\+)\)\=:E\%(683\|480\):/ "E683: File name missing or invalid pattern --- E480: No match:
" How do you want to handle this exception?
echoerr v:exception
return
endtry
execute l:prefix . 'window'
"catch /.*/
finally
let &ignorecase = ignorecase
endtry
endfunction
I really liked romainl's answer, but there were a few sticky edges that made it awkward to use in practice.
The following in your .vimrc file introduces a user command Gall
(Grep all) that addresses the issues that I found irksome.
funct! GallFunction(re)
cexpr []
execute 'silent! noautocmd bufdo vimgrepadd /' . a:re . '/j %'
cw
endfunct
command! -nargs=1 Gall call GallFunction(<q-args>)
This will allow case-sensitive searches like this:
:Gall Error\C
and case-insensitive:
:Gall error
and with spaces:
:Gall fn run
vimgrepadd
-ing results from each buffer.:cn
gets second result or CTRL-w b <enter>
to get to the first result directly.CTRL-w b <enter>
.To navigate to a result in any buffer quickly:
:[count]cn
or
:[count]cp
E.g. :6cn
to skip 6 results down the list, and navigate to the correct buffer and line in the "main" window.
Obviously, window navigation is essential:
Ctrl-w w "next window (you'll need this at a bare minimum)
Ctrl-w t Ctrl-w o "go to the top window then close everything else
Ctrl-w c "close the current window, i.e. usually the Quickfix window
:ccl "close Quickfix window
If you close the Quickfix window, then need the results again, just use:
:cw
or
:copen
to get it back.
I made this function a long time ago, and I'm guessing it's probably not the cleanest of solutions, but it has been useful for me:
" Looks for a pattern in the open buffers.
" If list == 'c' then put results in the quickfix list.
" If list == 'l' then put results in the location list.
function! GrepBuffers(pattern, list)
let str = ''
if (a:list == 'l')
let str = 'l'
endif
let str = str . 'vimgrep /' . a:pattern . '/'
for i in range(1, bufnr('$'))
let str = str . ' ' . fnameescape(bufname(i))
endfor
execute str
execute a:list . 'w'
endfunction
" :GrepBuffers('pattern') puts results into the quickfix list
command! -nargs=1 GrepBuffers call GrepBuffers(<args>, 'c')
" :GrepBuffersL('pattern') puts results into the location list
command! -nargs=1 GrepBuffersL call GrepBuffers(<args>, 'l')