Emacs+AutoCompleteでTypeScriptの補完をしたい
最近型の無い世界が身近になりつつあるderuiです。型のある世界が好みなんですが、仕事なんでそうも言ってられないというのが。
まぁつまりはJavaScriptなんですけど、個人的にはJavaScriptは柔らかすぎて好みじゃないです。色んな人がそう感じているから、AltJSという分野がホットなんでしょうけど。
さて、最近個人的にTypeScriptを勉強がてらなんか書いてみています。当然ながらEmacsで書いていますが、TypeScriptのシンタックスハイライトについては、Vim/Sublime Textと併せて提供されているのですが、肝心の補完については、提供されている中には含まれていません。
変わって会社では学校を卒業して以来初めてVisual Studioをインストールして、こっそりとTypeScriptを書いています。Visual Studioの強力なインテリセンスが使え、オートフォーマットがかなり賢いため、かなりサクサクを書いていくことができます。小規模なら大丈夫ですけど、ある程度の規模になるとインテリセンス的なものが効かないとかなり辛いです。
というわけで調べてみると、VimとEmacsですでにやられている方がいました。
http://d.hatena.ne.jp/MATSUZAKI/20121111/1352643697
http://vividcode.hatenablog.com/entry/ts/how-to-completion
どうもtypescript-toolsというものがあるらしく、それを利用すれば、インターフェースだけ作ってやればなんとかなりそうではあります。まぁせっかくなので、node.jsを使ってみるというのもあわせて、TypeScriptでサーバを書いてやってみました。
なお、AutoCompleteとのインターフェースの大半は、こちらのソースコードをかなりパク・・・ゲフゲフ参考にさせていただきました。
https://github.com/derui/ac-typescript
作りとしては、最初にac-typescript/start-serverで、node.js上でTypeScriptのLanguageServiceを組み込んだサーバーが起動します。あとはそれに対してcurlで都度リクエストを投げていっています。本当は補完候補の取得までを非同期でやりたかったんですが、auto-completeの補完候補はどうも非同期で出すなんてことができなさそうだったので、同期にしてます。
ただ、現在の作りとしては、開いていったTypeScriptを、サーバー内部で持っているLanguageServiceに追加し、更新されたら更新分を通知、ということをやっていますので、わりかし更新に追従でき・・・てません。ある程度は追従してくれるのですが、Hoge.の.を入力した直後とかでは、サーバー側が更新されていないため、どうにもうまくいかない場合が多いです。逆に上手くいく場合もあるので、LanguageServiceの動作がいまいち把握できていません。まぁ、超開発版ですので、上手くいかないのがデフォルトみたいなものです。普通
にインテリセンスを使いたい方は後述のものと、前述の方が公開されているインターフェースを使った方がいいと思います、ホント(マテ)
うん、まぁ普通にtypescript-toolsを使った方がいいんじゃまいかと思います。実装は見ていませんが、TypeScript Service Serverということで、多分基本は一緒みたいですし、機能も豊富です。
もちろん高速化する余地はいくらでもあると思います。現在は、サーバーからjsonで送られてきたものをemacsのjsonライブラリで変換しているのですが、大量の候補になったりすると、他の処理との兼ね合いとかでmax-lisp-eval-depthのエラーが発生したりします。emacsなら、S式で受け取ってevalするのが一番だとは思っていますが・・・。
また、curlでリクエストを投げるとき、deferred.elを使って非同期にしています。このため、TypeScriptファイルを開いたときと、アイドル時の更新はほとんど固まりません。候補取得も非同期化してやった方がいいのかもしれません、が、そもそもこれはできなさそうな気もします。
何より一番重いのは、サーバー内でLanguageService.getCompletionsAtPositionを呼び出しているときです(多分)。これを軽くできれば、他で小細工するよりもよっぽど速くなると思います。
機能面で一番問題なのは、現在の入力内容とサーバー側が同期していないため、大抵候補がちゃんと出ない、という部分です。致命的ですね(ぉぃ。
これをなんとかするには、サーバー側に逐一更新内容を送信してやる必要があります。一回AutoCompleteのpre/post-command-hook的なものにadviceをひっかけて、都度送信するようにしてみたのですが、
- 非同期にすると重くはないが、順序が保証されない
- 前とテキストがどう違っているのかをちゃんと送ってやらないと、サーバー側で持ってるスクリプトの情報がえらいことになる
- 一処理ごとに挟むには処理がちと重たい
- テキストの前後比較をやる手段が・・・
などなどの問題が発生したため、上述のリポジトリにコミットしているelispには含んでいません。
ほぼネイティブでガリガリ動くVisual Studioと、Emacsとを比較してもしょうがないんですが、こういう点ではやっぱりIDEが優れているなぁ、と思います。でもなんとかやっていきたいですねぇ。