# vim からの制御シーケンスの使用例

Tera Term では、制御シーケンスを使用する事によってホスト側から Tera Term の動作を変更したりする事が出来ます。 ここでは vim からこれらの制御シーケンスを活用する為の設定例を紹介します。

## 挿入モードへの出入り時にカーソル形状を変更する

Tera Term では、以下のカーソル表示状態変更制御シーケンスに対応しています。

略称 | シーケンス | 機能
--- | --- | ---
DECTCEM | ESC [ ? 25 h | カーソルを表示状態にする
ESC [ ? 25 l | カーソルを非表示状態にする
DECSCUSR | ESC SP 0 q | カーソル形状を箱型で点滅状態にする
ESC SP 1 q | カーソル形状を箱型で点滅状態にする
ESC SP 2 q | カーソル形状を箱型で点灯(非点滅)状態にする
ESC SP 3 q | カーソル形状を下線で点滅状態にする
ESC SP 4 q | カーソル形状を下線で点灯(非点滅)状態にする
ESC SP 5 q | カーソル形状を縦線で点滅状態にする
ESC SP 6 q | カーソル形状を縦線で点灯(非点滅)状態にする
WYSTCURM | ESC [ 33 h | カーソルを点灯(非点滅)状態にする
ESC [ 33 l | カーソルを点滅状態にする
WYULCURM | ESC [ 34 h | カーソル形状を下線にする
ESC [ 34 l | カーソル形状を箱型にする
(AT&T 610) | ESC [ ? 12 l | カーソルを点灯(非点滅)状態にする
ESC [ ? 12 h | カーソルを点滅状態にする

これらの制御シーケンスを挿入モードに入る時 (t_SI 設定) および挿入モードから出る時 (t_EI 設定) に出力する事で、挿入モードでのカーソル形状を変更できます。
例えば .vimrc に以下の設定を追加すると、挿入モードに入った時にカーソル形状が下線で点滅に、挿入モードから出た時に箱型で点滅に変わるようになります。

    let &t_SI .= "\e[3 q"
    let &t_EI .= "\e[1 q"

注: DECTCEM 以外の制御シーケンスを使うには、Additional Settings ダイアログの Control sequence タブに有る Cursor control sequence を on にする必要があります。(デフォルトは off)

## クリップボードからの貼り付け時に自動インデントを無効にする

通常、ホスト側のアプリケーション (vim 等) からは、ユーザのキー入力とクリップボードからの貼り付けを区別できません。 しかし xterm の拡張機能である Bracketed Paste Mode を利用するとこれらを区別出来るようになるため、クリップボードからの貼り付け時は動作を変える事ができます。

.vimrc に以下の設定を追加すると、クリップボードからの貼り付け時に自動で set paste を実行して、自動インデントや補完等が無効になります。

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

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

        noremap <special> <expr> <Esc>[200~ XTermPasteBegin("0i")
        inoremap <special> <expr> <Esc>[200~ XTermPasteBegin("")
        cnoremap <special> <Esc>[200~ <nop>
        cnoremap <special> <Esc>[201~ <nop>
    endif

上記の設定ではノーマルモードで貼り付けを行うと挿入モードに切り替わって貼り付けが行われますが、これを行わずに挿入モードの時のみ有効にする場合は以下の設定を使用して下さい。
後述のGNU Screenとの併用時の注意点にある設定を行う場合はこちらの方が都合がいいでしょう。

    if &term =~ "xterm"
        let &t_SI .= "\e[?2004h"
        let &t_EI .= "\e[?2004l"
        let &pastetoggle = "\e[201~"

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

        inoremap <special> <expr> <Esc>[200~ XTermPasteBegin("")
    endif

#### Bracketed Paste mode について

この機能は xterm 拡張で、有効になっていると、クリップボードからの貼り付け時に文字列の前後に特別なシーケンスを付加するというものです。
具体的には、DECSET の 2004(<CSI>?2004h) で有効になり、貼り付け時に文字列の前に <ESC>200~, 後ろに <ESC>201~ が付くようになります。

## IME を制御する

Tera Term では IME の状態を制御する為の独自の制御シーケンスをサポートしています。 この制御シーケンスを利用すれば、モードにより IME の有効/無効を切り替えるといった事が出来るようになります。

.vimrc に以下の設定を追加すると、挿入モードから抜けた時に IME をオフにし、再度挿入モードに入った時に IME の状態を元に戻すようになります。

    let &t_SI .= "\e[<r"
    let &t_EI .= "\e[<s\e[<0t"
    let &t_te .= "\e[<0t\e[<s"

    set timeoutlen=100

timeoutlen の設定は、挿入モードで ESC キーを押してから実際に挿入モードを抜ける(IME がオフになる)までの時間を調整しています。 この値を短くすれば ESC キーを押してから IME がオフになるまでの時間が短くなりますが、 遅い回線を使っている時などにカーソルキーやファンクションキーが正しく働かないというトラブルが起きる事があります。
また ESC キーを押した後の遅れを減らすには"挿入モードでの ESC キーを押した後の待ちを無くす"の設定を利用する方法もあります。

## 挿入モードでの ESC キーを押した後の待ちを無くす

Tera Term や xterm 等の端末エミュレータでは、ESC キーが押された時に ESC (0x1b) を送信します。また、カーソルキーやファンクションキー等が押された時に ESC で始まる文字列を送信します。 この為、ホスト側のアプリケーションでは ESC を受信しても、それだけでは ESC キーが押されたのか、それともカーソルキーやファンクションキーが押されたのか区別する事が出来ません。
そこで vim では ESC を受信した時は 1 秒ほど待ち、他の文字が送られてこないか、送られてきた場合はカーソルキー等を表す文字列になるかどうかで、ESC キーが押されたのか、それともカーソルキー等が押されたのかを判別します。 この為、挿入モードで ESC キーを押されてから実際に挿入モードを抜けるのが 1 秒程遅れます。 これにより、モードによりカーソル形状を変える設定や IME を制御する設定を行っても、状態が反映されるのが遅れます。

この問題は Application Escape Mode を使う事で回避できます。 .vimrc に以下の設定を追加すると、挿入モードで ESC キーを押した時に待たずにすぐに挿入モードを抜けるようになります。

    let &t_SI .= "\e[?7727h"
    let &t_EI .= "\e[?7727l"
    inoremap <special> <Esc>O[ <Esc>

## ウィンドウタイトルを保存/復元する

Tera Term では xterm のウィンドウタイトル変更制御シーケンスに対応しています。 例えば vim の場合、set title を実行するとウィンドウタイトルに現在開いているファイル名等の情報が表示されるようになります。
しかし vim はウィンドウタイトルの変更は出来ますが、元のタイトルが何であったか把握する事が出来ません。 その為 vim の終了時に元のウィンドウタイトルに戻す事が出来ず、代わりに "Thanks for flying Vim" や "Vim を使ってくれてありがとう" というメッセージをウィンドウタイトルにセットして終了します。

この問題は、ウィンドウタイトルの保存/復元機能を使う事で回避できます。 .vimrc に以下の設定を追加すると、vim 起動時にウィンドウタイトルを保存し、終了時に保存したタイトルに復元するようになります。

    let &t_ti .= "\e[22;0t"
    let &t_te .= "\e[23;0t"

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

vim で縦分割(vsplit)をすると遅いとかがくがくすると言われる事がよくあります。 これは多くの端末エミュレータが基準としている VT100 や VT200 シリーズの仕様からくる問題です。

VT100 にはスクロールする範囲を設定する機能(スクロールリージョン/上下マージン)が有りますが、 これは例えば上マージンを5行目に、下マージンを20行目にセットする事によってスクロールする範囲を5行目から20行目にするというもので、 縦方向の桁単位で指定する事は出来ません。
このため、横分割(split)では上下マージンを利用してスクロールを行う事が出来ますが、 縦分割ではスクロールが利用できないため、スクロールしたい領域全体を書き直す必要があり、 スクロールが遅くなります。

しかし、VT400 シリーズ以降では桁位置によるスクロール範囲設定(左右マージン)機能が追加されており、Tera Term でもバージョン 4.79 からこの機能をサポートしました。 以下の設定を .vimrc に追加して縦分割時のスクロールに左右マージンを利用するように設定する事によって、縦分割時のスクロールが改善されます。

    if &term =~ "xterm"
        let &t_ti .= "\e[?6h\e[?69h"
        let &t_te .= "\e7\e[?69l\e[?6l\e8"
        let &t_CV = "\e[%i%p1%d;%p2%ds"
        let &t_CS = "y"
    endif

注: この設定を行うと、左右マージンに対応していない端末では縦分割が正しく動かなくなります。xterm や mlterm, RLogin, tanasinn, pangoterm などは左右マージンに対応していますが、それ以外の端末を併用する場合は注意してください。

## GNU Screenとの併用時の注意点

GNU Screenを使用している場合、screen内部で動かしているアプリケーションが制御シーケンスを送っても、そのシーケンスにscreenが対応していない場合はTera Term側へ送らずに捨ててしまい期待通りに動きません。その場合、

    if &term == "screen"
        let &t_SI .= "\eP\e[3 q\e\\"
        let &t_EI .= "\eP\e[1 q\e\\"
    else
        let &t_SI .= "\e[3 q"
        let &t_EI .= "\e[1 q"
    endif

というように、端末タイプが screen の時は t_SI, t_EI, t_ti, t_te に設定するシーケンスを "\eP" と "\e\\" で挟むとTera Termに制御シーケンスが届くようになります。
ただし、この方法を使って制御シーケンスがscreenを抜けるようにしても送ったシーケンスの状態はscreenの管理外になるので、screenのウィンドウを切り替えた時等に期待通りに動かない場合がありますので注意してください。