Vim には 'cursorline'
という機能があります。これは名前の通り、カーソルのある行を強調し、目立たせてくれるものです。
便利な機能なのですが、常時有効にしていると以下のような問題がありました。
- 有効のまま画面をスクロールすると動作が重い。
- (カラースキームによっては)文字が背景と同化して見づらい。
そもそもこれはカーソルの位置を見失わないようにするための機能ですから、必要な時だけ有効になってくれると便利です。具体的には、以下のように切り替わって欲しいです。
- カーソルを動かすと無効にする。
- ウィンドウを開いたり、移動したりした時に有効にする。
- しばらくカーソルが止まっている時も有効にする。
……と、ここまでは以下のエントリーに書いてあることの要約です。
エントリー内のスクリプトでまさにこれが実現できるのですが、後述の理由により一つのプラグインとしてまとめました。
インストール方法などは README を読んでみてください。以下は、どうしてこのプラグインを作ったのかについて書いています。
CursorHold
イベントの功罪
従来の Vim では、他の言語にはよくある「タイマー機能」というものがありませんでした。例えば JavaScript の setTimeout()
のような、「一定時間が経ったら〇〇を実行する」ということができなかったのです。
その不便をある程度解消するために使われていたのが CursorHold
イベントです。
これはカーソルを移動させた後、'updatetime'
に指定した時間が過ぎた時に発動するイベントです。おそらくユーザーはカーソルを留めて何か考え中のはずですから、多少重たい動作をさせても構わないだろう、というわけです。このため、なるべくユーザーの操作をブロックしたくないプラグインは、みんなこの CursorHold
イベントで関数を呼び出していました。
ただこの 'updatetime'
の値は、Vim 全体で一つしか設定できません。例えば vim-gitgutterのようなプラグインではこの値を小さく(100ms)設定することが推奨されています。
しかし今回の要件のためには 100ms という時間は短すぎます。上述の thinca 氏のエントリー内のスクリプトでは CursorHold
を使っているのですが、vim-gitgutter のために、頻繁に cursorline が on/off されるのが悩みのタネでした。
CursorHold
の代わりにタイマー機能を使う
Vim8.0 ではまさにこのために、タイマー機能が実装されました。
使い方は簡単で、timer_start()
の引数に時間と関数を指定するだけです。
function! EchoHoge(timer_id) abort
echo 'hoge'endfunction" 1秒後に hoge と表示するlet timer_id = timer_start(1000,'EchoHoge')" 関数リファレンスも使えるlet timer_id = timer_start(1000,function('EchoHoge'))" Lambda も OKlet timer_id = timer_start(1000,{-> execute('echo "hoge"','')})
今回はこれを使って、以下のような処理を実装しています。
autocmd CursorMoved,CursorMovedI * calls:cursor_moved()function!s:cursor_moved() abort
" cursorline を消すsetlocal nocursorline
" すでに起動しているタイマーを止めるcall timer_stop(s:timer_id)" 新しいタイマーを起動lets:timer_id= timer_start(1000,function('s:enable_cursorline'))endfunctionfunction!s:enable_cursorline(timer_id) abort
setlocal cursorline
endfunction
基本的にはこれだけです。ただ、このままだとカーソルを動かすたびに setlocal nocursorline
していて無駄ですし、他にもいろんなエッジケースがあったのでもう少し複雑なことをやっています。詳しくはソースを読んでみてください。
まとめ
これからは CursorHold
じゃなくてタイマー機能を使おうぜ!という話でした。