1. Quramy
Changes in tags
Changes in body
Source | HTML | Preview
@@ -1,173 +1,176 @@
# はじめに
最近, 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
+* [VisualStudio Code](https://code.visualstudio.com/) : TypeScriptの連携機能にはバンドルされているTSServerが利用されている. MicroSoft謹製.
* [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 | 機能概要 |
|:---|:---|:----|
+| brace | response| ファイルのカーソルから見て, brace matchingする箇所を返す(`{`に対応する`}`の場所等). 一般的なエディタであれば, デフォルトで持っている機能であり, いらない子. |
| 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の定義箇所を取得する. |
+| exit | none | tsserver自体を終了する. |
| format | response | ソースをフォーマットする?使ってないのでよくわからん |
| formatonkey | response | これも使ってないのでよく分からん. |
| geterr | event | コンパイルエラー情報を取得する. 現状, 唯一のevent発火API(後述). |
| navbar | response | ファイルの見出しを作成する. IDEのサイドメニューに表示するような一覧情報を作るのに便利. |
| navto | response | キーワード検索を行う. |
+| occurrences | response | カーソル上のIdentiferの出現箇所を取得する. referencesと似ているが, referencesは「どのようなcontextで参照しているか」まで取得可能であるのに対し, occurrencesは出現している場所情報のみを返却する. |
| 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する箇所を返す(`{`に対応する`}`の場所等). 一般的なエディタであれば, デフォルトで持っている機能であり, いらない子. |
+| signatureHelp | response | ファイルカーソル上のメソッド呼び出しについて, 引数情報等の詳細を取得. |
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使いに見せつけてやろうぜ