Hatena::ブログ(Diary)

日記

2012-10-30

WindowsのSublime Text 2 でIMEのインライン変換できるようにしたのでまとめ

現在の Windows用の Sublime Text 2 は IME のインライン変換の入力文字がウィンドウの外とか、おかしな場所に表示されてしまう問題があります。これを、正しくカーソル位置に表示するプラグインを作りました。詳細は github に置いたリポジトリのREADMEを見てください。

no title

このエントリーでは、実装に使った手段を書いておきます。

インライン変換の実装に必要だったのは以下の3点です。

  • 変換ウィンドウの位置指定
  • 変換開始イベントのハンドリング
  • 変換ウィンドウの位置計算

変換ウィンドウの位置指定

インライン変換の表示位置がおかしいのは、Sublime Text 2が表示位置を指定していないためです。 これは、単に Win32 APIの ImmSetCompositionWindow を呼び出せば対応できます。そして、Sublime Text 2が搭載するPython処理系は、当然ctypesを同梱しているので、Win32 APIを呼び出すことができます。

これに気づいたので、プラグインを作り始めました。

変換開始イベントのハンドリング

ImmSetCompositionWindow を呼び出すだけ、と言っても、IMEの変換開始時に適切なタイミングで呼び出す必要があります。 具体的には、ウィンドウメッセージとして WM_IME_STARTCOMPOSITION が渡ってきた時に呼び出します。

ウィンドウメッセージを処理するには、ウィンドウプロシージャを実装する必要がありますが、普通はすべてのメッセージは Sublime Text 2 が処理してしまいます。これをプラグイン側で処理できるようにするために、ウィンドウのサブクラス化という方法を使いました。

独自のウィンドウプロシージャをPython関数として実装して、それをC言語関数ポインタとして扱えるように変換し、 SetWindowLongW 関数を使ってウィンドウプロシージャの関数ポインタを置き換えます。元のウィンドウプロシージャのポインタは保存しておいて、IMEに無関係のメッセージは元のウィンドウプロシージャで処理させます。

変換ウィンドウの位置計算

これが一番大変でした。ウィンドウの左上からカーソル位置までの距離を ImmSetCompositionWindow に渡す必要があるのですが、Sublime Text 2にそれを直接取得するAPIは存在しないので、いろいろ計算する必要がありました。

(あとで詳しく書く)

補足

Win32 APIの幾つかは、ウィンドウハンドル(hwnd)を引数として取ります。これは Sublime Text 2の Windowクラスのhwndメソッドを呼び出して取得できます。なおhwndは公式ドキュメント(http://www.sublimetext.com/docs/2/api_reference.html#sublime.Window)に記載されていないメソッドです。

今後の予定

  • IMEのON/OFFを制御できるコマンドを追加する予定です。ただ、なぜか ImmSetOpenStatus を呼び出すと "[Error 126] 指定されたモジュールが見つかりません。" というエラーが出てしまって実現できていません。
  • http://d.hatena.ne.jp/topiyama/20070703/p1 に書いてあるような、 IMEの前後参照変換機能 に対応する予定です。