Gopls: ナビゲーション機能
このページでは、Gopls のソースコードナビゲーション機能について説明します。
定義
LSP の textDocument/definition リクエストは、カーソル下のシンボルの宣言位置を返します。ほとんどのエディタは、その位置に直接移動するコマンドを提供しています。
定義クエリは、次のような予期せぬ場所でも機能します
- インポートパス上では、インポートされたパッケージのファイル内の各パッケージ宣言の位置のリストを返します。
- パッケージ宣言上では、そのパッケージのドキュメントを提供するパッケージ宣言の位置を返します。
go:linknameディレクティブ内のシンボル上では、そのシンボルの宣言位置を返します。- ドキュメントリンク上では、(
hoverと同様に) リンクされたシンボルの位置を返します。 go:embedディレクティブ内のファイル名上では、埋め込みファイルの場所を返します。- Go 以外の関数(本体のない
func)の宣言上では、もしあれば、アセンブリ実装の位置を返します。 - return ステートメント上では、関数の結果変数の位置を返します。
- goto、break、または continue ステートメント上では、それぞれラベル、関連するブロックステートメントの閉じ括弧、または関連するループの開始位置を返します。
クライアントサポート
- VS Code: 定義へ移動(
F12または⌘-クリック)を使用します。カーソルがすでに宣言位置にある場合、リクエストは代わりに「参照へ移動」として解釈されます。 - Emacs + eglot:
M-x xref-find-definitionsを使用します。 - Vim + coc.nvim: ??
- CLI:
gopls definition file.go:#offset
参考文献
LSP の textDocument/references リクエストは、カーソル下のシンボルを参照するすべての識別子の位置を返します。
参照アルゴリズムは、構文の様々な部分を次のように扱います。
- シンボルへの参照は、そのシンボルのすべての使用箇所を報告します。エクスポートされたシンボルの場合、これには他のパッケージ内の位置が含まれる場合があります。
- パッケージ宣言への参照は、そのパッケージへの直接インポートと、同じパッケージ内の他のすべてのパッケージ宣言です。
intやappendのような組み込みシンボルへの参照を要求することはエラーです。なぜなら、それらは数が多すぎて関心がないと想定されているからです。- インターフェースメソッドへの参照には、インターフェースを実装する具象型の参照が含まれます。同様に、具象型のメソッドへの参照には、対応するインターフェースメソッドへの参照が含まれます。
struct{T}のような構造体型における埋め込みフィールドTは、Go では型への参照とフィールドの定義の両方であるという点でユニークです。references操作は、フィールドとしての参照のみを報告します。型への参照を見つけるには、まず型宣言にジャンプしてください。
参照クエリは、選択したファイルを解析するために使用されたビルド構成に関する情報のみを返すことに注意してください。したがって、foo_windows.go で定義されたシンボルへの参照を要求した場合、たとえ bar_linux.go ファイルが同じ名前のシンボルを参照していても、結果に bar_linux.go が含まれることはありません。詳細については、https://go.dokyumento.jp/issue/65755 を参照してください。
クライアントは、宣言をリファレンスに含めるように要求できます。ほとんどのクライアントはこれを行います。
クライアントサポート
- VS Code:
Go to Referencesを使用して参照を素早く「プレビュー」するか、Find all Referencesを使用して参照パネルを開きます。 - Emacs + eglot:
xrefパッケージ経由で:M-x xref-find-referencesを使用します。 - Vim + coc.nvim: ??
- CLI:
gopls references file.go:#offset
実装
LSP の textDocument/implementation リクエストは、抽象型と具象型、およびそれらのメソッド間の関係を照会します。
インターフェースと具象型はメソッドセットを使用して照合されます
- インターフェース型への参照で呼び出されると、そのインターフェースを実装する各型の宣言位置を返します。
- 具象型で呼び出されると、一致するインターフェース型の位置を返します。
- インターフェースメソッドで呼び出されると、インターフェースを満たす型の対応するメソッドを返します。
- 具象メソッドで呼び出されると、一致するインターフェースメソッドの位置を返します。
例
implementation(io.Reader)には、io.ReadCloserのようなサブインターフェースや、*os.Fileのような具象実装が含まれます。また、io.Readerと同等の他の宣言も含まれます。implementation(os.File)には、io.Readerやio.ReadCloserのようなインターフェースのみが含まれます。
LSP の実装機能には、サブタイプに対する組み込みの偏りがあります。おそらく、Java や C++ のような言語では、型とそのスーパータイプ間の関係が構文に明示されているため、対応する「インターフェースへ移動」操作は、「定義へ移動」の2つ以上のステップのシーケンスとして実現できるためです。最初のステップで型宣言を訪れ、残りのステップで祖先を順次訪れます。(https://github.com/microsoft/language-server-protocol/issues/2037 を参照してください。)
Go では、2つの型間に構文上の関係がないため、サブタイプとスーパータイプの間をいずれかの方向に移動する際には検索が必要です。上記のヒューリスティックは多くの場合にうまく機能しますが、io.ReadCloser のスーパーインターフェースを要求することはできません。サブタイプとスーパータイプの間でより明示的なナビゲーションを行うには、[型階層](#Type Hierarchy) 機能を使用してください。
自明でないインターフェースのみが考慮されます。型 any の実装は報告されません。
同じパッケージ内では、すべての一致する型/メソッドが報告されます。ただし、パッケージ間では、エクスポートされたパッケージレベルの型とそのメソッドのみが報告されるため、ローカル型(インターフェース、または埋め込みによるメソッドを持つ構造体型)が結果から欠落する可能性があります。
関数、func 型、および動的関数呼び出しはシグネチャを使用して照合されます
- 関数定義の
funcトークンで呼び出されると、一致するシグネチャ型と動的呼び出し式の位置を返します。 - シグネチャ型の
funcトークンで呼び出されると、一致する具象関数定義の位置を返します。 - 動的関数呼び出しの
(トークンで呼び出されると、一致する具象関数定義の位置を返します。
ターゲット型または候補型のいずれかがジェネリックである場合、一方が他方を実装できるような両方の型のインスタンス化があれば、結果には候補型が含まれます。(注: 現在、マッチャーは完全なユニフィケーションを実装していないため、型パラメータは任意の型に一致するワイルドカードのように扱われ、メソッドセット全体または単一メソッド内での置換の一貫性は考慮されません。これにより、時折、偽の一致が生じる可能性があります。)
型は関数型であると同時にメソッドを持つ名前付き型である可能性もあるため (たとえば http.HandlerFunc )、両方の種類の実装クエリ (メソッドセットによるものと関数シグネチャによるもの) に参加する可能性があります。メソッドセットを使用するクエリは型またはメソッド名で呼び出す必要があり、シグネチャを使用するクエリは func または ( トークンで呼び出す必要があります。
クライアントサポート
- VS Code: 実装へ移動(
⌘F12)を使用します。 - Emacs + eglot:
M-x eglot-find-implementationを使用します。 - Vim + coc.nvim: ??
- CLI:
gopls implementation file.go:#offset
型定義
LSP の textDocument/typeDefinition リクエストは、選択されたシンボルの型の位置を返します。
たとえば、選択が *bytes.Buffer 型のローカル変数 buf の名前である場合、typeDefinition クエリは bytes.Buffer 型の位置を返します。クライアントは通常、その位置にナビゲートします。
ポインタ、配列、スライス、チャネル、マップなどの型コンストラクタは、名前付き型を検索する際に選択された型から取り除かれます。たとえば、x が chan []*T 型の場合、報告される型定義は T のものになります。同様に、シンボルの型が1つの「興味深い」(名前付きでエラーでない)結果型を持つ関数の場合、その関数の結果型が使用されます。
Gopls は現在、typeDefinition クエリが任意の式ではなくシンボルに適用されることを要求しています。この機能の潜在的な拡張については、https://go.dokyumento.jp/issue/67890 を参照してください。
クライアントサポート
- VS Code: 型定義へ移動を使用します。
- Emacs + eglot:
M-x eglot-find-typeDefinitionを使用します。 - Vim + coc.nvim: ??
- CLI: サポートされていません
ドキュメントシンボル
textDocument/documentSymbol LSP クエリは、このファイル内のトップレベル宣言のリストを報告します。クライアントはこの情報を使用して、ファイルの概要と、より高速なナビゲーションのためのインデックスを表示できます。
クライアントが hierarchicalDocumentSymbolSupport を示す場合、Gopls は DocumentSymbol 型で応答します。それ以外の場合は SymbolInformation を返します。
クライアントサポート
- VS Code: ナビゲーションにはアウトラインビューを使用します。
- Emacs + eglot: シンボルにジャンプするには
M-x imenuを使用します。 - Vim + coc.nvim: ??
- CLI:
gopls links file.go
シンボル
workspace/symbol LSP クエリは、ワークスペース内のすべてのシンボルのインデックスを検索します。
人気のあるあいまい一致検索ツール FZF から着想を得たデフォルトのシンボル一致アルゴリズム (fastFuzzy) は、クエリ内のスペルミスや略語を修正するために、さまざまな不正確な一致を試みます。たとえば、DocSym を DocumentSymbol の一致と見なします。
設定
symbolMatcher設定は、シンボルの一致に使用されるアルゴリズムを制御します。symbolStyle設定は、シンボル応答でシンボルがどのように修飾されるかを制御します。symbolScope設定は、クエリのスコープを決定します。directoryFilters設定は、検索から除外されるディレクトリを指定します。
クライアントサポート
- VS Code: ⌘T を使用してワークスペーススコープのシンボルへ移動を開きます。(または、Ctrl-Shift-O を使用し、ファイル内を検索するには
@プレフィックスを、ワークスペース全体を検索するには#プレフィックスを追加します。) - Emacs + eglot: 検索語に一致するシンボルを表示するには
M-x xref-find-aproposを使用します。 - Vim + coc.nvim: ??
- CLI:
gopls links file.go
選択範囲
textDocument/selectionRange LSP クエリは、現在の選択範囲を囲む各構文要素の語彙的な範囲に関する情報を返します。クライアントはこれを使用して、選択範囲を順次大きな式に拡張する操作を提供できます。
クライアントサポート
- VSCode: 選択範囲を拡張するには
⌘⇧^→を、再度縮小するには⌘⇧^←を使用します。このビデオをご覧ください。 - Emacs + eglot: 標準ではありません。この設定スニペットで定義されている
M-x eglot-expand-selectionを使用してください。 - Vim + coc.nvim: ??
- CLI: サポートされていません
呼び出し階層
LSP CallHierarchy メカニズムは、クライアントが静的呼び出しグラフの一部を階層的に表示できるようにする3つのクエリで構成されています。
textDocument/prepareCallHierarchyは、特定の場所に対する アイテムのリストを返します。各アイテムは、その場所を囲む名前付き関数またはメソッドを表します。callHierarchyItem/incomingCallsは、選択されたアイテムを呼び出す呼び出しサイトのセットを返します。callHierarchy/outgoingCallsは、選択されたアイテムによって呼び出される関数のセットを返します。
関数宣言内の名前を選択した状態でコマンドを呼び出します。
動的な呼び出しは分析的に検出することが実用的ではないため、含まれていません。したがって、結果が網羅的ではない可能性があることに注意し、必要に応じて 参照 クエリを実行してください。
階層は、ネストされた関数を囲む名前付き関数とは区別しません。(動的呼び出しを検出する機能がなければ、そうする意味はほとんどありません。)
以下のスクリーンショットは、f を根とする発信呼び出しツリーを示しています。このツリーは、f から fmt.Sprint の内部を経て fmt.Stringer の String メソッドへのパスを示すために展開されています。
クライアントサポート
- VS Code:
Show Call Hierarchyメニュー項目(⌥⇧H)で 呼び出し階層ビューが開きます(注: ドキュメントは C++ に言及していますが、Go でも考え方は同じです)。 - Emacs + eglot: 標準ではありません。
(package-vc-install "https://github.com/dolmens/eglot-hierarchy")でインストールしてください。選択した関数への直接の着信呼び出しを表示するにはM-x eglot-hierarchy-call-hierarchyを使用し、直接の発信呼び出しを表示するにはプレフィックス引数 (C-u) を使用してください。ツリーを展開する方法はありません。 - CLI:
gopls call_hierarchy file.go:#offsetは発信および着信呼び出しを表示します。
型階層
LSP の型階層メカニズムは、名前付き型に対するサブタイピング関係の一部を階層的に表示できるようにする3つのクエリで構成されています。
textDocument/prepareTypeHierarchyは、現在の位置にある名前付き型を記述する アイテムを返します。typeHierarchyItem/subtypesは、選択された(インターフェース)型のサブタイプのセットを返します。typeHierarchy/supertypesは、選択された型のスーパータイプ(インターフェース型)のセットを返します。
型の名前を選択した状態でコマンドを呼び出します。
実装クエリと同様に、型階層クエリは、クエリ対象の型と同じパッケージ内にある関数ローカル型のみを報告します。また、結果にはエイリアス型は含まれず、定義済み型のみが含まれます。
注意点
- 型階層は、名前付き型とその代入可能性の関係のみをサポートします。対照的に、実装リクエストは、名前のない
func型と関数宣言、関数リテラル、およびこれらの型の値の動的呼び出し間の関係も報告します。
クライアントサポート
- VS Code:
Show Type Hierarchyメニュー項目で 型階層ビューが開きます(注: ドキュメントは Java に言及していますが、Go でも考え方は同じです)。 - Emacs + eglot: 2025年3月にサポートが追加されました。
M-x eglot-show-call-hierarchyを使用します。 - CLI: まだサポートされていません。
このドキュメントのソースファイルは、golang.org/x/tools/gopls/doc の下にあります。