Vimで日本語を使っているとノーマルモード移行時に日本語入力状態のままだと ちょっと面倒なことになります。
そこで自動的にノーマルモードでは日本語入力をオフにしたいわけですが、 macOS SierraだとキーマップアプリのKarabinerが使えないために 中々上手いこと出来ていませんでした。
今回久しぶりにいろいろ試して取り敢えずなんとか出来たのでその方法について。
前に試した方法等
Karabinerが使える環境ではKarabinerを使って ターミナルアプリ上でESCが押された際に IMEがオンの状態だとESCと同時にIMEをオフにする操作を送ることで 日本語入力状態でESC(またはCtrl-[)を押しても IMEがオフの状態でノーマルモードに入れる様にしていました。
Windowsでも同じようなことをAutoHotKeyを使って実現しています。
ただSierraではKarabinerがまだ使えない状態なので他の方法を探して、 以前はBetterTouchToolでESCにAppleScriptを割り当て、 そのAppleScriptの中でキーを送ることでなんとかしようとしました。
ですがタイムラグが出るのと押すタイミング等で効かない状態に陥ったりして 不安定だったので辞めていました。
この辺、基本的にはVim独自と言うより、 ターミナル環境、またはシステム全体でESCをラップして 入力切り替えのコマンドを送ろうとしています。
新たな方法
VimでESCを押した時にIME OFFコマンドも送る
今回試した方法はVimの中でインサートモード中にESCが押された時に IMEの操作を行ってみることです。
まず、実現するにはコマンドラインからIMEがオフに出来ると色々できそうなので 以下の様なAppleScriptを考えてみます。
1 2 3 4 |
|
上で書いた以前試した方法の時のImeOffEsc.scptをシンプルにしたもので、 単にEISUを送るだけのスクリプトです。
これを実行するとどこでも常に英数入力状態になります。
英数キーのキーコードを使っていますが、USキーボードなど英数キーが無いキーボードを使っていても大丈夫です。
コマンドラインからでも
$ osascript ImeOff.scpt
とすればIMEが切り替わります。
取り敢えずコマンドラインからやってみると 特にタイムラグなどは気にならない感じです。
さらに、これをスクリプトを作らずに
$ osascript -e "tell application \"System Events\" to key code 102"
として呼ぶ事も出来ます。
これをVimの中で使うために以下の様な設定を.vimrcの中に加えます。
1 2 3 4 5 6 7 |
|
まず、Mac専用なのでMacの場合だけ。
後はコマンドを用意して、autocmd
で
InsertLeave
時に呼ぶようにしています。
これで取り敢えず日本語入力中にESCなどで ノーマルモードに戻るとき、IMEをオフにしてくれます。
前の方法に比べると大分副作用が小さく、上手く機能しています。
タイムラグを減らす
これで取り敢えずESCでインサートモードを抜ける時に IMEを確実にオフに出来る様になりました。
ですが、やってみるとIMEが切り替わるまでにちょっと時間がかかり、 ESCを押した後にJなどで移動しようとすると 日本語入力が始まってしまいおかしな表示になり、 場合によってはIMEの切り替えが元にもどってしまったりします。
これはVimのtimeoutlen
、ttimeoutlen
というオプションの値に関係があります。
timeoutlen
はキーマッピングでvl
等、v
を押した後にl
を押す、みたいな
マッピングがある時にv
を押してから次の入力を待つ時間。
ttimeoutlen
はエスケープシーケンスな入力、例えば
矢印上キー等の入力: OA
(ESC+O+A)が来た時にそのシーケンスの入力を待つ時間です。
値はミリ秒設定でtimeoutlen
が1000、ttimeoutlen
が-1になっていますが、
ttimeoutlen
がマイナスの場合はキーシーケンスの待ち時間はtimeoutlen
のものを使います。
従って、ESCキーを上の様にマッピングすると、
デフォルトではtimeoutlen
の1000ミリ秒(1秒)待ってから
そのマップされたものが実行される形になります。
もともとのESCでも 1秒の待はあるのですが、ESCがマップされてない状態だと他のキーを入力した瞬間に キーシーケンスとは捉えられずにノーマルモードに戻ってそのキーのコマンドを実行するようになっています。
これに関連してちょっと謎なのは、Vimの中で矢印キーのキーを見たい時、
Ctrl-Vを押してから矢印キーを押すと上の様にOA
が表示されるわけですが、
コマンドラインなんかだと[A
になります。O
でなくて[
。
実際、コマンドラインでは(ESC+[+Aというシグナルで上キーを入力出来、
一方VimではESC+O+Aをしても上手く上移動になりませんが、
矢印入力が上手く行かないときにこのキーに<Up>
などをマップしたりすることがあります
1。
ともかく、ESCを押した後の待時間を減らすには
set ttimeoutlen=1
みたいな設定をします。
timeoutlen
の方は短くしてしまうと複数キーマップしたものが入力出来なくなるので
別途ttimeoutlen
で設定すべきです。
こちらの方は短くしても特にカーソルキーでの入力などには問題はありません。
また、これらの設定はtimeout
やttimeout
という設定項が無効になっていると
そもそも待ち時間設定関係なく延々と待ち続ける様な設定になりますが、
デフォルトでtimeout
はオンになってるので敢えてオフにしてない限りは設定し直す必要はりません。
timeout
がキーマップ用、ttimeout
がキーシーケンス用ですが、
ttimeout
の方の設定に関わらずtimeout
が有効であれば常にキーシーケンスの方も
タイムアウトが有効になる設定になってるので、両方有効にしたいのであればttimeout
の方を直接設定する必要はありません。
もし、この設定をしても1秒の待ち時間が変わらない様であれば、 ESCに関するマッピングがある可能性があります。
:imap <ESC>
などしてマップされてるものが無いか確認して必要なければ.vimrcなどから削除します。 (プラグインなどで自動で設定されてる可能性もあります。)
最終的にはこんな感じ。
1 2 3 4 5 6 7 8 9 |
|
ノーマルモード/ビジュアルモード時にも間違えて日本語になってる場合に
ESCで強制的に日本語になるようにnoremap
の設定も加えてあります。
ただし、下にも書くようにもしノーマルモード時にカーソルキーを使って移動してる様な場合は 設定するとカーソルキーが使えなくなるので辞めてください。 もしくはちゃんとhjklを使ってください。
ttimeoutlen
に関してはここに入れずに通常の設定として入れておいても問題ないかも。
その他色々試したこと
InsertLeave
を使う代わりにinoremap
でnoremap
みたいに直接キーマッピングを
変更する方法もあるかと思います。
inoremap <silent> <ESC> <ESC>:call system(g:imeoff)<CR>
みたいな感じ。もしくは
inoremap <silent> <ESC> <C-o>:call system(g:imeoff)<CR><ESC>
やってみた感じはInsertLeave
と変わらない感じでした。
ただし、この様にESCをマップしてしまうと
カーソルキーの入力がA
とかの入力になってしまいます。
なのでカーソルキーを使う場合にはInserLeave
を使うしかありません。
また、
inoremap <silent> <C-j> <ESC>:call system(g:imeoff)<CR>
みたいにESC以外にマップする方法もありますが、
これをするとttimeoutlen
の設定をしなくても即座にimeoff
が呼ばれて良いです。
ttimeoutlen
を1(もしくは0)に設定しても僅かにタイムラグがあります。
即座にjなどで移動しようとすると日本語が入力されてしまったりします。
なのでESCを使わずに他のキーでノーマルモードに戻る設定をしてたりすると それを上の様なマップに切り替えることでより快適に切り替えが出来るはずです。
よくあるのがjj
とか、後はちょっと挙動の違うCtrl-Cを
ESCにしてしまうとかでしょうか。
私自身はCtrl-[を常にESC変わりに使って居ますが、
Vimの中ではこのキーはESCと全く同じ扱いなので
上記のttimeoutlen
等が必要になります。
この方法を試すにあたって
Sierraになってから上にも書いたように一度試してだめだったわけですが、 その後も何度か試したりしています。
そんな中で、他の人も試してる人が居て、
このポストを見てVimの中でやることを参考にしました。
ただ、ここで使われているswim
と言うコマンドはGoogle日本語入力だと
上手く扱えなかったので断念。
コマンドラインから使える他の方法を探してた所、
と言うちょっと古いものをみつけて試してみたところ、 SierraでもGoogle日本語入力の操作も一応出来ているようでした。
ただ、何故かターミナル上からコマンドを送るとIMEの表示は切り替わるのですが ターミナル上では日本語入力のままになり、さらに何故か一度他のアプリに切り替えてから すぐにターミナルに戻ると英数入力にきちんと変更されてる、という状態。
これだとVimのなかからコマンドを送っても上手く行かないので断念。
で、ちょっと自分の前のポストを見てたらAppleScriptで簡単に出来るな、ということで やってみた次第です。
まとめ
今回の方法は以前試した方法よりは上手くいっています。
ただ、ちょくちょく上手くimeoffが呼べずに日本語のまま残っていたり、 微妙なタイムラグが残っていてESCを押した直後に他のキーを押そうとすると 日本語が残ってしまったり変なキーが入力されたりしてしまうことがあります。
特に、インサートモードに入ってIMEをオンにして、そのまますぐにノーマルモードに戻ろうとすると 何故か必ず日本語のまま残ってしまいます。 ちょっとでも文字を書いてからだと大概上手く行くのですが。
でも無いよりマシかな、という程度にはなったのでしばらくは使っていこうと思います。
最近この設定無しでしばらく使ってきたので、日本語からノーマルモードに戻る時に すぐにIMEの切り替えキーを押す癖も付いてしまっていますが、 必要なければそれだけ速く打てることは打てるので。
後は
$ osascript -e "tell application \"System Events\" to key code 102"
のワンライナーで特に余計なものを入れずに IMEをOFFにできるのを覚えておくと色々使えそう。
追記: 2017/03/12
Google日本語入力+iTerm2限定になりますが、 もっと良い方法が見つかりました。
追記ここまで