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_SIt_EIに設定すれば作動します。

set t_SI+=^[[<r
set t_EI+=^[[<s^[[<0t
set t_te+=^[[<0t^[[<s

Neovimの場合

Neovimはt_SIt_EIはありません。代わりに、chansendstderrに書き出します。

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