jsx-mode.el において非同期で補完候補を取得するようにしてみた

先日 develop ブランチに JSX の補完機能と連携する機能を取り込みました。
jsx-mode.el に補完機能を実装しました - あらびき日記

っで、「jsx の –complete オプションが重いって愚痴ってる暇があったらエディタ側で最大限努力しろや」的なことを言われたのでデモ的に実装してみました。

https://github.com/jsx/jsx-mode.el/tree/feature/auto-complete-async

auto-complete コマンドは同期的に行われますが、自動補完は非同期で補完候補を取得するため、入力中にカクカクするということはありません。

実装メモ

start-process でプロセスを実行することで非同期になっています。
set-process-sentinel でプロセスが終了した時に呼ばれるコールバック関数を指定できるようなので、そのコールバック関数でキャッシュをセットしています。
キャッシュがセットされていれば補完候補としてそのキャッシュを返すようにしています。

問題はキャッシュをいつクリアするかで、面倒だったのでとりあえず ac-init に次のような advice を設定しています。

(defadvice ac-init (before jsx--ac-init activate)
  (when jsx--candidates-cache
    ;; ac-init is called only once after the cache is set.
    ;; The cache should be cleared next time ac-init is called
    (if (and (not jsx--clear-candidates-p) (eq jsx--ac-point ac-point))
        (setq jsx--clear-candidates-p t)
      (setq jsx--candidates-cache nil)
      (setq  jsx--clear-candidates-p nil))))

つまり、キャッシュがセットされてから2回 ac-init が呼ばれるか、キャッシュを作成する際の ac-point と現在の ac-point が異なる場合にキャッシュをクリアします。

実用レベルでは不具合は気にならないレベルだと思いますが、キャレットの位置を□で表すと、例えば

"".se□

と入力した後に素早く “^H^H,se” を入力すると

"",se□

の状態で search が補完候補として挙がります。
このように、キャッシュがセットされる前に ac-point と prefix が同じになるように変更を加えるとおかしな候補が表示されることになります。

この辺を完璧に実装しようと思うとかなり面倒です。
例えば

a.b.c
.d.e
.f.part□

みたいな状態でキャッシュをセットするまでの数秒の間に

b.b.c
.d.e
.f.part□

のように書き換えられる可能性があります。

また、

var a = { abcd: 1 };  // instance variable
(snip)
this.ab□

の状態で

var a = { abcc: 1};  // instance variable
(snip)
this.ab□

に書き換えられる可能性もあります。

まぁ極端な例ですが・・・。
あと、major-mode で advice 定義するのはどうなんですかね・・・。auto-complete.el では定義されてますが。
やろうと思えば他のスクリプトで定義されている関数の挙動を大きく変更することもできるわけで。

何はともあれ、JSX の補完機能の高速化が待ち遠しいですね!!!!