検索ヒット数をポップアップで表示する Vim プラグインを作った

公開:2020/10/21
更新:2020/10/31
3 min読了の目安(約3000字TECH技術記事

こんにちは。私はつい最近まで Vim の Vi 文字も知らなかったのですが、好奇心でちょっとさわってみたところ、どっぷりとハマってしまいました。まっさらな状態の Vim を自分色に染め上げていくのはとても楽しいです。

vimrc を書いて Vim をカスタマイズしていくうちに、 Vim script のことが少しずつわかってきました。そこで一つプラグインを作りました。ちょうど Zenn で記事を書いてみたいと思っていたところなので、これを機にご紹介させてください。

vim-hitspop

hitspop

検索コマンドのヒット数をポップアップで表示してくれるプラグインです。
たとえば /foo というコマンドを実行すると、ポップアップが現れて foo [3/7] といった検索結果を表示します。ここで 7 はトータルのヒット数を、3 は現在カーソルがどの位置にあるのかを表しています。 n n n ... とすれば foo [4/7] foo [5/7] foo [6/7] ... というふうに内容が更新されていきます。

ポップアップが表示される位置は、VSCode などのモダンな IDE を参考にして、カレントウィンドウの右上部分にしてみました。ウィンドウの右端までコードが埋まっていることは比較的少ないので、ポップアップが邪魔になることも少ない思います。

機能はたったのこれだけです。そこのあなた、ヘボいとか言わない。
まあ私としては頑張って作ったので、ぜひ使ってみてください。

内部のはなし

このプラグインは hlsearch オプションを有効にしていないと機能しません。
というのも、hlsearch によってキーワードがハイライトされているとき v:hlsearch という内部変数の値が 1 になるのですが、 この変数の値が 1 ならばポップアップを作成 or 更新する、0 ならばポップアップを削除 or なにもしない――といった処理を行っているからです。

上記の処理は自動コマンドとして登録されていて、
CursorHold,CursorMoved,CursorMovedI,WinEnter が起こるたびに呼び出されます。見てのとおりかなりの頻度で呼び出されることになるので、もしかしたら Vim の動作が遅くなるかもしれません。もしも遅くなったら次の方法を試してみてください。

  • 上記自動コマンドを調整する
  • コードを改善して pull-req する(推奨
  • :!rm -rf vim-hitspop

このような仕組みになっていますので :nohlsearch コマンドで hlsearch のハイライトを停止すればポップアップも自動的に削除されます。
なお、:nohlearch コマンドをいちいち手打ちするのはめんどうなので、次のようなキーマッピングを設定しておくとよいかもしれません。

nnoremap <silent> <ESC><ESC> :<C-u>nohlsearch<CR>

shortmess-=S

少し昔ばなしをします(とはいっても私はつい最近 Vim をさわり始めたばかりなので体験談ではないです)。
昨年の5月、 shortmess オプションに S フラグが追加されました。 set shortmess-=S とすることで [3/7] のような検索結果がコマンドラインの右端に表示されるようになりました。

この機能が追加されるまでは、検索ヒット数を動的に表示するには osyo-manga/vim-anzu といったプラグインを利用する必要があったようです。

これで便利になったかと思えたのですが、一つ問題がありました。この検索結果は、何かのコマンドで画面が再描画されると消えてしまうのです。たとえば nnzz にマップしているような人だと、 n で検索キーワードを巡回するとき zz による画面の再描画によって検索結果が一瞬で消えてしまうため使いものになりません。

そこで今年の6月に追加されたのが次の関数です。

searchcount()

この関数は直近の検索に関する情報を更新 & 取得し、辞書にして返します。

searchcount([{options}])                                        searchcount()
        最後の検索数の取得もしくは更新をする。'shortmess' で "S" 無し
        で表示されるのと同等の結果が得られる。'shortmess' で "S" あり
        の場合でも動作する。
        ...

この関数を使うことでステータスラインに検索結果を表示するといったことが可能になりました。

searchcount() の help には、これでもかというぐらい丁寧に活用方法が記述されています[1]
vim-hitspop の肝である検索結果を取得する部分では help にのっているコードを ほぼ丸パク 大いに参考にしました。

おわりに

現状ではカスタマイズ性にとぼしく、ポップアップ内に検索ワードを表示にしないようにするオプションがあるだけです。もしもバグや機能要望などがあれば issue にてご報告ください。

さいごに(hitspop とはなんの関係もありませんが)私のお気に入りの設定をおいておきます。

nnoremap <silent> - :<C-u>call <SID>ExploreHead()<CR>

function! s:ExploreHead() abort
  let l:dir = expand('%:h')
  if !isdirectory(l:dir)
    return
  endif
  exe 'edit' l:dir
endfunction

おしまい。

脚注
  1. help が丁寧なのは何もこの関数に限ったことではありません。vim の help は本当によくできています。丁寧に書かれているがゆえに膨大な量の help があるのですが、有志の方々によって継続的に日本語に翻訳されています。多謝。 ↩︎