跳过正文
  1. 文章/

在 Vim 中搜索

·372 字·2 分钟
作者
Yang Hu

搜索是开发中最常用也是最重要的操作之一。Vim 提供了非常高效和方便的搜索功能。 这篇笔记记录了一些常用的搜索命令、例子以及自定义的 key mapping。

  • 使用 ripgrep 搜索文件内容(:Rg2,gg,gw/,gW
  • 使用 git-grep 搜索 git branch/commit,以及使用 fzf 显示 git grep 的结果(:Ggrep
  • 使用 fzf/coc list 搜索当前文件/buffers 中的行,实现快速定位/跳转
  • 使用 quickfix lists 快速访问上述搜索的结果,以及利用 :cdo/:cfdo 等命令对结果进行批量操作

在文件中通用搜索
#

Ripgrep 与 fzf
#

fzf.vim 提供了 :Rg 命令来调用 ripgrep 搜索当前目录。不过,我构建了一个更实用的变体,支持指定路径并向 ripgrep 传递其他参数,映射到快捷键 <Leader>gg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
" https://github.com/junegunn/fzf.vim/issues/837#issuecomment-1179386300
" Search with ripgrep and fzf with folder specs. Defult starts in git root folder
" Examples:
"   :Rg2 -w "apple" ./folder_test
"   :Rg2 -w "apple" -g "!themes/*" -g "*.css"
"   :Rg2 --type=js "apple"
"   :Rg2 --fixed-strings "apple"
"   :Rg2 -e -foo
command! -bang -nargs=* Rg2
  \ call fzf#vim#grep(
  \ "rg --column --line-number --no-heading --color=always --smart-case ".<q-args>,
  \ 1, {'dir': system('git -C '.expand('%:p:h').' rev-parse --show-toplevel 2> /dev/null')[:-2]},
  \ <bang>0)

Ripgrep 搜索选项:

  • -w:按单词搜索
  • -g:glob,用于包含或排除文件/目录(在模式前加 ! 表示排除)
  • --type:按文件类型过滤。示例:--type=js

结果通过 fzf 展示,可以进行模糊搜索:

  • "!pattern":排除包含该模式的结果
  • "pattern":模糊匹配,只包含含有该模式的结果

tab 选择结果,或用 c-a 全选,然后按 enter 打开,或用 c-q 构建 quickfix 列表。

技巧:

  • 使用 <C-r><C-w> 插入光标下的 word,或用 <C-r><C-a> 插入 WORD。在 tmux 中,需要按两次 c-a

在当前文件中搜索
#

Fzf 提供了 :Lines(所有打开缓冲区)和 :BLines(当前缓冲区)命令进行行过滤。输入要搜索的词,按 enter 快速跳转到对应行。

也可以从结果中构建 quickfix 列表:先用 <C-a> 全选,再用 <C-q> 构建。

自定义快捷键:

  • <Leader>l*:搜索所有打开的缓冲区
  • <Leader>/:搜索当前缓冲区(类似 /,但使用 fzf)
  • <Leader>lw:在所有打开的缓冲区中搜索光标下的当前单词

CocSearch
#

  • 优点: 使用直观,结果以格式良好的缓冲区窗口展示
  • 缺点: 未与 quickfix 列表集成,无法像 fzf 那样轻松过滤结果

ripgrep 参数(在 CocSearch 命令中用 -A 添加):

  • -g {GLOB}:包含或排除匹配 glob 的文件/目录(! 表示排除)
  • -w --word-regexp:只显示被单词边界包围的匹配项
  • -x --line-regexp:只显示被行边界包围的匹配项

示例:搜索一个字符串,但忽略 keyboards/ 目录并排除 .mk 文件:

1
:CocSearch STM32F411 ../../.. -g !keyboards/ -g !*.mk

搜索 Git 仓库
#

使用 Fugitive 的原生 Ggrep
#

fugitive.vim 提供了在 VIM 中使用 git grep 的集成,结果直接发送到 quickfix 列表。

1
2
3
4
5
:Ggrep "ripgrep" HEAD~
:Ggrep "ripgrep" master
:Ggrep -w "coc" HEAD~ -- "*.md"
:Ggrep --cached findme -- "*.[ch]"
:Ggrep --untracked findme

更多详情见下方关于 quickfix 列表的章节

使用 fzf 查看结果
#

注意:如果搜索的是不同分支或提交,FZF 的预览无法显示该文件,此时使用 fugitiveGgrep 更有用。

1
2
3
4
command! -bang -nargs=* GGrep
  \ call fzf#vim#grep(
  \   'git grep --line-number -- '.fzf#shellescape(<q-args>),
  \   fzf#vim#with_preview({'dir': systemlist('git rev-parse --show-toplevel')[0]}), <bang>0)

在 quickfix 列表中处理搜索结果
#

搜索结果保存在 quickfix 列表中,可用 :cw 打开,通过 ]q/[q 浏览(使用 vim-unimparied 插件映射),或打开 quickfix 列表后用 j/k 浏览。也可以使用 coc-list 通过自定义映射 <leader>lq 来浏览/搜索 quickfix 列表。

当你对一个搜索完成后又需要做另一个搜索时,可以用 :col[der] 返回上一个 quickfix 列表,继续处理之前的结果。

相关资源:

  • :h quickfix
  • :h cdo
  • :h cfdo
  • :h ccl - 关闭 quickfix 窗口
  • :h col[der] - 转到更旧的 quickfix 列表
  • :h cnew - 转到更新的 quickfix 列表
  • :h chi - 显示 quickfix 列表历史(最多 10 条)

快捷键映射
#

  • <Leader>gg:在普通模式下进入 :Rg2 等待输入;在可视模式下以可视选区作为参数填入
  • <Leader>g{w|W}:搜索光标下的 word/WORD

结合快捷键 %%(展开为当前文件所在目录),可以轻松开始搜索。