Vim/Neovimでgrep結果をいい感じにフィルタリングして一括編集する~quickfixにプラグインを添えて~
はじめに
grepした結果を一括で編集する方法はたくさんあります。
しかしgrepした結果を確認すると「編集したくない対象が紛れ込んでおり、それを除外するために正規表現を考えなおす」なんて経験はありませんか。
今回は雑にgrepした結果を直接編集して絞り込み、絞り込んだ対象を一括編集する方法を紹介します。
色々ありますが、今回はquickfixというVimの組み込み機能をプラグインで拡張して実現する方法を紹介します。
結論
Neovimの場合は以下のどちらかの組み合わせを使いましょう。
Vimの場合はvim-qfreplaceとvim-qfeditを使いましょう。
操作の全体像
操作の流れは以下です。
- grepした結果をquickfixで表示する
- quickfixの結果から除外したいものを消す
- quickfixの結果を編集して一括適用する
今回は私のZennの全ての記事の先頭に「車アイコンによる、」という文言を付けたくなったという想定で説明します。
それでは順番に見ていきましょう。
grepした結果をquickfixで表示する
様々な方法がありますが、プラグインを利用するケースと利用しないケースをひとつずつ紹介します。
 :Telescope live_grepを利用する(Neovimのみ)
Neovimユーザーに大人気のtelescope.nvimを使った方法です。
:Telescope live_grepを実行し、titleという文字列を検索するとこんな感じです。
この状態で<C-q>を押してみましょう。
画面下部にあらわれたのがquickfixです。
quickfixでEnterを押すと該当箇所にジャンプできます。
ただquickfixは編集不可なので、このままでは編集できません。
 :grepを使う
Vimには:vimgrepと外部のgrepプログラムを利用する:grepがあります。
2025年現在、grepと言えばripgrepを使いたいので、:grepを使います。
:grep title | copenを実行すると以下のようにgrepの結果が表示され、hit-enter-promptが表示されます。
ここでEnterを押すとquickfixが表示されます。
 :grepでripgrepを使う
:grepは'grepprg'の値をgrepプログラムとして実行します。
その結果を'grepformat'の値に従って解析します。
つまり'grepprg'と'grepformat'をripgrep用に設定すれば良いです。
Neovimはripgrepがインストールされていればデフォルトで'grepprg'と'grepformat'を設定します。
この時'grepprg'はrg --vimgrep -uuと設定されるため、ripgrepのオプションを変えたい場合は'grepprg'を変更しましょう。
私は-uuの部分を変えたり、--smartcaseを付けています。
Vimの場合は以下のように自分で設定する必要があります。
if executable('rg')
  let &grepprg = 'rg --vimgrep --smart-case --hidden'
  set grepformat=%f:%l:%c:%m
endif
ripgrepのオプションはお好みにあわせて編集してください。
 vim-qfreplace + vim-qfedit構成の場合
 quickfixの結果から除外したいものを消す(vim-qfedit編)
quickfixは編集不可能なので、普通は消せません。
しかしvim-qfeditを使うと不要な行を消すことができます。
さきほどの例ではpackage-lock.jsonの結果が不要なので、試しにddで一行消してみましょう。
凄い、消えてます。
それではZennの記事のtitle以外を全て消してしまいましょう。
これでgrepする正規表現などを工夫せず、Zennの記事タイトルの行だけの一覧を得ることができました。楽ちん。
 quickfixの結果を一括編集する(vim-qfreplace編)
それではZennの記事のtitleを一括で編集します。
:Qfreplaceコマンドを実行すると編集バッファが表示されます。
真ん中のバッファがqfreplaceバッファです。
ここを編集して保存してみましょう。
これをpushすれば、私の全ての記事タイトルが「車アイコンによる、」から始まります。
やりませんよ
 quicker.nvim構成の場合(Neovimのみ)
 quickfixの結果から除外したいものを消す(quicker.nvim編)
quicker.nvimはquickfixの見た目をリッチにします。また通常のバッファと同様に編集可能とし、保存時に実態へ反映させるプラグインです。
oil.nvimの作者が作っているので、思想が似ていますね。
quicker.nvimをインストールするとquickfixは以下のように表情を変えます。
ファイル名、行数、検索結果の開始位置が揃うので見やすいですね。
qfeditと同様に、不要な行を削除していきましょう。
できました。
 quickfixの結果を一括編集する(quicker.nvim編)
直接バッファを書き換えて保存します。
こちらはqfreplaceと違って直接quickfix上で編集します。保存すると適用される点は同一です。
直接編集したいか、安全に別バッファで編集したいかは好みが別れる部分です。
どちらも試してみて、お好みの方をお使いください。
 nvim-bqfでquickfixのジャンプ先のpreviewを表示する
quickfixの結果は、周辺コードと一緒に確認したいことがよくあります。
nvim-bqfを利用するとpreviewを表示できます。
インストールした様子は以下。
grep結果の妥当性確認の際、実際にジャンプせずに判断できます。
vim.diagnostic.setqflist()で診断結果をquickfixで表示した際の見通しも良くなります。
ただしデフォルトでいくつかマッピングするので注意が必要です。
特にquicker.nvimと組み合わせる場合は<C-v>の矩形選択を良く使うので、私は以下のように無効化しています。
{
  'kevinhwang91/nvim-bqf',
  ft = 'qf',
  config = function()
    require('bqf').setup {
      auto_enable = true,
      func_map = {
        vsplit = '',
      },
    }
  end,
},
気を付けること
quicker.nvimとvim-qfeditは完全に競合するため、どちらか一方をインストールしましょう。
quicker.nvimでquickfixを編集した後にvim-qfreplaceを使うとエラーが発生します。
quicker.nvimを使う場合、quickfixの編集はquicker.nvimに統一しましょう。
という事で結論に到達しました。
vim-qfeditでquickfixを編集した場合、ただちにnvim-bqfのpreviewが更新されます。
quicker.nvimでquickfixを編集した場合、バッファの保存をするまでnvim-bqfのpreviewがズレます。
quicker.nvimは通常のバッファと同様の操作感を提供し、保存操作で編集した内容を適用するという思想のためと考えられます。
nvim-bqfを併用してquickfixを編集する場合、マッピングが競合する場合は削除するか、他のキーに移動しましょう。
さいごに
昔からのVimmerにとってquickfixはお馴染みの機能ですが、最近使い始めた人にも是非触ってみて欲しいです。
LSの診断結果の閲覧についても以前からtrouble.nvimを使ってリッチに確認できました。
今ではvim.diagnostic.setqflist()とnvim-bqfの組み合わせも選択肢に入ると考えています。
専用プラグインの能力も魅力的ですが、quickfixという共通IFを活用することで、プラグイン毎に操作方法を覚える必要が無いことも、また魅力と感じています。
他にvim.lsp.buf.references()もquickfixを使います。telescope.nvimなどに置き換えている人も是非一度quickfix + nvim-bqfの使い勝手を試してみて欲しいです。
referencesは同じ一覧を使って、様々な場所にジャンプするシーンがあります。ファジーファインダーのresume機能を使うより、quickfixでジャンプする方が快適な場面もあるのでお試しあれ。



Discussion