0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LSP、ベースプロトコル、LSIFの覚書

0
Posted at

LSP とは

Language Server Protocol (LSP) は、エディタや IDE(開発ツール)と言語サーバー間で使用されるプロトコルを定義しています。

言語サーバーは、自動補完・定義や参照情報の検索などの言語機能を提供します。

言語サーバーを1つ実装することで、LSP に対応したすべての開発ツールで利用できます。そのため、エディタの数と言語の数だけ各開発ツールでそれらの機能を開発する必要がなくなります。

LSP なし

自動補完・定義や参照情報の検索機能を実現するためには、エディタ・IDE の実装 × 言語の m × n が必要になります。

LSP あり

自動補完・定義や参照情報の検索機能を実現するためには、言語数の n だけで済みます。(エディタ・IDE 側で LSP に対応する必要はあります)

LSP の仕様

概要

言語サーバーは、開発ツールとは別のプロセスとして実行され、JSON-RPC を介して言語プロトコルを使用して通信します。

言語サーバーは LSP を通じて知ったドキュメント(ファイルの内容を指す LSP 上の用語)を真実のデータとして認識し、メモリ上で管理します。ドキュメントは同期されている必要があります。

フロー例(ファイルを開いてから編集した後に閉じる場合)

  1. 開発ツールが言語サーバーへ Document を送信します。
    • Document は言語サーバーのメモリに保存され、以後これが Document の真実となります。
    • Notification
      • method: textDocument/didOpen
      • params: { document }
        • document: ファイル全体
  2. ユーザーがファイルを更新します。
  3. 開発ツールが言語サーバーへ Document を更新したことを通知します。
    • Notification
      • method: textDocument/didChange
      • params: { documentURI, changes }
        • documentURI: ドキュメントの URI
        • changes: ドキュメントの変更内容(差分)
  4. 言語サーバーが Document を解析して開発ツールへ結果を応答します。
    • Notification
      • method: textDocument/publishDiagnostics
      • params: { Diagnostic[] }
  5. ユーザーがファイルを閉じます。
    • 言語サーバーに Document が残らなくなったことを示します。
    • Notification
      • method: textDocument/didClose
      • params: { documentURI }

フロー例(関数の定義を参照する場合)

  1. ユーザーが開発ツールで「定義へジャンプ」を選択します。
    • Request
      • method: textDocument/definition
      • params: { documentURI, position }
    • Response
      • method: textDocument/definition
      • result: Location{ documentURI, range }

※ 言語に依存しないよう、ファイルの場所(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-Length
    • Content-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 SynchronizationnotebookDocument/didOpen 等)や、Inlay HintInline Value などの新しい言語機能が追加されています。

LSIF とは

Language Server Index Format(LSIF)は、ソースコードのローカルコピーを必要とせずに、開発ツールや Web UI における豊富なコードナビゲーションをサポートするためのデータフォーマット定義です。

LSP と LSIF

LSP が Request → Computation ⚙️ → Response という流れであるのに対して、LSIF は Computation ⚙️ → Dump → Query という流れで利用されます。GitHub などのコードベースで言語サーバーが提供する高度な機能を実現するために利用されるフォーマットです。

LSIF で対応している LSP のリクエスト

  • textDocument/documentSymbol
  • textDocument/foldingRange
  • textDocument/documentLink
  • textDocument/definition
  • textDocument/declaration
  • textDocument/typeDefinition
  • textDocument/hover
  • textDocument/references
  • textDocument/implementation
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?