1. Quramy

    Posted

    Quramy
Changes in title
+TSServerの使い方メモ
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,173 @@
+
+# はじめに
+
+最近, TypeScript 1.5関連のエントリが少しずつ上がってきてるけど, このエントリはその中で最も誰得?となること間違いなし!
+
+最初に断っておくが、このエントリを読んで得するのは、これから何かしらのエディタ(EclipseとかEmacsとか秀丸とか自分の信じている神に従え)でTypeScript向けのPluginを作ろうと思っている人限定である。
+「他人の作ったpluginなんて使う気にすらならないぜ!自分で実装するぜ!」って奴は, こんなもん読まなくても自分で何とかしそうな気がプンプンする.
+
+さて, 先日 [別のエントリ](http://qiita.com/Quramy/items/e0f004695e8bcda7604e)にて記載したが, TypeScript v1.5.0 alpha版公開に合わせて, [TSServerを利用したVim plugin tsuquyomi](https://github.com/Quramy/tsuquyomi)を作成&公開した.
+
+このエントリでは, tsuquyomiを作成する上で身についたTSServerの基礎知識を、備忘録も兼ねて書き連ねていこうと思う.
+
+# TSServerとは
+
+TSServerはTypeScript 1.5からbundleされたエディタ向けのサーバであり, `tsserver` コマンドで動作する.
+[本家のwiki](https://github.com/Microsoft/TypeScript/wiki/Architectural-Overview) にも記載がある通り, TSServerはLanguage ServiceにエディタやIDEがアクセスするためのアダプタの位置づけである.
+
+詳細な機能は後述するが, TSServerを使うと下記機能をエディタから利用可能となる:
+
+* 編集しているソースのcompile error取得
+* ClassやInterfaceの定義情報取得
+* リファクタリング(識別子の名称変更)
+* 補完候補の取得
+* etc...
+
+特徴的なのは, APIのインターフェイスに標準入出力を採用しているため, エディタ/IDEの側ではプロセスとSTDIN/STDOUTを扱う仕組みさえあればよく, Node.jsによる実装は一切必要ないという親切設計な点.
+
+## TSServerを利用した各種Plugin
+
+* [TypeScript-Sublime-Plugin](https://github.com/Microsoft/TypeScript-Sublime-Plugin) : Sublime向けplugin. MicroSoft謹製.
+* [tsuquyomi](https://github.com/Quramy/tsuquyomi) : 拙作Vim plugin.
+
+
+# TSServerの使い方
+
+## メッセージの書式
+Requestは, 次のようなJSONフォーマットで組み立ててTSServerの標準入力に流し込む形となる.
+
+```json
+{"type": "request", "seq": 0, "command": "open", "arguments": {"file": "sample1.ts"}}
+```
+
+* `type` : `"request"` 固定. 省略しても動く.
+* `seq` : リクエストのシーケンス番号. クライアント側で適宜インクリメントして利用する. 省略しても動く.
+* `command` : requestの種別. 利用可能な文字列は後述.
+* `arguments` : commandの引数. command毎に異なる.
+
+## 実行例
+
+うだうだ書くより実例で示した方が早いのでサンプルをば.
+
+まず, TSServerに読み込ませるサンプルのソースコードを作成する.
+
+```ts:sample1.ts
+module SampleModule {
+ export interface ISome {
+ name: string;
+ }
+
+ export class SomeClass implements ISome {
+ constructor(public name: string) {}
+ }
+}
+```
+
+次に, TSServerの命令を用意する(インタラクティブにやってもいいけど,面倒なのでファイルにしておく)
+
+```json:definition.json
+{"type": "request", "seq": 0, "command": "open", "arguments": {"file": "sample1.ts"}}
+{"type": "request", "seq": 1, "command": "definition", "arguments": {"file": "sample1.ts", "line":6, "offset":37}}
+```
+
+内容である程度察しが付くと思うが, 上記のcommandは下記を表している.
+
+1. open command で先に用意したsample1.tsを開かせ,
+1. definition commandで sample1.ts上の6行37列目に書いてある `ISome`の定義箇所を取得.
+
+これをtsserverで実行すると,
+
+```bash
+tsserver < definition.json
+```
+
+下記の標準出力が得られる.
+
+```text
+Content-Length: 171
+
+{"seq":0,"type":"response","command":"definition","request_seq":1,"success":true,"body":[{"file":"sample1.ts","start":{"line":2,"offset":3},"end":{"line":4,"offset":4}}]}
+```
+
+interface `ISome`の定義は, sample1.tsファイルの2 行3 列目〜4行4列目にありまっせ、と正しく定義箇所情報が返却された.
+
+## APIs
+
+[TypeScriptレポジトリのsrc/server/session.ts](https://github.com/Microsoft/TypeScript/blob/master/src/server/session.ts#L78)を読むと一覧が載っているが, TSServerに用意されたcommandは以下の通り:
+
+| command | type | 機能概要 |
+|:---|:---|:----|
+| change | none | ファイルの変更箇所をTSServerに通達する. |
+| close | none | TSServerでファイルを閉じる. |
+| completions | response | ファイルのカーソル(lineとoffsetで指定)位置における補完可能な単語の一覧を取得. |
+| completionEntryDetails | response | completions commandで取得した補完情報の詳細を取得. |
+| signatureHelp | response | ファイルカーソル上のメソッド呼び出しについて, 引数情報等の詳細を取得. |
+| configure | none | インデント時のtab幅等, フォーマットに必要な設定を行う. tsconfigやcompilerOptions的な内容ではないので注意. |
+| definition | response | ファイルのカーソル上のsymbolについて, そのsymbolの定義箇所を取得する. |
+| format | response | ソースをフォーマットする?使ってないのでよくわからん |
+| formatonkey | response | これも使ってないのでよく分からん. |
+| geterr | event | コンパイルエラー情報を取得する. 現状, 唯一のevent発火API(後述). |
+| navbar | response | ファイルの見出しを作成する. IDEのサイドメニューに表示するような一覧情報を作るのに便利. |
+| navto | response | キーワード検索を行う. |
+| open | none | ファイルをTSServer上で開く. |
+| quickinfo | response | ファイルのカーソル上の情報を取得. |
+| references | response | ファイルのカーソル上のsymbolについて, そのsymbolを参照している箇所を既にTSServerでOpenしたファイルから検索し, 参照箇所の一覧を返す. |
+| reload | none | ファイルをリロードする. change と違い, 新ファイルで旧ファイルの内容を全置き換えするイメージ. |
+| rename | response | ファイルのカーソル上に存在するsymbol(identifier)を別名に置き換える場合に, 同時に置換すべき箇所の一覧を返す. リファクタリング用の機能を作るのに便利. |
+| saveto | none | デバッグ用command. TSServerが認識しているfileの内容をダンプする. reload, change commandで, TSServer上のファイル内容を変更しまくった時, 「あれ, TSServerが認識しているファイル内容ってどないやねん」ってなったときに利用する. |
+| brace | response| ファイルのカーソルから見て, brace matchingする箇所を返す(`{`に対応する`}`の場所等). 一般的なエディタであれば, デフォルトで持っている機能であり, いらない子. |
+
+typeに"none"と記載のあるcommandについては, commandを実行しても何のresponseも出力されない. 上手く処理されているかどうかしりたければ, ログ(後述)に頼るしかない.
+
+各commandのarguements, response bodyについては, [src/protocol.d.ts](https://github.com/Microsoft/TypeScript/blob/master/src/server/protocol.d.ts) を見れば完全な情報が記載されているので参考にされたし.
+
+## 実用時のcommand flow
+
+実際にエディタ向けのpluginを作成する場合は, 下記のようなフローに従う.
+
+1. TSServerのプロセスを立ち上げる.
+1. oepn commandで対象の.tsファイルをTSServer上で開く.
+1. definitionやcompletions commandでTSSserverから情報を引っこ抜く.
+1. エディタ側で対象の.tsファイルを編集したら, reload or change commandで変更内容をTSServerに通達.
+1. 編集が完全に完了したらclose comanndでファイルを閉じる.
+1. (エディタ終了時等で)TSServerのプロセスを落とす.
+
+## 諸注意
+
+### ファイルパス
+上記の例では, 簡便のために相対パスで記載していたが, **`"arguments: {"file": "..."}}` については, 絶対パスで指定すること** .
+また, windowsで使う場合は, `\\` は `/`に置き換えておくこと.
+
+### tsconfig.json
+open command実行時に, 対象ファイルのbaseディレクトリからrootに向かって, `tsconfig.json`を探索する.
+このときにtsconfig.jsonが見つかれば, compilerOptionsが適用される.
+当然, geterr commandの結果に影響してくる.
+自分でtsconfig.jsonをopenしても意味はないし, reloadやchangeコマンドの実行時は探索されない.
+
+したがって, open commandの後に tsconfig.jsonを変更した場合, TSServerにtsconfigの変更を通知する術が現状存在しないため, close & openを実行するしかない.
+
+### geterr
+geterr commandは結果が非同期で返ってくる.
+従って, 先のdefinition.jsonをリダイレクトで食わせるような実行をしても, eventがemitされる前にprocessが落ちてしまう.
+また, このcommandは1リクエストに対して, "synatxDeagnostic"(文法エラー情報)と"semanticsDegnostic"(セマンティックエラー情報)の二つのイベントを発火させるため, plugin側で両方のイベント(標準出力)を監視する必要がある.
+
+# 困った時は
+1.5.0-alphaがリリースされる前からtsuquyomiの開発を始めていたというのもあるが, APIが思う通りの結果を返してくれず、うんうん悩む時間も多かった.
+困ったらログとデバッグですよねー.
+
+## ログ
+
+`process.env.TSS_LOG` でTSServerにログを吐かせることが出来る.
+
+```bash:TSS_LOGの設定例
+export TSS_LOG="-file `pwd`/tsserver.log -level verbose"
+```
+
+`-file` で出力先ファイルの指定, `-level`で出力レベルの指定が行える.
+
+## デバッグ
+ログでも追えないときは[node-inspector](https://github.com/node-inspector/node-inspector)を使ってブレークポイント仕掛けていけば何とかなる.
+
+# まとめ
+
+* TSServer使ってイカしたIDEプラグイン作ろうぜ. VS使いに見せつけてやろうぜ