端末エミュレータから 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 の状態を復元する.
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 からクライアント側の端末エミュレータを制御することができます.エスケープシーケンスによる端末制御の大まかな流れは以下の通りです.
- ユーザがキー操作を行う(例|挿入モードを出る)
- キー操作に対応するシーケンスを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
オプションに小さな値を設定します.ただし,小さすぎる値を設定すると,回線が遅い場合に方向キーなどのエスケープシーケンスが正しく動作しなくなる可能性が高くなります.個人的には0.1秒(timeoutlen=100
)くらいがちょうどいいと感じました.
※タイムアウトに関する追記 [2015/1/5]
timeoutlen
ではなく ttimeoutlen
の値を小さくするほうが使い勝手が良いことがわかりました.
" ESC キーを押してから挿入モードを出るまでの時間を短くする
" timeoutlen ではなく ttimeoutlen の値を小さくする
set ttimeoutlen=100
というのも,Vim には「エスケープシーケンスを区別するための待機時間」の他に「キーマッピングを区別するための待機時間」が設けられているためです.timeoutlen
は両方共通の待機時間を設定するオプションなので,この値を小さくするとキーマッピングの待機時間も短くなります.そのため,複数のキーの組み合わせからなるマッピングを使おうとすると,超高速で入力しなければ正しく反応してくれなくなります.一方,ttimeoutlen
はエスケープシーケンス(※)の待機時間のみを設定するオプションなので,この値を小さくしてもマッピングの入力速度には影響を与えません.
※ドキュメントには「エスケープシーケンス」ではなく「キーコード」の待機時間と書かれているので,本当はエスケープシーケンスよりも広い概念だと思いますが,調べてもよくわかりませんでした.