How do I search the open buffers in Vim?

SearchVim

Search Problem Overview


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:

  • I can't pass the list of open files to :grep/:vim, especially the names of files that aren't on the disk;
  • The result of :grep -C 1 text doesn't look good in the quickfix window.

Here is a nice example of multiple file search in Sublime Text 2:enter image description here

Any ideas?

Search Solutions


Solution 1 - Search

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.

Solution 2 - Search

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.

Solution 3 - Search

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.)

Solution 4 - Search

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
Pros
  • It will only open the Quickfix window, nothing else.
  • It will clear the Quickfix window first before vimgrepadd-ing results from each buffer.
  • The Quickfix window will contain the locations of all matches throughout the open buffers, not just the last visited.
  • Use :Gall repeatedly without any special housekeeping between calls.
  • Doesn't wait on errors and displays results immediately.
  • Doesn't allow any autocmd to run, speeding up the overall operation.
Ambivalent features
  • Doesn't preemptively jump to any occurrence in the list. :cn gets second result or CTRL-w b <enter> to get to the first result directly.
Cons
  • If there's only one result, you'll have to navigate to it manually with 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.

Solution 5 - Search

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')

Solution 6 - Search

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsquirrelView Question on Stackoverflow
Solution 1 - SearchromainlView Answer on Stackoverflow
Solution 2 - SearchConnerView Answer on Stackoverflow
Solution 3 - SearchIngo KarkatView Answer on Stackoverflow
Solution 4 - SearchPaul ParkerView Answer on Stackoverflow
Solution 5 - SearchWazView Answer on Stackoverflow
Solution 6 - SearchFocusedWolfView Answer on Stackoverflow