vimとterm.c

この記事はVim Advent Calendar 2014の15日目の記事です。 14日目は haya14busa さんの[incsearch.vimでVimの検索体験をリッチにする](http://haya14busa.com/enrich-your-search-experience-with-incsearch-vim/)だったので、incsearch.vim入れてみたところかなり快適でした。しばらく使ってみます。

さてこの前あるvimmerとtwitterで話した時に、term.cという、端末との雑多なやりとりをする部分をneovimが捨てるという話を耳にしまして、私はneovimのことは全然知らないんですが、知らないなりにそれは非常にもったいないなあ、とか勝手に言ってたんですが、その場ではいまいちそのもったいなさが伝わってなかった気がしたので、この場を借りてterm.cの魅力の一端をご紹介させてください。
[trachet](http://qiita.com/kefir_/items/3fcadb4633863e25e12d)を利用して、term.cならではの起動っぷりを観察してみます。
[trachet](http://qiita.com/kefir_/items/3fcadb4633863e25e12d)というのは何かというと、端末とアプリケーションの間でどんなデータのやりとりが行われているかを可視化するツールです。

コードは一切出さないので、これを機会にterm.cに興味を持ってもらえれば幸いです。

環境はXTerm #pl313で、$TERMはxtermです。
端末に vim\<Enter\> と打ち込んで、vimが起動するまでのほんの一瞬の間のやりとりを見ていきましょう。

フェーズ 1: あいまいな文字幅の判定
-----------------------------------------------
![phase1.png](https://qiita-image-store.s3.amazonaws.com/0/7622/5680b880-8411-26d7-d985-5dd391adaf6c.png)

いきなり変なスクリーンショット出しますが、これは何かというと、まさにvimを起動した瞬間の、vimと端末のやりとりをあらわしている、とお考え下さい。「>>>」で始まる行はvimから端末へ、「<<<」で始まる行は端末からvimへデータが流れています。1行1行の意味をくわしく理解する必要も特に無いので、なんとなく見ていて頂ければいいです。

起動後、まずは代替スクリーンへ入り(t_ti)、テンキーやカーソルキーの動作設定を初期化しています(t_ks)。

代替スクリーンというのを簡単に説明すると、現在のスクリーンを退避させることができる機能です。
vimをサスペンドしたり終了した際に、画面がvim起動前の状態に戻っていたりしたら、この機能が使われています。
端末のアプリケーションではお約束の初期化処理ですが、ここを過ぎるとvim独特の動きがはじまります。
まずはambiwidthオプションの初期化です。

- カーソルを\[2, 1\](2行目の1文字目)へ移動
- 幅の解釈があいまいな文字「▽」を出力
- 現在カーソル位置の報告を要求(デバイス状態要求、DSR 6)
- 端末より、カーソル位置応答\[2, 2\]を得る
- 応答より、「▽」の論理的な幅は1文字と判断、ambiwidthオプションを"single"とする

といったインタラクションが読み取れますね。

フェーズ 2: 端末IDとバージョンの取得
-------------------------------------------------
![phase2.png](https://qiita-image-store.s3.amazonaws.com/0/7622/9a8fefce-3b4f-cd0d-03dc-bcf99183eaee.png)

カーソルの表示・点滅状態設定、スクリーン消去に続いて、DA2要求(第2端末特性要求、t_RV)が出ています。
XTermは応答として、VT420相当の端末IDと、バージョン313を返しました。

近年のXTermはVT420相当の端末IDを報告しますが、実際にclass 4相当の大量の機能を持っています。
vimは非常によくやっている方で、このうちの2割くらいは引き出せているのではないでしょうか。

ともあれここは非常に高度な読み合いをやってるところです。
このわずかなDA2応答から、vimは以下の情報を汲み取っています。

- 端末が7ビット符号系を使用しており、以降は8bit目を落とした制御シーケンスを出力すべきである
- XTermのパッチレベルは277以上であるので、SGR拡張マウスレポーティングを有効にしてもよい(ttymouseオプションを"sgr"とする)
- XTermのパッチレベルは141以上であるので、次項で説明するtcap-queryを使用できる可能性がある。

DA2の読み方などについて知りたい方は[こちら](http://qiita.com/kefir_/items/0bda5e55f43392420d66)をご覧下さい(情報がちょっと古いですが)
SGR拡張マウスレポーティングについては[こちら](http://qiita.com/kefir_/items/4de35de8f0abe4f35392)に別途記事を書いています。

フェーズ 3: 応答ベースのtermcap要求
----------------------------------------------------
![phase3.png](https://qiita-image-store.s3.amazonaws.com/0/7622/f6a966ea-6cb2-8e08-ae46-abdca6ca676d.png)

起動フェーズ終盤です。
まずXのウィンドウタイトル(t_ts/t_fs)とアイコンラベル(t_IS/t_IE)を設定していますね。
タブ型端末の場合は、アイコンラベルのかわりにタブタイトルが設定されることがあります。

その後、tcap-queryを断続的に送出しています。
tcap-queryはあまり聞き慣れない言葉だったと思いますが、ようするに端末に直接termcapを問い合わせることです。
これは2000年前後、XTermに256色拡張が入った直後の混乱をどうにかするために、vim側からの要望によってXtermに入ったもののようで、xterm/vim双方のChangeLogからその経緯をなんとなく窺い知ることができました。

またこの機能は、最近になってRLogin/TeraTerm(色数のみ)/iTerm2など一部の端末でなぜか流行し始め、掘り起こし対応が進んでいる分野でもあります。TeraTermなんかでは、tcap-queryを使われやすくするためにDA2のバージョン番号を上げてきたりしています。

現在のところ、vimは35個のtcap-queryを送出し、その応答を得て以下のような端末に関するオプション値を調整します。

- 端末がサポートする色数
- カーソルキー/Shift付きカーソルキーシーケンス
- ファンクションキーシーケンス
- Home/End/Shift+Home/Shift+Endキーシーケンス
- Help/Undo/Backspace/Insert/Deleteキーシーケンス
- 全画面/次画面キーシーケンス

ただし、この35個のクエリは一度に送出されず、10個づつ様子を見ながら送られます。
1度に送出してしまうとパケット分割が起き、その間にまれにユーザのキー入力が挟まって誤動作を引き起こしてしまうからです。

-------------
みなさんがvimを起動する度に、だいたいこんな感じのやりとりがおこなわれています。
起動してからのこともまだ色々書きたいんですけど日付が変わりそうですし、他のことも書くととりとめなくなってしまうのでここで終わります。
でもterm.cのマウスサポートの多彩さについて書いてなかったです。これは圧巻で、追随するアプリケーションは他にちょっと見当らないので、いつかまとめて書きたいです。

-------------
あとこの際だから言っておきたいんですけど、端末のvimでttyfastをsetすると速くなる、みたいな謎の噂があるけど、term.cを見る限りは全然ちがうように見えるんですよね。ttyfastは

- 上下スクロールマージン機能をもたない端末において、行削除と行挿入を同時に行うことによる上下スクロールもどきな動きを~~行う。（ヘルプには「スクロールが滑らかになる」とか書かれてたりしますが、たぶん逆にガタつくと思います）~~
追記：すみません、「スクロールもどきな動きを行う」、じゃなくて逆の「スクロールもどきな動きを行わないようにする」でした。
      調べたのが1年以上前で記憶が混濁してました。[このへん](https://twitter.com/kefir_/status/373675710029651968)を見て思い出しました。
      結局のところ上下スクロールマージンの無い端末なんて今時無い(VT100でさえ上下スクロールマージンは切れる)でしょうし、こっちの意義はもう無いと思います。

- 行をまたぐマウスコピー時に、間に余計な改行が挟まらないよう、特殊なテクニックで細工する。

という全然関係無い2つの機能をなぜか一緒に提供する謎のオプションだと思います。
とりとめのない話ですみませんでした。