🔍

Neovimで表示範囲の文字列だけを検索するプラグインを作った

2021/03/31に公開

表示範囲の文字列だけを検索するバッファを提供するプラグインを作った。

https://github.com/notomo/reacher.nvim

ウィンドウ内の今表示されている文字列だけを検索できる。
concealやfoldなどにも対応しているので、今見えていない文字を意識する必要がない。

モチベーション

今見ている位置に移動したい場合vim-easymotionが便利。
(最近だとhop.nvimもある)

しかし最近目が悪くなったのか、集中力がないのか、
表示されるヒントを識別してからキーを押すのが億劫になってしまった。
つまり最速で移動するのを諦めてでも、集中せずに画面をぼんやり見て移動したくなった。

検索(/?)を移動に使う場合、移動したいと思ったときには移動先の文字列を知っている。
ただ、移動したい位置が上か下かで使い分けるのが面倒なのと、
入力ミス時に今表示されている範囲の外にマッチして本来の位置を見失うのが嬉しくない。
そして、複数の候補にマッチする場合に本来移動したい位置に移動するのが大変。
(<C-t>, <C-g>を押しやすい1文字にマッピングするのは現実的じゃない)

なので思考停止できるようにプラグインを作った。

複数の候補にマッチする場合の対策は?

入力欄はコマンドラインじゃなくてただのバッファなので、モードごとにキーマッピングできる。
reacherファイルタイプに対するデフォルトキーマッピングとして以下を用意している。

nnoremap <buffer> j <Cmd>lua require("reacher").next()<CR>
nnoremap <buffer> k <Cmd>lua require("reacher").previous()<CR>
nnoremap <buffer> gg <Cmd>lua require("reacher").first()<CR>
nnoremap <buffer> G <Cmd>lua require("reacher").last()<CR>

移動先の文字列を入力して、移動先の選択が必要ならノーマルモードになって、上記のノーマルモードライクなキーマッピングで移動できる。
何なら今見てる場所のハイライトが変わるまでjを押していればたどり着ける。
その間文字列を認識している必要がなくて楽。

side_first(), side_last()など列に着目して移動する関数も提供しているので、
例えば表示列が一番最後のマッチへの移動も可能。
(gifで最後に使っているのはside_next())

実装しての感想

そもそも\%l, \%c, \%v辺りを使えばで行や列の範囲を絞った検索はできるが、
concealやfold内の行を無視して今見えてる文字列そのままを検索したいとなると途端に難しい。

今表示されている範囲の文字列を取得するだけでも結構大変。
conceal, fold, diff, tab文字, wrapなど様々な対策が必要になった。
オプションを可能な限り洗い出して対策してるが、たぶん漏れはあるし壊れやすい実装になっている。

それでも表示周りのオプション関数について知らなかったことを山程知れたのと、
Neovimのバグ?も見つかったのでヨシ。
💭 このissueは今のところ自力で直せる気がしていない。

Discussion