Edited at

挿入モードを出る時にIMEをオフにする(SSH接続など端末エミュレータからVimを利用する場合)

More than 3 years have passed since last update.

端末エミュレータから Vim を利用する場合,Vim はサーバ側に,IME はクライアント側にあります.そのため,挿入モードを出る時に IME を(日本語入力を)自動的にオフにしようと思っても,MacVim や GVim のようにはいきません.一部の端末エミュレータでは,サーバからクライアントの IME をエスケープシーケンスによって制御することが可能です.ここでは,エスケープシーケンスを用いて挿入モードを出る時に IME を自動的にオフにする方法を紹介します.


使用した端末エミュレータ

RLogin (2.16.8)

※ 元ネタは Tera Term のマニュアルから拾ってきたので,おそらく Tera Term でも動くはず.


設定

Vim で .vimrc を開いて,以下のコマンドを記述します.注意事項として,^[[<r^[[<s^[ESC を表しています.入力する際には,^ [ ではなく,Ctrl+v ESC と打ってください.以下のコマンドをコピペしても正しく動きません!


.vimrc

" 挿入モードに入る時,前回の挿入モードにおける IME の状態を復元する.

set t_SI+=^[[<r

" 挿入モードを出る時,現在の IME の状態を保存し,IME をオフにする.
set t_EI+=^[[<s^[[<0t

" Vim 終了時,IME を無効にし,無効にした状態を保存する.
set t_te+=^[[<0t^[[<s

" ESC キーを押してから挿入モードを出るまでの時間を短くする
set timeoutlen=100


※ 本記事の最後に timeoutlen の設定について追記しましたので,そちらもご覧ください.timeoutlen の代わりに ttimeoutlen を使うほうが便利らしい,という話です.


解説

エスケープシーケンス(ANSI escape code)とは,端末を制御するための文字の組み合わせです.端末エミュレータはエスケープシーケンスによる制御機能もエミュレートしているため,エスケープシーケンスを利用することでサーバ側の Vim からクライアント側の端末エミュレータを制御することができます.エスケープシーケンスによる端末制御の大まかな流れは以下の通りです.


  1. ユーザがキー操作を行う(例|挿入モードを出る)

  2. キー操作に対応するシーケンスをVim が端末エミュレータに送信する

  3. 端末エミュレータがシーケンスを受信する

  4. 受信したシーケンスに基づいて端末エミュレータが処理を行う(例|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 オプションに小さな値を設定します.ただし,小さすぎる値を設定すると,回線が遅い場合に方向キーなどのエスケープシーケンスが正しく動作しなくなる可能性が高くなります.個人的には0.1秒(timeoutlen=100)くらいがちょうどいいと感じました.


※タイムアウトに関する追記 [2015/1/5]

timeoutlen ではなく ttimeoutlen の値を小さくするほうが使い勝手が良いことがわかりました.


.vimrc

" ESC キーを押してから挿入モードを出るまでの時間を短くする

" timeoutlen ではなく ttimeoutlen の値を小さくする
set ttimeoutlen=100

というのも,Vim には「エスケープシーケンスを区別するための待機時間」の他に「キーマッピングを区別するための待機時間」が設けられているためです.timeoutlen は両方共通の待機時間を設定するオプションなので,この値を小さくするとキーマッピングの待機時間も短くなります.そのため,複数のキーの組み合わせからなるマッピングを使おうとすると,超高速で入力しなければ正しく反応してくれなくなります.一方,ttimeoutlen はエスケープシーケンス(※)の待機時間のみを設定するオプションなので,この値を小さくしてもマッピングの入力速度には影響を与えません.

※ドキュメントには「エスケープシーケンス」ではなく「キーコード」の待機時間と書かれているので,本当はエスケープシーケンスよりも広い概念だと思いますが,調べてもよくわかりませんでした.


参考資料


  1. IME を制御する|Tera Term ヘルプ

  2. Tera Term の対応制御シーケンス|Tera Term ヘルプ

  3. RLogin の対応制御シーケンス|RLogin

  4. ANSI escape code|Wikipedia

  5. 端末オプション|Vim documentation

  6. ttimeoutlen|Vim documentation