LSP とは
Language Server Protocol (LSP) は、エディタや IDE(開発ツール)と言語サーバー間で使用されるプロトコルを定義しています。
言語サーバーは、自動補完・定義や参照情報の検索などの言語機能を提供します。
言語サーバーを1つ実装することで、LSP に対応したすべての開発ツールで利用できます。そのため、エディタの数と言語の数だけ各開発ツールでそれらの機能を開発する必要がなくなります。
LSP なし
自動補完・定義や参照情報の検索機能を実現するためには、エディタ・IDE の実装 × 言語の m × n が必要になります。
LSP あり
自動補完・定義や参照情報の検索機能を実現するためには、言語数の n だけで済みます。(エディタ・IDE 側で LSP に対応する必要はあります)
LSP の仕様
概要
言語サーバーは、開発ツールとは別のプロセスとして実行され、JSON-RPC を介して言語プロトコルを使用して通信します。
言語サーバーは LSP を通じて知ったドキュメント(ファイルの内容を指す LSP 上の用語)を真実のデータとして認識し、メモリ上で管理します。ドキュメントは同期されている必要があります。
フロー例(ファイルを開いてから編集した後に閉じる場合)
- 開発ツールが言語サーバーへ Document を送信します。
- Document は言語サーバーのメモリに保存され、以後これが Document の真実となります。
- Notification
- method:
textDocument/didOpen - params:
{ document }- document: ファイル全体
- method:
- ユーザーがファイルを更新します。
- 開発ツールが言語サーバーへ Document を更新したことを通知します。
- Notification
- method:
textDocument/didChange - params:
{ documentURI, changes }- documentURI: ドキュメントの URI
- changes: ドキュメントの変更内容(差分)
- method:
- Notification
- 言語サーバーが Document を解析して開発ツールへ結果を応答します。
- Notification
- method:
textDocument/publishDiagnostics - params:
{ Diagnostic[] }
- method:
- Notification
- ユーザーがファイルを閉じます。
- 言語サーバーに Document が残らなくなったことを示します。
- Notification
- method:
textDocument/didClose - params:
{ documentURI }
- method:
フロー例(関数の定義を参照する場合)
- ユーザーが開発ツールで「定義へジャンプ」を選択します。
- Request
- method:
textDocument/definition - params:
{ documentURI, position }
- method:
- Response
- method:
textDocument/definition - result:
Location{ documentURI, range }
- method:
- Request
※ 言語に依存しないよう、ファイルの場所(documentURI)・記述箇所(position)で要求し、応答もまた記述箇所(Location)で応答します。
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/use.cpp"
},
"position": {
"line": 3,
"character": 12
}
}
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/provide.cpp",
"range": {
"start": {
"line": 0,
"character": 4
},
"end": {
"line": 0,
"character": 11
}
}
}
}
LSP とベースプロトコル
言語サーバーによらず基本的なデータ構造やデータタイプはベースプロトコルとして定義されており、LSP ではベースプロトコルを使ってやり取りされる JSON-RPC のメソッドが定義されています。
ベースプロトコル
通信の構造
通信は HTTP に似た構造を持ち、「ヘッダー」と「コンテンツ」の2つの部分で構成されます。
- ヘッダは必ず1つは指定する必要があります。
- ヘッダ行は
\r\nで終了します。 - 最後のヘッダとコンテンツとの間は
\r\nで区切られます。 - サポートされているヘッダ
Content-LengthContent-Type
Content-Length: ...\r\n
\r\n
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion",
"params": {
...
}
}
JSON 構造
メソッド
-
Notification type
-
textDocument/didOpen: Document オープン- document: Document 内容(ファイル全体)
-
textDocument/didChange: Document の更新通知- documentURI: 更新された Document の URI
- changes: 更新された内容
-
textDocument/publishDiagnostics: Document における警告情報 -
textDocument/didClose: Document クローズ- documentURI
-
-
Request/Response type
-
textDocument/definition- Request: documentURI, position
- Response: location
-
$/cancelRequest$/progress-
Capability
- 対応しているメソッドを相手へ通知するためのメソッドです。
- Client Capability
- Server Capability
※
$/から始まるメソッドの Notification や Request はプロトコル実装に依存しており、すべてのサーバー・クライアントで実装できるとは限りません。
基本的なデータ構造
URI・正規表現・列挙型(Enumerations)の扱い、および整数や配列などの基本型が定義されています。
メッセージの順序
基本的にリクエストを受け取った順にレスポンスを返すべきですが、正確性に影響しない範囲で並列実行による順序の変更も許容されます。
ライフサイクル管理
サーバーの起動から終了までの流れを制御するメッセージが定義されています。
-
initialize/initialized: 初期化プロセスと、その完了通知 -
shutdown/exit: サーバーの終了要請と、実際のプロセス終了
機能のネゴシエーション(Capabilities)
すべてのサーバーが全機能をサポートしているわけではないため、Capabilities(機能フラグ)の仕組みが用意されています。
初期化時にクライアントとサーバーが互いのサポート機能を通知し合い、利用可能な機能を決定します。また、動的な機能の登録(client/registerCapability)や解除(client/unregisterCapability)もサポートされています。
LSP で定義されるメソッド
ドキュメント同期(Document Synchronization)
エディタ上のテキスト内容をサーバーと同期するためのメソッドです。
| カテゴリ | メソッド名 | 種類 | 概要 |
|---|---|---|---|
| 同期 | textDocument/didOpen |
通知 | ドキュメントが開かれたことを通知します。 |
| 同期 | textDocument/didChange |
通知 | ドキュメントの内容が変更されたことを通知します。 |
| 同期 | textDocument/willSave |
通知 | 保存直前に送られる通知です。 |
| 同期 | textDocument/willSaveWaitUntil |
リクエスト | 保存直前にテキスト編集(フォーマット等)を要求します。 |
| 同期 | textDocument/didSave |
通知 | ドキュメントが保存されたことを通知します。 |
| 同期 | textDocument/didClose |
通知 | ドキュメントが閉じられたことを通知します。 |
言語機能(Language Features)
コード補完や定義への移動など、言語固有のインテリジェンスを提供するメソッドです。
| カテゴリ | メソッド名 | 種類 | 概要 |
|---|---|---|---|
| 診断 | textDocument/publishDiagnostics |
通知 | サーバーからクライアントへエラーや警告を通知します。 |
| 補完 | textDocument/completion |
リクエスト | カーソル位置でのコード補完候補を要求します。 |
| 補完 | completionItem/resolve |
リクエスト | 選択された補完項目の詳細情報を追加で要求します。 |
| ヘルプ | textDocument/hover |
リクエスト | シンボル上のホバー情報を要求します。 |
| ヘルプ | textDocument/signatureHelp |
リクエスト | 関数などのシグネチャ(引数)情報を要求します。 |
| 定義 | textDocument/definition |
リクエスト | シンボルの定義場所を要求します。 |
| 定義 | textDocument/declaration |
リクエスト | シンボルの宣言場所を要求します。 |
| 定義 | textDocument/typeDefinition |
リクエスト | 型の定義場所を要求します。 |
| 定義 | textDocument/implementation |
リクエスト | 実装場所を要求します。 |
| 参照 | textDocument/references |
リクエスト | シンボルの参照箇所を検索します。 |
| 編集 | textDocument/codeAction |
リクエスト | エラー修正などのコードアクション(クイックフィックス)を要求します。 |
| 編集 | textDocument/formatting |
リクエスト | ドキュメント全体の整形を要求します。 |
| 編集 | textDocument/rename |
リクエスト | シンボルのリネーム編集を要求します。 |
| 解析 | textDocument/documentSymbol |
リクエスト | ドキュメント内のシンボル階層を要求します。 |
| 表示 | textDocument/foldingRange |
リクエスト | 折りたたみ可能な範囲を要求します。 |
ワークスペース機能(Workspace Features)
プロジェクト全体や設定に関するメソッドです。
| カテゴリ | メソッド名 | 種類 | 概要 |
|---|---|---|---|
| シンボル | workspace/symbol |
リクエスト | ワークスペース全体からシンボルを検索します。 |
| 設定 | workspace/configuration |
リクエスト | サーバーがクライアントから設定を取得します。 |
| 設定 | workspace/didChangeConfiguration |
通知 | 設定が変更されたことをサーバーに通知します。 |
| フォルダ | workspace/didChangeWorkspaceFolders |
通知 | ワークスペースのルートフォルダ変更を通知します。 |
| ファイル | workspace/didChangeWatchedFiles |
通知 | 監視対象ファイルの変更をサーバーに通知します。 |
| 実行 | workspace/executeCommand |
リクエスト | サーバー側でのコマンド実行を要求します。 |
| 編集 | workspace/applyEdit |
リクエスト | サーバーからクライアントへ、ワークスペースの編集適用を要求します。 |
ウィンドウ機能(Window Features)
UI 表示やログ出力に関するメソッドです。
| カテゴリ | メソッド名 | 種類 | 概要 |
|---|---|---|---|
| UI | window/showMessage |
通知 | クライアントにメッセージ表示を要求します。 |
| UI | window/showMessageRequest |
リクエスト | 選択肢付きのメッセージを表示し、結果を受け取ります。 |
| ログ | window/logMessage |
通知 | クライアントにログ出力を要求します。 |
| 進捗 | window/workDoneProgress/create |
リクエスト | サーバー主導の進捗表示を開始するためにトークンを作成します。 |
| テレメトリ | telemetry/event |
通知 | テレメトリデータをクライアントに送信します。 |
この他にも、最新の仕様(3.17以降)では Notebook Document Synchronization(
notebookDocument/didOpen等)や、Inlay Hint・Inline Value などの新しい言語機能が追加されています。
LSIF とは
Language Server Index Format(LSIF)は、ソースコードのローカルコピーを必要とせずに、開発ツールや Web UI における豊富なコードナビゲーションをサポートするためのデータフォーマット定義です。
LSP と LSIF
LSP が Request → Computation ⚙️ → Response という流れであるのに対して、LSIF は Computation ⚙️ → Dump → Query という流れで利用されます。GitHub などのコードベースで言語サーバーが提供する高度な機能を実現するために利用されるフォーマットです。
LSIF で対応している LSP のリクエスト
textDocument/documentSymboltextDocument/foldingRangetextDocument/documentLinktextDocument/definitiontextDocument/declarationtextDocument/typeDefinitiontextDocument/hovertextDocument/referencestextDocument/implementation