端末エミュレータから Vim を利用する場合,Vim はサーバ側に,IME はクライアント側にあります.そのため,挿入モードを出る時に IME を(日本語入力を)自動的にオフにしようと思っても,MacVim や GVim のようにはいきません.一部の端末エミュレータでは,サーバからクライアントの IME をエスケープシーケンスによって制御することが可能です.ここでは,エスケープシーケンスを用いて挿入モードを出る時に IME を自動的にオフにする方法を紹介します.
使用した端末エミュレータ
RLogin (2.16.8)
※ 元ネタは Tera Term のマニュアルから拾ってきたので,おそらく Tera Term でも動くはず.
設定
Vim で .vimrc を開いて,以下のコマンドを記述します.注意事項として,^[[<r
や ^[[<s
の ^[
は ESC
を表しています.入力する際には,^
[
ではなく,Ctrl+v
ESC
と打ってください.以下のコマンドをコピペしても正しく動きません!
" 挿入モードに入る時,前回の挿入モードにおける IME の状態を復元する.sett_SI+=^[[<r" 挿入モードを出る時,現在の IME の状態を保存し,IME をオフにする.sett_EI+=^[[<s^[[<0t
" Vim 終了時,IME を無効にし,無効にした状態を保存する.sett_te+=^[[<0t^[[<s" ESC キーを押してから挿入モードを出るまでの時間を短くするsettimeoutlen=100
解説
エスケープシーケンス(ANSI escape code)とは,端末を制御するための文字の組み合わせです.端末エミュレータはエスケープシーケンスによる制御機能もエミュレートしているため,エスケープシーケンスを利用することでサーバ側の Vim からクライアント側の端末エミュレータを制御することができます.エスケープシーケンスによる端末制御の大まかな流れは以下の通りです.
- ユーザがキー操作を行う(例|挿入モードを出る)
- Vim がキー操作に対応するシーケンスを端末エミュレータに送信する
- 端末エミュレータがシーケンスを受信する
- 端末エミュレータが受信したシーケンスに基づいて処理を行う(例|IME をオフにする)
端末オプション
Vim は「ユーザのキー操作」と「操作時に端末へ送信されるシーケンス」の対応表を端末オプションという形で保持しています.現在設定されている端末オプションは :set termcap
で確認できます.端末オプションに IME を制御するエスケープシーケンスを追記することで,Vim から IME を制御することが可能になります.今回の設定で用いるオプションは以下の3つです.
オプション | オプションの意味 |
---|---|
t_SI | 挿入モードに 入る時 |
t_EI | 挿入モードを 出る時 |
t_te | 端末オプションに基づくシーケンスの送信を終了する時(Vim 終了時) |
Control Sequence Introducer (CSI)
エスケープシーケンスの多くは ESC
[
で始まります.これらのシーケンスは CSI と呼ばれ,CSI を用いることで端末の高度な制御を行うことが可能です.今回の設定に関係するエスケープシーケンスは以下の通りです.以下のエスケープシーケンスを上記の端末オプションに追記することで,IME の制御を行います.
シーケンス | シーケンスの意味 |
---|---|
ESC [ < r | 保存された IME の状態を復元する |
ESC [ < s | 現在の IME の状態を保存する |
ESC [ < 0 t | IME を オフにする |
ESC [ < 1 t | IME を オンにする |
タイムアウト
実は ESC
を押してから挿入モードを出るまでの間に,1秒間(timeoutlen=1000
)の待ち時間が設けられています.これは ESC
単体とエスケープシーケンスを区別するための仕掛けです.ESC
は単体で用いられる場合と,ESC
[
A
(方向キー上)のようにエスケープシーケンスとして他の文字と組み合わせて用いられる場合の2パターンがあります.ESC
を受信した直後は後に文字が続く可能性があり,どちらのパターンなのか区別することができません.そのため,Vim は timeoutlen
オプションで指定した時間だけ待機し,その間に続く文字が入力されなければ「ESC
は単体で用いられた」と判断します.
この判断の後にエスケープシーケンスが送信されるため,timeoutlen
オプションの値が大きいと ESC
を押してから IME がオフになるまでの間に無視できないレベルのタイムラグが発生します.これを防ぐために,timeoutlen
オプションに小さな値を設定します.