IMEでの状態の扱い

はじめに

sennという名前のWindowsで動くIMEを個人で開発しています。 IMEの状態の扱いがなかなか複雑なので、ここでメモしてみます。

stateful-ime

sennにはstateful-imeというオブジェクトがあります。キー入力の処理等、ユーザーと対話するのはこのstateful-ime (の派生) です。 stateful-imeは、stateとstatelessな機能の組み合わせです。 stateは画面の状態等、いろいろな状態を保持しています。

stateful-imeはstateの保持、更新を担当するのみで、具体的な機能はstatelessのほうが担当します。 stateful-imeのメソッドが呼び出されると、引数と保持している状態とを合わせてstatelessな機能に処理を委譲します。 stateful-imeはstatelessから返り値と新しい状態を受け取り、受け取った状態で現在の状態を更新したのちに呼び出し側に返り値を渡します。

たとえば、下はprocess-inputの定義です。このメソッドはユーザーのキー入力を処理します。

(defun process-input (stateful-ime key)
  (with-accessors ((input-state state-input-state)
                   (input-mode state-input-mode))
      (stateful-ime-state stateful-ime)
    (let ((result (senn.win.im.process-input:execute
                   stateful-ime input-state input-mode key)))
      (destructuring-bind (can-process view &key state)
          result
        ;; update application state
        (when state
          (setf input-state state))
        (format nil "~A ~A~%"
                (if (and can-process view) 1 0)
                (or view ""))))))

stateful-imestateful-ime-stateで自身の状態を取り出し、statelessなsenn.win.im.process-input:executeに渡します。 senn.win.im.process-input:executeが新しいinput-stateを返却すると、それでstateful-imeの状態を更新します。 (format nil ...の部分は返り値で、この情報はC++実装のフロントエンドが画面を更新するために利用します。

テストを書くときは、このstateful-imeprocess-inputに対してのみテストを書けばよく、senn.win.im.process-input:executeのテストは書かなくていいかなと思っています。 stateful-imeは、ユーザーと対話しているため、ユーザーの行動をテストで模擬し、ちゃんと対話できているのかを確認するのは意味があると思っています。 一方、senn.win.im.process-input:executeは内部処理であったり、stateの定義変更により影響を受けやすいため、stateful-imeほどテストする意味はないと思っています。 stateful-imeは状態をカプセル化しているため、stateの定義が変わっていてもテストケースは変化しないはずです。

おわりに

以上、IMEの状態の扱い方の方針でした。