diff --git a/internal/lsp/source/symbols.go b/internal/lsp/source/symbols.go index 1ce5fc8619..c0dcaf4805 100644 --- a/internal/lsp/source/symbols.go +++ b/internal/lsp/source/symbols.go @@ -68,7 +68,7 @@ func DocumentSymbols(ctx context.Context, f File) []Symbol { switch spec := spec.(type) { case *ast.TypeSpec: if obj := info.ObjectOf(spec.Name); obj != nil { - ts := typeSymbol(spec, obj, fset, q) + ts := typeSymbol(info, spec, obj, fset, q) symbols = append(symbols, ts) symbolsToReceiver[obj.Type()] = len(symbols) - 1 } @@ -161,7 +161,7 @@ func setKind(s *Symbol, typ types.Type, q types.Qualifier) { } } -func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol { +func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol { s := Symbol{Name: obj.Name()} s.Detail, _ = formatType(obj.Type(), q) setKind(&s, obj.Type(), q) @@ -193,6 +193,66 @@ func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q typ } } + ti, objIsInterface := obj.Type().Underlying().(*types.Interface) + ai, specIsInterface := spec.Type.(*ast.InterfaceType) + if objIsInterface && specIsInterface { + for i := 0; i < ti.NumExplicitMethods(); i++ { + method := ti.ExplicitMethod(i) + child := Symbol{ + Name: method.Name(), + Kind: MethodSymbol, + } + + var spanNode, selectionNode ast.Node + Methods: + for _, f := range ai.Methods.List { + for _, id := range f.Names { + if id.Name == method.Name() { + spanNode, selectionNode = f, id + break Methods + } + } + } + if span, err := nodeSpan(spanNode, fset); err == nil { + child.Span = span + } + if span, err := nodeSpan(selectionNode, fset); err == nil { + child.SelectionSpan = span + } + s.Children = append(s.Children, child) + } + + for i := 0; i < ti.NumEmbeddeds(); i++ { + embedded := ti.EmbeddedType(i) + nt, isNamed := embedded.(*types.Named) + if !isNamed { + continue + } + + child := Symbol{Name: types.TypeString(embedded, q)} + setKind(&child, embedded, q) + var spanNode, selectionNode ast.Node + Embeddeds: + for _, f := range ai.Methods.List { + if len(f.Names) > 0 { + continue + } + + if t := info.TypeOf(f.Type); types.Identical(nt, t) { + spanNode, selectionNode = f, f.Type + break Embeddeds + } + } + + if span, err := nodeSpan(spanNode, fset); err == nil { + child.Span = span + } + if span, err := nodeSpan(selectionNode, fset); err == nil { + child.SelectionSpan = span + } + s.Children = append(s.Children, child) + } + } return s } diff --git a/internal/lsp/testdata/symbols/main.go b/internal/lsp/testdata/symbols/main.go index 032f74e1f1..b9ee77c0f8 100644 --- a/internal/lsp/testdata/symbols/main.go +++ b/internal/lsp/testdata/symbols/main.go @@ -1,6 +1,8 @@ package main -import "io" +import ( + "io" +) var x = 42 //@symbol("x", "x", "Variable", "") @@ -39,5 +41,16 @@ func main() { //@symbol("main", "main", "Function", "") } type Stringer interface { //@symbol("Stringer", "Stringer", "Interface", "") - String() string + String() string //@symbol("String", "String", "Method", "Stringer") +} + +type ABer interface { //@symbol("ABer", "ABer", "Interface", "") + B() //@symbol("B", "B", "Method", "ABer") + A() string //@symbol("A", "A", "Method", "ABer") +} + +type WithEmbeddeds interface { //@symbol("WithEmbeddeds", "WithEmbeddeds", "Interface", "") + Do() //@symbol("Do", "Do", "Method", "WithEmbeddeds") + ABer //@symbol("ABer", "ABer", "Interface", "WithEmbeddeds") + io.Writer //@symbol("io.Writer", "io.Writer", "Interface", "WithEmbeddeds") }