📝

VimのIM制御(iminsert)の振る舞い

2023/09/06に公開

このポストは以下のポストへの補足です。

https://swnakamura.github.io/posts/vim-japanese-input/#jiao-zhu

iminsertというオプションで制御することになっており、ヘルプ曰くこの値を2にするとIMEを制御できる……とあるがこれは嘘

実はVimのヘルプには 'iminsert' でIMEの状態を「制御できる」とまでは書いていません。ちょっと引用してみましょう。

Specifies whether :lmap or an Input Method (IM) is to be used in Insert mode.

実際の動作を加味しつつ翻訳すると「インサートモードでlmapもしくはInput Method(以下IM)が使われていたかを指定する」となります。

では 'iminsert' とその周辺オプションの振る舞いと成り立ちと意味を見ていきましょう。加えてVimにおけるIMの制御に必要な操作を示します。

'iminsert'とは

実際の'iminsert'には、最後にインサートモードでlmapもしくはIMを利用していたかを記憶します。Vimがインサートモードを抜けノーマルモードに戻る際に、lmapが使われていたら1を、IMが使われていたら2を、どちらでもなければ0が 'iminsert' へ自動的に設定されます。インサートモードを抜ける際には、'iminsert'へ値を設定するのに加えてlmap及びIMの無効化します。そうしてインサートモードに再度入る際に'iminsert'の値に応じて、lmapもしくはIMを有効にします。

同じことが'imcmdline''imsearch'にも言えます。前者はコマンドラインモード、後者はサーチモードにおける最後のIMの状態を記録するオプション、というよりも変数といったほうが適切かもですね。

この一連の振る舞いで、ノーマルモードに戻る時にIMを無効化し、該当するモードに再び入る時に前回の状態に基づいてIMEを有効化するというのが、Vimが意図している振る舞いです。しかしこれが機能するのはごく一部の環境(WindowsのGvimのみ)に限られます。

IM制御とその歴史

VimによるIM制御に必要なAPIを考えてみましょう。まず現在のIMの状態、すなわち日本語入力がオンかオフかを取得するAPIが必要です。次にIMの状態を設定するAPIが必要です。必要なのはこのたった2つのAPIです。

WindowsにおけるIM制御

しかしこの2つのAPIをOSの責任として長年変わらずに提供できているのはWindowsだけです。またそのAPIはコンソールからは容易には利用できません。そのためWindowsのGvimだけがIM制御を、本来の意図通りに一切の設定なしに完璧に、行えます。

X11によるIM制御

過去のLinux (UNIX)においてはX11の一部であるXIMがその責を担っていました。しかしXIMは定義が不十分で、特にIMの状態を設定する術が存在しませんでした。XIMクライアントであるkinput2の新し目のバージョンでは、代替手段として変換コンテキストを作り直すことでIMをオフにし、IMをオンにするためのキーを疑似的に送信することでIMをオンにするという力技がありました。その「IMをオンにするためのキー」は人によって異なりますから、それを指定するための'imactivatekey'というVimのオプションが用意され、今もその名残を残しています。

余談: macosにおけるIM制御

macosについては、OS Xの時代には完全に制御できるパッチもあったはずなのですが、その後どうなったのかはわかりません。OSのUIやIMの仕組みも変わっているだろうし、素のままだと機能しない可能性が高いと考えて良いでしょう。

現代LinuxにおけるIM制御

現代のLinuxにおいてIM制御は UIM, IBus, Fcitx あたりが担うことになります。更に言えば利用している入力エンジン自身が持つ制御機構を使う方法もありえます。こうバリエーションが増えてくると、Vimとしてはいちいち全部に対応するのは無理だよねとなりました。そこで前述の2つのAPIをユーザーが選択・設定できるようにしよう、ということで設けられたのが'imstatusfunc''imactivatefunc'の2つです。

'imstatusfunc'には現在のIMの状態を取得するVimスクリプトの関数を指定します。スクリプトのなかで別コマンドを実行するなり、if_xxxを利用してDBus通信をしたり、任意の方法でIMの状態を取得できます。

'imactivatefunc'にはIMの状態をオン・オフに切り替えるVimスクリプトの関数を指定します。同様に任意の手続きを実行することができますが、引数の値に応じて正しい状態を設定する必要があります。

またコレらの関数を使えば、Vimにパッチを当てることで、GUI上のターミナル(CUI)でもIM制御を利用できます。なおこのパッチはちょっと古いので現行の最新のVimにはそのままでは適用できない可能性があります。

各IMの制御法

ではスクリプトを用いてIMを制御する際に、前述のVim側の事情を踏まえた上で、何が必要かを簡単に見ていきましょう。

UIMの制御法

UIMでは特定のソケットとの通信でIMの状態の取得、変更が行えます。ちょっと古い実装なので、今も動くかはわかりませんが、参考までに自作のプラグインを紹介しておきます。
https://github.com/koron/imcsc-vim/tree/master/uim-ctlso

IBusの制御法

正直な話、IBusはあきらめた方が良いです。資料として、以下にIBus用の自作プラグインを紹介しますが、現在では機能しないことがわかってます。
https://github.com/koron/imcsc-vim/tree/master/ibus-python

機能しなくなった際の経緯は以下のポストを参照してください。ちょっとタイトルが煽っているのは、若気の至りということで御容赦を。
https://www.kaoriya.net/blog/2013/10/18/

Fcitxの制御法

FcitxはDBusソケット経由でIMの状態を取得・制御できます。そのため自作プラグインではif_pythonを用いてDBusソケット通信をすることで、FcitxのIMを制御しています。以下を参照してください。
https://github.com/koron/imcsc-vim/tree/master/fcitx-python

余談:IIIMFへの期待と現実

XIM自体、定義が不十分で、特にIMの状態を設定する術が存在しませんでした

実はXIMに変わるIM制御の規格が過去には提案されていました。それがIIIMFです。XIMの使えなさに辟易していた私は、IIIMFの存在を知った時に「これでUNIXのVimでもまともなIM制御が実現できる」と大きく期待しました。

「でも、そうはならなかった」
「ならなかったんだよ」
「だから、この話はここでお終いなんだ」

IIIMFの詳細については以下を参照してください。
https://ja.wikipedia.org/wiki/IIIMF

まとめ

'imstatus'への誤解を訂正するために、その動作、歴史、現状を説明しました。このあたりのVimでの苦悩は、Webブラウザの開発でもしばしば参照されていたようです。Webのテキストフォームには英数字しか入力できないような制限が付けられ、その実装に同じように苦慮している歴史が垣間みえます。

願わくば、IIIMFのような統一仕様が制定され、広く利用されるようになって欲しいですね。

変更内容

2024-01-25

  • Fcitx の制御方法についての言及を実態にDBusからソケットへ変更しました (記憶と違ってた)
  • 一部助詞がおかしかった箇所を修正しました
  • 読みにくかった箇所を修正しました

Discussion