Vim/Neovimからモード切替時にIMEを制御する
VimとIMEの相性の悪さ
Vimはモードを切り替えながら使うテキストエディターです。入力時は主にノーマルモードとインサートモードを行き来しながら使うことでしょう。モード切替により多様な操作が可能になっています。
しかし、日本語の文章を書くときはかなり面倒なことになります。ノーマルモードでなにか操作するときはIMEがオフでないといけません。したがって、IMEの状態も制御する必要がでてきて、Vimの2モード×IMEの2モードの計4モードの制御をユーザーがやるはめになります。これは実際やってみるとかなり難しいです。
解決策
いくつかの方法が考案されています。
前者の方法は、グローバルホットキーとしてESCにIMEオフの機能を割り当てることで、インサートモードからノーマルモードへ切り替えるときにIMEをオフにしてしまうものです。この方法の欠点は、再びインサートモードへ切り替えた際にIMEをオンにする操作は自分でやらなければいけないことです。
後者の方法は、端末の機能をつかってVim側から能動的にIMEの状態を切り替える方法です。インサートモード→ノーマルモード→インサートモードと切り替えたときにIMEの状態を復元できます。この方法の欠点は、IME制御機能を備えた端末を使う必要があることです(この機能をサポートしている端末はあまり多くありません)。
- Tera Term: 恐らくOK
- RLogin: 恐らくOK
- mintty 3.6.0: OK
- Windows Terminal 1.13.1143: NG
個人的には、後者の方法でIME状態復元をするほうが好みでした。ただし、Neovimの場合や、tmuxの中で起動している場合に罠があります。以降で罠の回避方法を述べます。
IME制御のエスケープシーケンス
重要なのは以下の4つです。
\e[0t
: IMEオフ\e[1t
: IMEオン\e[s
: IME状態保存\e[r
: IME状態復元
これらを、Vimのモード切替時に端末に書き出せばいいわけです。
Vimの場合は、参考元ページに有るように、t_SI
、t_EI
に設定すれば作動します。
set t_SI+=^[[<r
set t_EI+=^[[<s^[[<0t
set t_te+=^[[<0t^[[<s
Neovimの場合
Neovimはt_SI
やt_EI
はありません。代わりに、chansend
でstderr
に書き出します。
augroup restore-ime
autocmd!
autocmd InsertEnter * silent call chansend(v:stderr, "\e[<r")
autocmd InsertLeave * silent call chansend(v:stderr, "\e[<s\e[<0t")
autocmd VimLeave * silent call chansend(v:stderr, "\e[<0t\e[<s")
augroup END
tmuxの場合
tmuxの中でVimを起動した場合、いくつかの例外を除いて、エスケープシーケンスはtmuxでハンドリングされて捨てられます。ただし、強制的に端末まで届ける手段が用意されています。なお、バージョン3.3からは、allow-passthrough on
を明示的にセットしないと機能しません(デフォルトはオフ)。
具体的には、\e
を\e\e
にして、\ePtmux;
と\e\\
ではさみます。したがって、Neovimの場合は以下になります。
augroup restore-ime
autocmd!
autocmd InsertEnter * silent call chansend(v:stderr, "\ePtmux;\e\e[<r\e\\")
autocmd InsertLeave * silent call chansend(v:stderr, "\ePtmux;\e\e[<s\e\e[<0t\e\\")
autocmd VimLeave * silent call chansend(v:stderr, "\ePtmux;\e\e[<0t\e\e[<s\e\\")
augroup END
tmuxの中かどうかで分岐するのもよいでしょう。
augroup restore-ime
autocmd!
if exists("$TMUX")
autocmd InsertEnter * silent call chansend(v:stderr, "\ePtmux;\e\e[<r\e\\")
autocmd InsertLeave * silent call chansend(v:stderr, "\ePtmux;\e\e[<s\e\e[<0t\e\\")
autocmd VimLeave * silent call chansend(v:stderr, "\ePtmux;\e\e[<0t\e\e[<s\e\\")
else
autocmd InsertEnter * silent call chansend(v:stderr, "\e[<r")
autocmd InsertLeave * silent call chansend(v:stderr, "\e[<s\e[<0t")
autocmd VimLeave * silent call chansend(v:stderr, "\e[<0t\e[<s")
endif
augroup END
Neovim logo by Jason Long, CC BY 3.0