Gopls: 実装

最終主要更新日: 2024年1月16日

このドキュメントは、新しい貢献者が道を見つけるのに役立つよう、goplsの構造の概要を高いレベルで示します。実装の完全な説明、または主要なコンポーネントのいずれかの説明を意図したものではありません。それについては、以下のパッケージドキュメントやコード内の他のコメントの方が良いガイドとなります。

以下の図は、goplsモジュールの選択されたコンポーネントと、Goインポートグラフに基づくそれらの相互関係を示しています。テストおよびテストインフラストラクチャ、ユーティリティパッケージ、x/toolsモジュールからのパッケージは示されていません。簡潔にするため、パッケージは最後のセグメントで参照されていますが、通常はあいまいではありません。

各ブロックの高さは、その技術的な深さに漠然と対応しています。protocolのように、LSPプロトコル全体のGo型を宣言するような、広くて浅いブロックもあります。一方、多くの密なロジックとアルゴリズムを含むcachegolangのように、深いブロックもあります。

Gopls architecture

下から順に、様々なコンポーネントを説明します。

最下層は、Language Server Protocolのリクエストとレスポンスの型を定義します。

  • protocolパッケージは標準プロトコルを定義します。これは主にMicrosoftが提供するスキーマ定義から機械的に生成されます。最も重要な型はDocumentURIで、クライアントエディタドキュメントを識別するfile: URLを表します。また、ソース位置に使用される異なる座標系(UTF-8、UTF-16、token.Pos)間をマッピングするMapperも提供します。

  • commandパッケージはGoplsの非標準コマンドを定義します。これらはすべてworkspace/executeCommand拡張メカニズムを通じて呼び出されます。これらのコマンドは通常、コードアクションまたはコードレンズの継続としてサーバーから返されます。ほとんどのクライアントは、それらへの呼び出しを直接構築することはありません。

次の層は、いくつかの重要で広く使用されるデータ構造を定義します。

  • fileパッケージは、クライアントファイルの主要な抽象化を定義します: そのIdentity (URIとコンテンツハッシュ)、およびそのHandle (ファイルの特定のスナップショットのバージョンとコンテンツをさらに提供します)。

  • parsegoパッケージは、Goソースファイルのパースされた形式であるFileを定義します。これには、そのコンテンツ、構文ツリー、および座標マッピング(Mapperとtoken.File)が含まれます。このパッケージは、Goパーサーのエラー回復の欠点を回避するために、様々な種類のツリー修復を実行します。

  • metadataパッケージは、Goパッケージのメタデータの抽象化であるPackageを定義します。これはgo list -jsonの出力と似ています。メタデータは、go listの呼び出しを処理するgo/packagesから生成されます。(Bazel用のGOPACKAGESDRIVERである程度機能するとユーザーは報告していますが、このシナリオのテストは維持していません。)

    このパッケージは、ワークスペースの完全なインポートグラフであるGraphも提供します。各グラフノードはPackageです。

settings層は、gopls設定オプションのデータ構造(実質的には大きなツリー)と、そのJSONエンコーディングを定義します。

cache層は、goplsの最大かつ最も複雑なコンポーネントです。状態管理、依存関係分析、無効化に関係しています。クライアントとの通信のSession、クライアントが開いているFolder、特定のビルドオプションを持つ特定のワークスペースツリーのView、特定の編集操作後のワークスペース内のすべてのファイルのSnapshot、ディスクに保存されている(DiskFile)か編集されて保存されていない(Overlay)かにかかわらず、すべてのファイルの内容、go.modファイルのパースやシンボルインデックスの構築などのメモリ内のメモ化された計算のCache、およびGo構文からパッケージの型チェックの結果を保持するPackageです。

キャッシュ層は、以下を含む様々な補助パッケージに依存しています。

  • filecacheパッケージは、goplsの永続的でトランザクションベースのファイルベースのキー/値ストアを管理します。

  • xrefsmethodsets、およびtyperefsパッケージは、型チェックから派生した情報のインデックスを構築するためのアルゴリズムと、これらのシリアル化可能なインデックスをファイルキャッシュでエンコードおよびデコードするためのアルゴリズムを定義します。

    これらのパッケージを合わせることで、v0.12の再設計によって提供され、「成長するGoエコシステムのためのgoplsのスケーリング」で説明されている、高速な再起動、メモリ消費量の削減、およびプロセス間の相乗効果が可能になりました。

キャッシュは、ワークスペース全体でモジュール分析(go vetに類似)を実行するgoplsのgo/analysisドライバーも定義します。Goplsには、vetの一部ではない多数の分析パスも含まれています。

次の層では、特定の言語のファイルを処理するための4つのパッケージを定義しています。modはgo.modファイル用、workはgo.workファイル用、templatetext/template構文のファイル用、そしてgolangはGo自身のファイル用です。このパッケージは、はるかに大きく、goplsの主要な機能、すなわちGoコードのナビゲーション、分析、リファクタリングを提供します。ほとんどのユーザーが想像するように、このパッケージこそがgoplsそのものです。

serverパッケージはLSPサービスの実装を定義し、LSPリクエストタイプごとに1つのハンドラメソッドがあります。各ハンドラはファイルのタイプに基づいて切り替わり、4つの言語固有パッケージのいずれかにディスパッチします。

lsprpcパッケージは、サービスインターフェースを私たちのjsonrpc2サーバーに接続します。

この図が依存関係グラフ、つまりプログラムの構造の「静的」な視点であることを念頭に置いてください。より動的な視点では、特定の要求の処理中に遭遇するシーケンスに基づいてパッケージを並べ替えます。このような視点では、最下層は「ワイヤ」(プロトコルとコマンド)を表し、次の層はRPC関連パッケージ(lsprpcとserver)を保持し、機能(例:golang、mod、work、template)は最上部に位置します。

cmdパッケージは、goplsコマンドのコマンドラインインターフェースを定義します。goplsのメインパッケージは、その単なるトリビアルなラッパーに過ぎません。通常は引数なしで実行され、サーバーを起動して無期限にリッスンします。また、サーバーを起動し、単一のリクエストを行い、終了する多数のサブコマンドも提供し、サーバー機能への伝統的なバッチコマンドアクセスを提供します。これらのサブコマンドは主にデバッグ補助として提供されますが、https://go.dokyumento.jp/issue/63693を参照してください。


このドキュメントのソースファイルは、golang.org/x/tools/gopls/doc の下にあります。