20行でできる、端末版vimの縦分割スクロール高速化設定

RLogin, XTerm, iTerm2, TeraTerm, mltermなど、VT class 4の左右マージン機能([DECLRMM](http://www.vt100.net/docs/vt510-rm/DECLRMM)/[DECSLRM](http://www.vt100.net/docs/vt510-rm/DECSLRM))に対応した端末では、vimの出力コードである`t_CV`を使って、縦分割スクロールを速くすることが可能です。

`t_CV`はvimに大昔から入っていたようですが、はたしてVT端末を意識して入ったものなのかどうか、ぐぐってみても経緯がよくわかりませんでしたし、ヘルプにも端末での設定方法は書かれていません。

少なくとも現代のVT互換端末でこれを使うためのノウハウをちゃんと説明しているところは@ttdodaさんによる解説くらいでした。

[vimでの縦分割時のスクロール速度改善](http://slashdot.jp/~doda/journal/572013/)

ここでは、上記のページで例示されている設定を改良し、左右スクロールマージン対応端末の自動判定機能をつけたバージョンを提案します。
左右マージン機能を持たない端末でこの設定をしても、表示が乱れてしまうことは多分ありません。


効能
------

この`t_CV`の設定は、

- 細い回線越しにリモートアクセス(SSHなど)をしている時
- 端末のスクリーンサイズが大きい時

であるほど大きな効果が得られ、特に

- 縦分割領域を`j`/`k`キーで1行づつスクロールする

といった操作の速度を劇的に改善します。
どのくらい劇的かというと、処理するデータ量的に見て最大100倍以上の差が生じます。

@ttdodaさんによる、説明動画
[![decslrm.png](https://qiita-image-store.s3.amazonaws.com/0/7622/75e98b4e-c972-2878-f574-781f657915fa.png)](http://youtu.be/KQfOArRJkYI)

あと、「<b>別に縦分割時じゃなくてもスクロールが遅い</b>」というケースは対象外です。
そういう時は相当非効率な動きをするプラグインがステータスラインを色々デコレーションしていないかとか、端末が受け付けるキーリピート間隔の問題を疑ってみましょう。


設定
------

`.vimrc` のどこかに以下の20行を追加して下さい。

```vimrc
" Use vsplit mode
if has("vim_starting") && !has('gui_running') && has('vertsplit')
  function! EnableVsplitMode()
    " enable origin mode and left/right margins
    let &t_CS = "y"
    let &t_ti = &t_ti . "\e[?6;69h"
    let &t_te = "\e[?6;69l\e[999H" . &t_te
    let &t_CV = "\e[%i%p1%d;%p2%ds"
    call writefile([ "\e[?6;69h" ], "/dev/tty", "a")
  endfunction

  " old vim does not ignore CPR
  map <special> <Esc>[3;9R <Nop>

  " new vim can't handle CPR with direct mapping
  " map <expr> ^[[3;3R EnableVsplitMode()
  set t_F9=^[[3;3R
  map <expr> <t_F9> EnableVsplitMode()
  let &t_RV .= "\e[?6;69h\e[1;3s\e[3;9H\e[6n\e[0;0s\e[?6;69l"
endif
```

ここでは、端末からの応答を確実にとるためにファンクションキーを1個(t_F9/\<F19\>)を潰すような書き方をしています。t_F9の部分は、自分が使わないキーにして下さい。

2015/02/09 追記：
すっかり書き忘れていましたが、

>   set t_F9=^[[3;3R

のところで、`^[`となっているところは、<b>制御文字のESC(\033)</b>です。
なので、<b>上記をそのままコピペしても動きません。</b>
vimでは`<C-V><ESC>`のように入力して下さい。

t_CVについて
-------------------

vimの`t_CV`に設定されるべき出力コードは、スクリーンの左右マージンを設定するシーケンスです。
これは、terminfoでは`smglr`、termcapでは`ML`に相当するもので、最近のXTermの場合`"\033[%i%p1%d;%p2%ds"`を設定することが可能です。
また、VT100の場合はこの機能を持たないため、空とすべきです。
GUI版やMS-DOS版のvimでは内部専用コード`"\033|%p1%d;%p2%dV"`が設定され、vim自身によって再解釈されます。

`t_CV`が空になってしまうと、縦分割スクロール時、該当領域の全書き直しが起こります。
みなさんが縦分割領域を`j`/`k`キーで1行づつスクロールするごとに、縦分割された該当領域の文字やアトリビュート情報(色・強調・下線など)を、<b>vimが毎回生成して標準出力に送ることになってしまいます</b>。
さらにそれを<b>端末が毎回読んでパースし、端末が内部で保持しているスクリーン情報をいちいち書き替える</b>という、大変無駄な作業が発生してしまいます。

またもうひとつの副作用として、こうやって端末が大量のデータを一度に送るようになると、<b>画面がガタついて見えます</b>。
これはVT端末の描画更新タイミングをホスト側で制御できるような取り決めは特に無い、という問題のせいで、スクリーンを更新している途中の状態が端末に表示されてしまうためです。

一方、`t_CV`が適切に設定された場合、縦分割領域を`j`/`k`キーで1行づつスクロールするごとにvimが送る情報は、スクロールの指示と、スクロールによって新規に画面に表示されることになる1行だけになるため、リモート接続時などでは体感できるレベルのパフォーマンス向上メリットを得ることができます。


おわりに
-----------

ここまで読んだ皆さんは、「高速化したというより、今までが無駄すぎたのでは」という感想を持たれるかと思います。

左右マージン機能が今まで普及しなかった一番の理由は、VT100やVT220の機能セットにはこれが無かったためと思われます。
しかし近年、XTermのパッチレベル#280によって、XTermのデフォルトエミュレーションレベルがVT420に引き上げられました。
このあたりを境に、このVT Class4の機能への対応がじわじわと複数の端末で進められてきた、という経緯は無視できません。
この基盤整備が無ければこのポストの設定は絵に描いた餅ですし、VT互換端末界隈が成し遂げつつある成果のひとつだと思います。
今後これに呼応した、アプリケーション側の動きにも期待していきたいところです。