Vim. 簡易プレビュー機能を vimgrep と quickfix で
vim のファイラーに勝手にプレビューを表示する機能があったら便利だと思った。いつも必要というわけではないが、時々あったらいいのにと感じる。
たとえば、マークダウンファイルのフロントマターに draft: true があるかどうかを見れば下書きで放置されていたりするページの発見、および、そこに何が書かれていたかのか確認するのに役立つ。ファイル名だけでは何が書いてあるかわからない日記のようなものの場合、 どこに何を書いているかわからなくなったときにも探しやすくて便利だ。
vim デフォルトの netrw はいちいち p
を押さないとプレビューを表示してくれない。そうじゃなくて、カーソルを移動させるたびに問答無用でドカドカとプレビューを表示してほしい。
ranger, vifm,など 有名な TUI のファイラーはちょっと設定しておけばそういうモードになって勝手に表示してくれる。しかし実際の利用目的を考えると、vim の中にいるときにそれが実現されてこそ、と思う。ただし、そのような機会はそれほど多いわけではないのでリッチなファイラープラグインを導入するのは気が引ける。
ということで、簡単に実現できそうな手段として vimgrep と quickfix の機能を使うことにする。
①: vimgrep (外部grepでも構わない)で、検索
:vimgrep 検索ワード 対象ファイル
例)
:vimgrep 'draft: true' ./**/*.md
実行すれば見つかったファイル群の1番目が表示される
これはデフォルトで使える機能。
②: quickfix の window (検索で見つかったファイルの一覧) は autocmd の設定で勝手に開くようにする
autocmd QuickFixCmdPost *grep* cwindow
ここまではよく見かける方法なので定番の設定なのだと思う。というかこれだけでやりたいことのほぼ9割は実現できてしまっている。これで十分と言えば十分だが、テキトーに操作しながらゆったりとファイルの中身を確認したいのでちょっとだけ工夫する。
この次から、ファイラーまがいの動きを実現するための設定をする。
③: ②で開いた window を左側に開くようにしておく
function! s:qf_window_settings() abort
wincmd H
vertical resize 70
endfunction
autocmd Filetype qf call s:qf_window_settings()
quickfix の window はデフォルトでは画面下半分に出てくるので、左側に開くようにしておく。右側にはファイルの中身が表示される。右側にはファイルの中身が表示される。
④: ③の 左側の window の中でカーソル移動 (j,k,gg,Gなど) と右側の表示を連動させる
function! s:cursor_move() abort
let lnum = line('.')
execute lnum . 'cc'
wincmd p
endfunction
autocmd CursorMoved * if &buftype ==# 'quickfix' | call s:cursor_move() | endif
左側の window の中でカーソル移動 (j,k,gg,Gなど) が発生するたびに、右側には対応するファイルの中身が表示されるようにする。
本来、一覧から表示するファイルを選択するときは Enter を押さないと右側の中身のほうは変化しない。また、どちらの window にいようが、 :[count]cnext
, :[count]cprevious
, :[nr]cc
などのコマンドを打てばファイルへの移動自体はできる。しかしこのコマンドの入力が数が増えてくるとなかなかめんどくさいということでキーマップが割り当てられることが多い。
キーマップよりさらにもう一段階楽をする。カーソルが移動すれば勝手に動くように autocmd を設定する。
quickfix の window はデフォルトでは編集不可能なのでそのままにしておく。そうすればカーソルが移動した先の行の番号 lnum
は、そのまま quickfix リストのN番目に対応する関係が崩れない。
一旦完成
複雑なことには対処できないので、なるべく触らなくて良いところは触らないようにしている。
結局ただのちょっとしたラッパーだ。
" vimgrep と quickfix で擬似的にファイラーのプレビューのような使い方をする
function! s:preview_disable() abort
augroup _preview
autocmd!
augroup END
endfunction
function! s:cursor_move() abort
let lnum = line('.')
execute lnum . 'cc'
wincmd p
endfunction
function! s:qf_window_settings() abort
wincmd H
vertical resize 70
endfunction
function! s:prewview_enable() abort
augroup _preview
autocmd!
autocmd Filetype qf call s:qf_window_settings()
autocmd CursorMoved * if &buftype ==# 'quickfix' | call s:cursor_move() | endif
autocmd BufWipeout, BufDelete * if &buftype ==# 'quickfix' | call s:preview_disable() | endif
augroup END
endfunction
function! s:preview(word, ...) abort
call s:preview_enable()
" 可変長引数 ... をそのままファイルパターンとして結合
let files = join(a:000, ' ')
execute 'vimgrep ' . a:word . ' ' . files
" silent execute 'grep ' . a:word . ' ' . files
cwindow
wincmd p
endfunction
command! -nargs=+ Preview call s:preview(<f-args>)
" マークダウンファイルの中で下書き状態のものを探して一覧表示、内容をプレビュー
nnoremap <F8> :Preview 'draft: true' ./**/*.md<CR>