😺

vim-ripgrepを作った話

2022/02/23に公開
1

結論

kyoh86/vim-ripgrep を作りました。

背景

使い勝手の問題

個人的に、ちょっといわゆる"プログラミング"(?)からは横にそれた作業をすることが多くなっています。

  • 運用作業
  • アーキテクチャの見直し
  • 大量データの確認
  • ...

そういう作業をしていると、 Vimmer としては Grep → Quickfix で旅をするというルーチンが頻発するわけですが、
jremmen/vim-ripgrep の初期応答の遅さが気になるようになってきました。

本家 ripgrep は、 rust 製のかなり高速な grep を実現してくれる一方、
仲介するプラグインの初期応答が遅いというのはいただけません。

さらに、最近の Quickfix は end_col (マッチの末端)も提示できるため、できればこれを活用したいところです。

開発上の問題

前々からこのプラグインのポリシーは肌に合わないと感じていたため、
CONTRIBUTING するモチベーションも持てないと思い、改めて作り直すことにしました。

  • 使われていない謎の関数があるなど、リファクタリングが要りそう
  • だが最低限のテストも CI もない (人のことを言えた立場ではないですが)
  • ドキュメントは README.md だけで doc/ はない
  • コマンドを自動で定義するが名前は :Rg とかなり短く、専有するには憚られる
    • 実際 fzf.vim と help tag の衝突を起こしている
    • ただしこれは fzf.vim の行儀が悪い
  • 'grepprg' を使っており、同期的に処理されるため、初期応答の改善は難しい

作ったものの紹介

基本的な使い方

kyoh86/vim-ripgrep は、関数 ripgrep#search({arg}) を提供します。

この関数は、シンプルに ripgrep に渡す引数を指定して、結果を Quickfix に格納する 関数です。
例えば、単語単位 (-w) で case-insensitive (-i) に foo という文字列を探したい場合は、次のように使用します。

:call ripgrep#search('-w -i foo')

コマンドその他は一切定義しません。
必要な場合は、次のようにコマンドを自分の好きな名前で定義することができます。

:command! -nargs=* -complete=file Ripgrep :call ripgrep#search(<q-args>)

定義したコマンドは次のように使用することができます。

:Ripgrep -w -i foo

発展的な使い方

今回このプラグインを開発するにあたっては、できるだけ pluggable にしておこうと考え、
ripgrep で見つけた検索結果は随時 observer に流すことができるようにしました。

ripgrep#search が Quickfix に結果を流し込む処理も、 observer の一つとして登録されているにすぎません。

そのため、必要とあらば

  • 検索中のファイル名を statusline に表示する
  • 検索中の ripgrep のエラーを何らかのファイルに書き出す

などの処理を挟むことができます。
イベントには現在のところ、

  • begin: 特定のファイルの検索を開始した
  • match: キーワードを見つけた
  • finish: すべての検索が完了した

などのイベントを設けています。

終わりに

どのくらい快適になったのか、わかりやすい例を提示してみました。
(環境に依ってはGifの再生速度が2倍近く遅くなるようです。そのうち差し替えます)

"初期応答が早い" という体験の良さの一端を感じてもらえれば幸いです。

  • vim-ripgrep : (10秒ほど待ってから Quickfix ウインドウが表示されています)
  • vim-ripgrep : (Quickfix ウインドウ内で常に G で末尾を表示しています)

Discussion