如何在Vim中搜索已打开的缓冲区?

63

我想在vim中搜索所有当前打开的文件中的文本,并将所有结果显示在一个地方。 我猜有两个问题:

  • 我不能将打开文件列表传递给:grep/:vim,特别是那些不在磁盘上的文件的名称;
  • :grep -C 1 text的结果在快速修复窗口中看起来不好。

这里有Sublime Text 2中多文件搜索的一个不错的例子: 输入图像描述

有什么想法吗?


1
我真的很想像gvim一样突出显示搜索模式 :-) - cnicutar
2
http://vi.stackexchange.com/questions/2904/how-to-show-search-results-for-all-open-buffers - Ciro Santilli OurBigBook.com
6个回答

68

或者

:bufdo vimgrepadd threading % | copen

快速修复窗口可能看起来不太好,但比ST2的“结果面板”功能更强大,因为您可以在跳转到位置并且即使它不存在时也可以与之交互。


非常好的解决方案。我之前不知道vimgrepadd命令。然而,该命令对我来说并不能直接使用,我需要执行以下操作: :bufdo vimgrepadd pattern % 然后再执行 :copen 或 :cw,甚至更好的是 :botright cw。 - Neg_EV
1
谢谢,这差不多是我想要的。:bufdo grepadd -C 1 threading % 大致上产生了我想要的结果。通过调整vim解释结果的方式,它可能会变得更加美观可爱。顺便说一下,如果需要,你可以在ST2中轻松拆分窗口并将结果与其他文件一起显示。 - squirrel
4
通过执行 :noautocmd bufdo grepadd threading % 命令可以显著加快搜索速度。 - Mapad
4
可以使用“/threading/j”而不打开新标签页。在执行此操作之前清除快速修复列表会更好:https://dev59.com/Rl4c5IYBdhLWcg3w7t7E - Ciro Santilli OurBigBook.com
2
使用 :cexpr [] 清空您的快速修复列表,然后使用 vimgrepadd 将内容添加到列表中。 - Moberg
显示剩余2条评论

8

ackAck.vim可以很好地解决这个问题。您还可以使用:help :vimgrep。例如:

:bufdo AckAdd -n threading

将创建一个漂亮的 QuickFix 窗口,让您可以跳转到光标位置。


8
如何在不打开所有找到的缓冲区的情况下使其在分割窗口中显示结果? - ThePosey

4

像 Waz 的答案一样,我为此编写了自定义命令,并在我的 GrepCommands插件中发布。它可以在缓冲区 (:BufGrep),可见窗口 (:WinGrep),选项卡和参数上进行搜索。

(但与所有其他答案一样,它尚未处理未命名的缓冲区。)


我认为vimgrep无法搜索未命名的缓冲区是因为它没有办法在QuickFix窗口中呈现匹配项而不使用名称。例如,匹配项看起来像这样“HillarysEmails.txt | 4 col 6 | I am Russia。”如果您执行:file HillarysEmails,则可以grep未保存的缓冲区。 - FocusedWolf

3

我很喜欢romainl的答案,但是在实践中有一些棘手的问题使它使用起来很尴尬。

以下是在你的.vimrc文件中引入的用户命令Gall(Grep all),它解决了我发现的讨厌问题。

funct! GallFunction(re)
  cexpr []
  execute 'silent! noautocmd bufdo vimgrepadd /' . a:re . '/j %'
  cw  
endfunct

command! -nargs=1 Gall call GallFunction(<q-args>)

这将允许区分大小写的搜索,例如:

:Gall Error\C

并且不区分大小写:

:Gall error

并且包含空格:

:Gall fn run

优点

  • 这个命令只会打开Quickfix窗口,不会有其他作用。
  • 在每个缓冲区中使用vimgrepadd搜索结果之前,它会首先清除Quickfix窗口中的内容。
  • Quickfix窗口显示了所有打开缓冲区中的匹配位置,而不仅仅是最后一个被访问的缓冲区。
  • 可以反复使用 :Gall 命令,无需在调用之间进行任何特殊的清理工作。
  • 并不会等待错误发生,立即显示结果。
  • 不允许任何自动命令运行,加速整个操作的速度。

矛盾特点

  • 不会主动跳转到列表中的任何一项。可以使用:cn命令访问下一项,或者使用CTRL-w b <enter>直接跳转到第一项。

缺点

  • 如果只有一个匹配结果,则需要手动使用CTRL-w b <enter>进行导航。

快速导航到任意缓冲区中的搜索结果:

:[count]cn

或者
:[count]cp

例如::6cn 可以跳过列表中的前 6 个结果,并导航到“main”窗口中正确的缓冲区和行。
显然,窗口导航非常重要:
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

如果您关闭了Quickfix窗口,但需要再次查看结果,请使用以下命令:
:cw

或者

:copen

要将它恢复。


我理解你的意思,但是它为什么会将当前缓冲区更改为缓冲区列表中的最后一个缓冲区呢?有什么想法吗? - Big McLargeHuge
不,我不知道。以上内容是用vim测试的。你可能正在使用neovim - Paul Parker

2
我很久以前就写了这个函数,我猜它可能不是最简洁的解决方案,但对我很有用:
" 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')

1
下面是Waz的答案的改进版本(强化版),具有更好的缓冲区搜索和特殊情况处理:

这里可以找到一个更详细的版本,包括箭头键绑定以导航QuickFix列表和F3关闭QuickFix窗口:https://pastebin.com/5AfbY8sm (当我想弄清如何制作插件时,我会更新此答案。现在我想尽快分享它)

" 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接