vimと端末エミュレーション

  • 45
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

===================

本記事は Vim Advent Calendar 2012 の8日目です。
7日目は@yoshikawさんによる、Vimの背景色を環境変数で設定するでした。

私はunix端末版のvimをよく使っているのですが、元来そういうものをあまりうまく使いこなせる性質ではなく、エディタと言われても実感としてはある特定のパターンを持ったI/Oの流れとして見てしまいがちです。実際vimやemacs等をいじっていても表現レベルの挙動がやたらと気になってしまい全く集中できず苦慮することもあります。しかし端末とのI/Oに関してはいつもチェックしていたり、批評家ぶって「あーこの動きキレがないわー、ありえないわー」などと日常的にブツブツ呟いているので、そのあたりを調整したり割り込んでどうにかする方面でがんばろうとしています。今回はそんな第3極的視点からvimにフォーカスし、来年以降プラグインや追加機能として提案していけそうなトピックをいくつか取り上げてみたいと思います。(今すぐ使えるtipsとかはほぼないので注意)

MinTTYの拡張モード7727への対応

viから受け継いだESCキー(またはCtrl-[やCtrl-3)を押下する、という動作は、vimにとって基本中の基本ともよべるものです。
しかし一方で端末シーケンスの標準(ISO-6429)的な観点で考えると、ESCキーの生成するエスケープキャラクタ\x1bは本来単独では認識されずいわゆる「エスケープシーケンス」の導入として使用されるのが筋とされており、それ以降に続く文字と組み合わせてはじめて意味を持つことになります。
実際に7bitモード下でのファンクションキーやカーソルキーや端末からの応答報告などは、エスケープ文字をプレフィックスにしたコードを生成するものがほとんどです。
そこでvimは、「エスケープキーが単独で押された」ことを無理矢理判定するためにタイムアウト機構等を使用しており、そのための設定や実装も実はめんどくさいことになっています。
そんな経緯から今後推進してみては、と考えているのがMinTTYの提案する拡張モード7727への対応です。このモードを使用すると、Vimが動作するキーパッドモードでエスケープキーを押したときに以下のコードが生成されるようになります。

ESC O [

これを使用することでアプリケーション側は即時にエスケープキーが押されたことを判定できるのですが、これに対応することによって以下のようなメリットが期待できます。

  • ESCキーのタイムアウトによる誤判定を回避できる。
    たとえばシングルシフトシーケンスで表現される上カーソルキー「ESC O A」を「独立したESC」+「O A」のように理解してしまう不具合をなくせる。

  • 無駄のない、キビキビとした操作感をユーザに提供できる
    vim側で実際に「ESC O [」が送られてくることを確認した時点でタイムアウト処理を無効化したり、タイムアウト時間を小さくする、などの調整を行なうことが可能になります。

端末側での普及の目処がついたら、vim側でもオプション値を新設するなどの方法で拡張を提案していければなと考えています。
また自然に考えておそらく過去にもESCの代替キーなどの仕組みが考案された経緯があるんじゃないかと思っています。そのあたりも調査してみたいです。(情報下さい)

フォーカスイベントの取得

最近ですが、gvimではvim scriptからFocusGainおよびFocusLostというイベントがとれることを知りました。
そこで、端末版vimでも、拡張モード1004(Focus Reporting Mode)と対応させることを提案してみたいと考えています。

このイベント報告は、7bit符号系では以下のように表現されます。

フォーカスイン:

ESC [ I

フォーカスアウト:

ESC [ O

現状でもvim scriptを用いて

let &t_ti .= "\e[?1004h"
let &t_te .= "\e[?1004l"

のようにしてこのモードを有効化し、

map <special> <Esc>[I …   " フォーカスインイベント
map <special> <Esc>[O …   " フォーカスアウトイベント

などとすることで、なんとかイベントに応じた動作をさせることが可能ですが、やはり将来的には本体側から対応してGUI版との差異を緩和するのが望ましいと思います。
この機能も多くの端末エミュレータが実装しています。

念のためESCのタイムアウトの調整具合(前項参照)から、フォーカスイベントがデフォルトのキーマップ"[I"と誤認される可能性を検討してみましたが、 同様の事例としてデフォルトのキーマップ"[M"とterminfoケーパビリティkmousの"ESC [ M"(標準マウスイベントシーケンスのプレフィクス)があり、すでにちゃんと運用されている実績があるなどの理由から、懸念としては無視してかまわないと考えています。

Bracketed Paste Mode(括弧付きの貼り付けモード)

最近端末界でかなり広く受け入れられるようになった、Bracketed Paste Mode(括弧付きの貼り付けモード)についても少し宣伝します。

従来の端末における貼り付け操作というのは、単純にインプットバッファに貼り付け文字を突っこむことで実現されていました。
これは、アプリケーション側から見るとキー入力とまったく区別が付きません。
そこで、貼り付け文字列の最初と最後に特殊な符号を付加すれば、paste awareなユーザビリティが実現できてすばらしいのでは、ということで提案されたのがBracketed Paste Modeです。
この機能は実装が単純なためか、貼り付け操作を受け入れるほとんどの端末エミュレータで実装されています。

こちらからvim scriptでの設定方法を引用します。

let &t_ti = &t_ti . "\e[?2004h"
let &t_te = "\e[?2004l" . &t_te
let &pastetoggle = "\e[201~"

function XTermPasteBegin(ret)
  set paste
  return a:ret
endfunction

map <special> <expr> <Esc>[200~ XTermPasteBegin("i")
imap <special> <expr> <Esc>[200~ XTermPasteBegin("")
cmap <special> <Esc>[200~ <nop>
cmap <special> <Esc>[201~ <nop>

gvimで実験してみると、上記のような設定を特にしなくても貼り付け操作を適切に感知して動作するようです。
端末版でもデフォルトで本体側の対応が入っても良い気がします。
またこの機能は、貼り付け操作にまつわるいくつかのセキュリティ上の問題対策にもなり得るのですが、現状の端末側の実装ではそれもあまり考慮されていない、という現実もあり、端末界サイドへの実装のブラッシュアップの提案もしていければな、と考えています。

文字ずれ問題への対応

文字ずれ
現在、端末と文字ずれの話はとてもややこしいことになっていて、文字ずれという現象の各種タイプとその要因、各端末やエディタ等における文字幅の扱い方の類型や細かな派生等について見ていくとそれだけでまるまる1エントリ消費してなお余りあるような状況です。
私も最近、MinEdという非常にビジョナリーな実装に触れたことで、端末文字ずれ問題を的確に捉え対処するための真に有用な方法論とそれを共通化する枠組みについてだいぶ考えがまとまってきているところですが、既存の提案の中にも、すでに文字ずれに関連したものがいくつかあります。

yaftのGWREPTを除いてはいずれもAmbiguous Width問題にフォーカスした文字ずれ対策なのですが、
現在vim scriptで対応できるのはMinEdの提案する拡張モード7700くらいでしょうか。

let &t_ti .= "\e[?7700h"
let &t_te .= "\e[?7700l"

noremap <special> <Esc>[1W :set ambiwidth=single<CR>
noremap <special> <Esc>[2W :set ambiwidth=double<CR>
inoremap <special> <Esc>[1W <Esc>:set ambiwidth=single<CR>i
inoremap <special> <Esc>[2W <Esc>:set ambiwidth=double<CR>i
cnoremap <special> <Esc>[1W <Esc>:set ambiwidth=single<CR>
cnoremap <special> <Esc>[2W <Esc>:set ambiwidth=double<CR>

拡張モード8428への対応は本体へのパッチでこんな感じになると思います。

diff -r 48af86560945 src/option.c
--- a/src/option.c  Sun Oct 21 02:37:10 2012 +0200
+++ b/src/option.c  Fri Dec 07 20:45:58 2012 +0900
@@ -5818,6 +5818,10 @@
    else if (set_chars_option(&p_fcs) != NULL)
        errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'");
 # endif
+        else if (STRCMP(p_ambw, "double") == 0)
+       out_str((term_is_8bit(T_NAME) ? CSI_STR : ESC_STR "[") "?8428l");
+        else /* if (STRCMP(p_ambw, "single") == 0) */
+       out_str((term_is_8bit(T_NAME) ? CSI_STR : ESC_STR "[") "?8428h");
     }
 #endif

TNREPTAMBは対応端末が少ないので、MinEdが行なっているようなCPR(カーソル位置報告)を利用した判定法を起動時のtermresponseのフェーズで行うのが良いかもしれませんが、起動時間が犠牲になる懸念があるため非同期に報告を受け取るなど、細心の注意を払って実装する必要がありそうです。

DRCS(Soft Character Set)を利用する

この話題も過去の実装の種類・経緯等について触れると非常に長くなるし、現在の話をしても一部ISO-2022やUnicodeの話題につっこむことになるので、だいたいどんなことを計画しているか、端末版vimでどんなことができるようになるか、という紹介だけにします。
現在、GUI版のvimではsignにアイコン画像を割り当てたりできるようなのですが、簡単に言うと端末版vimであっても、そういったグラフィカルな機能との対応を取れる可能性が出てきたということです。
このDRCSという機能を用いると、文字と絵を混在させることができるようになります。

たとえばDRCSを使用してvim-powerlineを表現することができます。
DRCS-Powerline

DRCSを並べておおきな画像を表現することも可能です。
DRCSはあくまでも文字の一種なので、たとえばvimでこのような「画像」を保存して、emacsで開き直して画像として見る、といったことが可能になります。
DRCS

RLoginではSixelカラーグラフィックス(DECSIXEL)を使用したカラー化拡張を行いました。これは現時点ではRLogin独自の仕様に過ぎないのですが、今後他の端末も追従する可能性もあります。
DRCS-RLogin

縦分割時のスクロール高速化

端末でvimを使う人であれば、縦分割時スクロールの異常な遅さは悩みの種だと思います。
あのガタつきは精神的にイラっとするばかりではなく、多分目とかにも良くないので、なんらかの対策を入れたいところです。
実はvimは縦分割時スクロールにかなり無駄なコードを送ってきているので、まだまだ高速化の余地があります。
どのくらい高速化するかは一概には言えないですが、bceと呼ばれるケーパビリティを持つ一般的なxterm系端末なら、I/O量ベースで見積もって半分未満程度には削減可能かな、と考えています。
またレンダリングにボトルネックがある場合は適切なヒントを端末に与えてやる必要がありますが、後期のVTをエミュレーションする端末(VT class 4相当の実装)ではスクロールの横マージンを設定したり矩形領域のコピー等が利用できる可能性があります。
このケーパビリティをうまく判定し、活用していくことでかなりの高速化が期待できると考え、現在このあたりの調査をはじめたところです。

端末背景色の判定

背景色の判定はemacsやMinEdでも行なわれているので、vimでもtermresponseフューチャーを有効化してコンパイルされたものは、これをおこなってよいだろうと考えています。
具体的には起動時のtermresponseフェーズにおいて、xtermのパッチレベル判定を行った後、端末に対してデフォルト前景色・背景色の報告を要求する特殊なクエリを投げ、応答を非同期で受け取りbackgroundオプション値自動設定の参考にするというものです。
背景色の判定が可能な端末は、xterm、rxvt-unicode、TeraTerm、RLogin、MinTTY、mltermなどです。gnome-terminal、rxvt、Poderosa、iTerm2などは、実はあと一歩というところまで実装されているので、対応を入れるのは楽そうだと考えています。

タイトルモードの利用

現在のvimでは、

set title

と設定することでタイトル設定シーケンスを使用します。

タイトル設定シーケンスはOSCシーケンスと呼ばれる書式で表現されるのですがこれには若干の問題があって、PuTTYやRLogin等の単一バイトの8bitコントロールキャラクタを解釈してしまう端末ではタイトル文字中に現われる\x9CがST(終端文字/8bit形式)と誤認されてしまい、残りの文字が端末にダダ漏れになる、という現象があります。\x9CはUTF-8の2バイト目以降に現れ得るので、たとえば

$ vim "( ^o^)< Hello.txt"

などとし、vimで

:set title

とすると、端末によっては若干おかしな挙動となる場合があります。

対策としては、8bitコントロールを無効化させるエスケープシーケンスを送り7bit符号系で運用する、タイトル中の\x9cを含む文字を"?"に置換するなどしてあえて化けさせる、などが考えられますが、よりベターな方法としてxtermパッチレベル252で導入されたタイトルモードを使用することを思いつきました。
このモードを使用してタイトルを設定すると7bit ASCIIの範囲の印字可能文字でOSCが表現できるようになるのでとても衛生的だし、ISO-6429標準の観点から見ても望ましいです。
まず主要な端末へこのタイトルモードを普及させる活動を行い、時期を見てvim側からの対応を提案してみたいな、と思いました。

まとめ

vimというよりはむしろ端末エミュレーションなエントリでしたがいかがでしたでしょうか。
ほかにとり上げたかった話題としてはtermresponseフェーズの抜本的改良やPASTE64(OSC52)を利用したクリップボード操作、各種ウィンドウ操作(最大化・最小化・フォアグラウンド化・透明化等)の話題、IME対応、テキストカーソル色や形、マウスカーソル周りの設定、SGRアトリビュート拡張への対応などがありましたがいずれも調査不足で割愛しました。
unix端末版vimの良いところは最終的に全ての入出力がデータストリームの形で抽象化され、またそれを自由にいじりまくれる点だと思っています。vim内部からの拡張でなにかもの足りなくなったときは、端末や端末フィルタの世界に足を踏み入れてみるのも良いかもしれません。

明日のVim Advent Calendar 2012は、@deris0126 さん PerlやRubyの正規表現などをtext objectとして扱うためのplugin です。