internal/lsp: group document symbols and add more detail

This change uses go/types information to get the types for the
different symbols. It also groups the symbols according to their kinds,
though this doesn't seem to be reflected in the actual VSCode UI...

Updates golang/go#30915

Change-Id: I2caefe01f9834aaad6b9e81cd391d461405ef725
Reviewed-on: https://go-review.googlesource.com/c/tools/+/169438
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-03-26 17:38:40 -04:00
parent 3ad05305c9
commit c02eab13f0
2 changed files with 59 additions and 56 deletions

View File

@ -5,11 +5,11 @@
package source package source
import ( import (
"bytes"
"context" "context"
"fmt"
"go/ast" "go/ast"
"go/format"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/internal/span" "golang.org/x/tools/internal/span"
) )
@ -26,31 +26,34 @@ const (
) )
type Symbol struct { type Symbol struct {
Name string Name string
Detail string Detail string
Span span.Span Span span.Span
Kind SymbolKind SelectionSpan span.Span
Children []Symbol Kind SymbolKind
Children []Symbol
} }
func DocumentSymbols(ctx context.Context, f File) []Symbol { func DocumentSymbols(ctx context.Context, f File) []Symbol {
var symbols []Symbol
fset := f.GetFileSet(ctx) fset := f.GetFileSet(ctx)
astFile := f.GetAST(ctx) file := f.GetAST(ctx)
for _, decl := range astFile.Decls { pkg := f.GetPackage(ctx)
info := pkg.GetTypesInfo()
q := qualifier(file, pkg.GetTypes(), info)
var symbols []Symbol
for _, decl := range file.Decls {
switch decl := decl.(type) { switch decl := decl.(type) {
case *ast.FuncDecl: case *ast.FuncDecl:
symbols = append(symbols, funcSymbol(decl, fset)) symbols = append(symbols, funcSymbol(decl, info.ObjectOf(decl.Name), fset, q))
case *ast.GenDecl: case *ast.GenDecl:
for _, spec := range decl.Specs { for _, spec := range decl.Specs {
switch spec := spec.(type) { switch spec := spec.(type) {
case *ast.ImportSpec:
symbols = append(symbols, importSymbol(spec, fset))
case *ast.TypeSpec: case *ast.TypeSpec:
symbols = append(symbols, typeSymbol(spec, fset)) symbols = append(symbols, typeSymbol(spec, info.ObjectOf(spec.Name), fset, q))
case *ast.ValueSpec: case *ast.ValueSpec:
for _, name := range spec.Names { for _, name := range spec.Names {
symbols = append(symbols, varSymbol(decl, name, fset)) symbols = append(symbols, varSymbol(decl, name, info.ObjectOf(name), fset, q))
} }
} }
} }
@ -59,67 +62,66 @@ func DocumentSymbols(ctx context.Context, f File) []Symbol {
return symbols return symbols
} }
func funcSymbol(decl *ast.FuncDecl, fset *token.FileSet) Symbol { func funcSymbol(decl *ast.FuncDecl, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{ s := Symbol{
Name: decl.Name.String(), Name: obj.Name(),
Kind: FunctionSymbol, Kind: FunctionSymbol,
} }
if span, err := nodeSpan(decl, fset); err == nil {
if decl.Recv != nil {
s.Kind = MethodSymbol
}
span, err := nodeSpan(decl, fset)
if err == nil {
s.Span = span s.Span = span
} }
buf := &bytes.Buffer{} if span, err := nodeSpan(decl.Name, fset); err == nil {
if err := format.Node(buf, fset, decl); err == nil { s.SelectionSpan = span
s.Detail = buf.String() }
sig, _ := obj.Type().(*types.Signature)
if sig != nil {
if sig.Recv() != nil {
s.Kind = MethodSymbol
}
s.Detail += "("
for i := 0; i < sig.Params().Len(); i++ {
if i > 0 {
s.Detail += ", "
}
param := sig.Params().At(i)
label := types.TypeString(param.Type(), q)
if param.Name() != "" {
label = fmt.Sprintf("%s %s", param.Name(), label)
}
s.Detail += label
}
s.Detail += ")"
} }
return s return s
} }
func importSymbol(spec *ast.ImportSpec, fset *token.FileSet) Symbol { func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{ s := Symbol{
Name: spec.Path.Value, Name: obj.Name(),
Kind: PackageSymbol,
Detail: "import " + spec.Path.Value,
}
span, err := nodeSpan(spec, fset)
if err == nil {
s.Span = span
}
return s
}
func typeSymbol(spec *ast.TypeSpec, fset *token.FileSet) Symbol {
s := Symbol{
Name: spec.Name.String(),
Kind: StructSymbol, Kind: StructSymbol,
} }
span, err := nodeSpan(spec, fset) if span, err := nodeSpan(spec, fset); err == nil {
if err == nil {
s.Span = span s.Span = span
} }
if span, err := nodeSpan(spec.Name, fset); err == nil {
s.SelectionSpan = span
}
s.Detail, _ = formatType(obj.Type(), q)
return s return s
} }
func varSymbol(decl *ast.GenDecl, name *ast.Ident, fset *token.FileSet) Symbol { func varSymbol(decl ast.Node, name *ast.Ident, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{ s := Symbol{
Name: name.Name, Name: obj.Name(),
Kind: VariableSymbol, Kind: VariableSymbol,
} }
if span, err := nodeSpan(decl, fset); err == nil {
if decl.Tok == token.CONST {
s.Kind = ConstantSymbol
}
span, err := nodeSpan(name, fset)
if err == nil {
s.Span = span s.Span = span
} }
if span, err := nodeSpan(name, fset); err == nil {
s.SelectionSpan = span
}
s.Detail = types.TypeString(obj.Type(), q)
return s return s
} }

View File

@ -5,9 +5,8 @@
package lsp package lsp
import ( import (
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
) )
func toProtocolDocumentSymbols(m *protocol.ColumnMapper, symbols []source.Symbol) []protocol.DocumentSymbol { func toProtocolDocumentSymbols(m *protocol.ColumnMapper, symbols []source.Symbol) []protocol.DocumentSymbol {
@ -21,6 +20,8 @@ func toProtocolDocumentSymbols(m *protocol.ColumnMapper, symbols []source.Symbol
} }
if r, err := m.Range(s.Span); err == nil { if r, err := m.Range(s.Span); err == nil {
ps.Range = r ps.Range = r
}
if r, err := m.Range(s.SelectionSpan); err == nil {
ps.SelectionRange = r ps.SelectionRange = r
} }
result = append(result, ps) result = append(result, ps)