VIM - 当前缓冲区中可视选择的VIMGREP热键

时间:2014-10-23 22:58:31

标签: regex vim vi vimgrep

如何设置热键(例如: CTRL + g )以对当前当前视觉选择执行VIMGREP操作缓冲?我的目的是在" quickfix"中显示行号列表。所有匹配搜索结果的窗口。

现在,如果我想获得正则表达式搜索的结果列表,我可以像这样执行命令模式查询:

:vimgrep /foo/ %

然而,这有两个问题:

  1. 我不想输入整个查询。我总是可以进行视觉选择,然后使用 CTRL + r CTRL + w 来粘贴当前视觉选择到命令缓冲区,但我喜欢比这更简单的东西。
  2. 上述方法要求当前缓冲区已保存到文件中。我希望能够处理我已粘贴到VIM的临时缓冲区,而不是每次我都想保存文件缓冲区。
  3. 谢谢。

3 个答案:

答案 0 :(得分:5)

低级别解决方案

尝试[I:ilist命令:

[I                 " lists every occurrence of the word under the cursor
                   " in the current buffer (and includes)

:ilist /foo<CR>    " lists every occurrence of foo in the current buffer 
                   " (and includes)

:,然后按行号,<CR>跳转到该行。

您可以使用简单的映射在视觉选择中使用它们:

xnoremap <key> "vy:<C-u>ilist /<C-r>v<CR>:

但是,您可能需要在插入时清理寄存器。

请参阅:help :ilist

另一个更低级别的解决方案

既然我们在这里,那就让我们深入挖掘,找到非常简单和优雅的东西:

:g/foo/#

您可以使用与上面:ilist相同的方式:

xnoremap <key> "vy:<C-u>g/<C-r>v/#<CR>:

限制

上面的解决方案显然不使用quickfix窗口,但它们允许您:

  • 以列表形式查看结果
  • 使用行号实际到达您想要的位置。

但他们有局限性:

  • 列表未缓存,因此如果您想要进行其他事件,则必须再次执行搜索,
  • 该列表不像quickfix列表那样是暂时的,因此您无法使用:cnext:clast等导航命令来移动结果。

更高级别的解决方案

如果这些限制是一个限制,下面的函数改编自justinmk在this /r/vim thread中的答案,为您提供了几乎完整的解决方案:

  • 正常模式下按[I,在整个缓冲区中搜索光标下的单词,
  • 正常模式下按]I,在当前行后面的光标下搜索单词,
  • visual 模式下按[I以在整个缓冲区中搜索所选文字,
  • visual 模式下按]I,在当前行后搜索所选文字。

当缓冲区与文件关联时,下面的函数使用quickfix列表/窗口,否则回退到[I]I的常规行为。它可能被修改为用作:Ilist命令的一部分。

" Show ]I and [I results in the quickfix window.
" See :help include-search.
function! Ilist_qf(selection, start_at_cursor)

    " there's a file associated with this buffer
    if len(expand('%')) > 0

        " we are working with visually selected text
        if a:selection

            " we build a clean search pattern from the visual selection
            let old_reg = @v
            normal! gv"vy
            let search_pattern = substitute(escape(@v, '\/.*$^~[]'), '\\n', '\\n', 'g')
            let @v = old_reg

            " and we redirect the output of our command for later use
            redir => output
                silent! execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' . search_pattern
            redir END

        " we are working with the word under the cursor
        else

            " we redirect the output of our command for later use
            redir => output
                silent! execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
            redir END
        endif
        let lines = split(output, '\n')

        " better safe than sorry
        if lines[0] =~ '^Error detected'
            echomsg 'Could not find "' . (a:selection ? search_pattern : expand("<cword>")) . '".'
            return
        endif

        " we retrieve the filename
        let [filename, line_info] = [lines[0], lines[1:-1]]

        " we turn the :ilist output into a quickfix dictionary
        let qf_entries = map(line_info, "{
                    \ 'filename': filename,
                    \ 'lnum': split(v:val)[1],
                    \ 'text': getline(split(v:val)[1])
                    \ }")
        call setqflist(qf_entries)

        " and we finally open the quickfix window if there's something to show
        cwindow

    " there's no file associated with this buffer
    else

        " we are working with visually selected text
        if a:selection

            " we build a clean search pattern from the visual selection
            let old_reg = @v
            normal! gv"vy
            let search_pattern = substitute(escape(@v, '\/.*$^~[]'), '\\n', '\\n', 'g')
            let @v = old_reg

            " and we try to perform the search
            try
                execute (a:start_at_cursor ? '+,$' : '') . 'ilist /' .  search_pattern . '<CR>:'
            catch
                echomsg 'Could not find "' . search_pattern . '".'
                return
            endtry

        " we are working with the word under the cursor
        else

            " we try to perform the search
            try
                execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . "I"
            catch
                echomsg 'Could not find "' . expand("<cword>") . '".'
                return
            endtry
        endif
    endif
endfunction

nnoremap <silent> [I :call Ilist_qf(0, 0)<CR>
nnoremap <silent> ]I :call Ilist_qf(0, 1)<CR>
xnoremap <silent> [I :<C-u>call Ilist_qf(1, 0)<CR>
xnoremap <silent> ]I :<C-u>call Ilist_qf(1, 1)<CR>

注意:<C-r><C-w>在光标下插入单词,而不是可视选择,遗憾的是没有这样的快捷方式。我们别无选择,只能猛拉。

答案 1 :(得分:4)

打磨暂存缓冲区

您可以使用:global命令结合:caddexpr将条目添加到当前的quickfix列表中。以下是:h :caddexpr的示例:

:g/mypattern/caddexpr expand("%") . ":" . line(".") .  ":" . getline(".")

这有一些问题:

  • 此方法每行仅匹配一个
  • 无法启动新的quickfix列表
  • 真的很长时间输入
  • 假设默认全局'errorformat'尚未更改

要克服这些问题(除了每行多个匹配项外),请在~/.vimrc文件中添加以下命令:

command! -nargs=1 -bar Cgrep
      \ let s:errorformat = &errorformat |
      \ try |
      \   let &errorformat='%f:%l:%m' |
      \   cexpr [] |
      \   execute 'g'.<q-args>.'caddexpr expand("%").":".line(".").":".getline(".")' |
      \   cc |
      \ finally |
      \   let &errorformat = s:errorformat |
      \ endtry

现在您可以使用:Cgrep/foo/来grep当前缓冲区。

视觉映射

为了使你能够做到这一点的可视化版本,你需要在所选文本中猛拉并通过:Cgrep将其传递给我们的<c-r>命令。以下是一个示例视觉映射g/来执行此操作:

xnoremap g/ y:<c-u>Cgrep/<c-r>"/<cr>

此映射也存在一些问题:

  • 此clobber未命名的注册表@"
  • 这假设视觉选择的文本将是有效的模式

以下映射通过<c-r>=使用表达式寄存器来修复映射,并且没有魔法\V

xnoremap g/ :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>Cgrep/\V<cr>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr>/<cr>

结论

就个人而言,我会放弃映射并获得一个visual star插件(那里有一些插件)。关于这个问题有一个很好的Vimcast:Search for the selected text。然后我会使用我们刚刚通过:Cgrep创建的:Cgrep//命令,或者更好的xmap g/ *:Cgrep//<cr>

如需更多帮助,请参阅:

:h :caddexpr
:h :cexpr
:h :g
:h 'efm
:h registers
:h /\V

答案 2 :(得分:1)

通过热键 CTRL + g 搜索文件* .c中的视觉选定文本:

:vmap <silent> <unique> <c-g> y:vimgrep "<c-r>"" *.c<CR>

还存在两个问题:

  1. 您想要在缓冲区中搜索,而不是在文件中搜索。
  2. 您需要quickfix中的行号。
  3. 至1: 据我所知,vimgrep只能搜索文件。解决方案是将缓冲区写入临时文件并在此文件中搜索并在不再需要时删除临时文件。此解决方案需要通过热键调用的脚本。 只是一个提示:要获得合适的文件名,可以使用VIM函数tempname()。例如:

    let s:tmpfile = tempname()
    

    (抱歉,我目前没有时间在这里显示脚本解决方案,也许我稍后再添加一个。也许其他人有更好的解决方案或者可以提供脚本?)

    到2: 此命令将启用当前缓冲区中的行号:

    :set number