Vimの<C-f>でスクロールしていくと最後一行になってしまうのを直す設定

Vim<C-f>を押していくと、最後に一行だけになってしまいます。 おっと行き過ぎたと言ってわざわざ戻っている方も多いのではないでしょうか。 f:id:itchyny:20160202225503g:plain この世の中にはたくさんスクロールのUIがありますが、普通はこうなっていません。 ウェブブラウザーのスクロールも、PDFビューワのスクロールも、lessコマンドのスクロールも、ページの一番下が画面の下に見えたら止まってくれます。

この挙動は、<C-f>を次のようにマッピングすると直ります。

noremap <expr> <C-f> max([winheight(0) - 2, 1]) . "\<C-d>" . (line('.') > line('$') - winheight(0) ? 'L' : 'H')

f:id:itchyny:20160202225534g:plain バッファーの最後の行がウィンドウの一番下になったらきちんと止まってくれます。 <C-d>はウィンドウをスクリーンの半分スクロールするマッピングです。 <C-f>と同様に[count]を受け取ることができますが、<C-d>はウィンドウの一番下がバッファーの最後の行を超えることはありません。 max([winheight(0) - 2, 1])は、ウィンドウの高さが小さすぎてもエラーにならないようにしています。 LHというのは、スクロールはせずにスクリーンに表示されている一番下や上の行にカーソルを移動するマッピングです。 現在の行が最後の方ならL、すなわちスクリーンの最下行に移動し、そうでなければ一番上の行に移動します。

<C-b><C-f>ほど困るようなマッピングではありませんが、少し気に入らないことがあります。 上にスクロールしていった時に、一行目まで戻ってくれません。 これはあまり気持ちがよくないので、私は<C-f>マッピングとのアナロジーで次のように設定しています。

noremap <expr> <C-b> max([winheight(0) - 2, 1]) . "\<C-u>" . (line('.') < 1 + winheight(0) ? 'H' : 'L')

最初のページになったらHで一行目に戻ります。 nnoremapではなくてnoremapなので、ビジュアルモードにも適用されて便利です。

スクロールするマッピングには他にも<C-e><C-y>などがあります。 これらも<C-f><C-b>と同じような問題があります。 つまり、<C-e>を押し続けると最後一行だけになってしまいますし、<C-y>は押し続けても途中で止まってしまって一行目まで行きません。 この挙動は私は好きじゃないので、次のようにマッピングしています。

noremap <expr> <C-y> (line('w0') <= 1         ? 'k' : "\<C-y>")
noremap <expr> <C-e> (line('w$') >= line('$') ? 'j' : "\<C-e>")

まとめ

最後は一ページ残った状態でスクロールが止まってくれるし、上に行くときはきちんと最初の行まで戻ってくれて便利な設定です。

noremap <expr> <C-b> max([winheight(0) - 2, 1]) . "\<C-u>" . (line('.') < 1         + winheight(0) ? 'H' : 'L')
noremap <expr> <C-f> max([winheight(0) - 2, 1]) . "\<C-d>" . (line('.') > line('$') - winheight(0) ? 'L' : 'H')
noremap <expr> <C-y> (line('w0') <= 1         ? 'k' : "\<C-y>")
noremap <expr> <C-e> (line('w$') >= line('$') ? 'j' : "\<C-e>")

<C-f>を押しすぎて最後必ず戻っている人は検討してみてください。