2019-03-18 16:43:08 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package source
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-03-26 15:38:40 -06:00
|
|
|
"fmt"
|
2019-03-18 16:43:08 -06:00
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
2019-03-26 15:38:40 -06:00
|
|
|
"go/types"
|
2019-03-18 16:43:08 -06:00
|
|
|
|
|
|
|
"golang.org/x/tools/internal/span"
|
|
|
|
)
|
|
|
|
|
|
|
|
type SymbolKind int
|
|
|
|
|
|
|
|
const (
|
|
|
|
PackageSymbol SymbolKind = iota
|
|
|
|
StructSymbol
|
|
|
|
VariableSymbol
|
|
|
|
ConstantSymbol
|
|
|
|
FunctionSymbol
|
|
|
|
MethodSymbol
|
2019-03-27 18:00:20 -06:00
|
|
|
InterfaceSymbol
|
2019-03-18 16:43:08 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type Symbol struct {
|
2019-03-26 15:38:40 -06:00
|
|
|
Name string
|
|
|
|
Detail string
|
|
|
|
Span span.Span
|
|
|
|
SelectionSpan span.Span
|
|
|
|
Kind SymbolKind
|
|
|
|
Children []Symbol
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func DocumentSymbols(ctx context.Context, f File) []Symbol {
|
|
|
|
fset := f.GetFileSet(ctx)
|
2019-03-26 15:38:40 -06:00
|
|
|
file := f.GetAST(ctx)
|
|
|
|
pkg := f.GetPackage(ctx)
|
|
|
|
info := pkg.GetTypesInfo()
|
|
|
|
q := qualifier(file, pkg.GetTypes(), info)
|
|
|
|
|
|
|
|
var symbols []Symbol
|
|
|
|
for _, decl := range file.Decls {
|
2019-03-18 16:43:08 -06:00
|
|
|
switch decl := decl.(type) {
|
|
|
|
case *ast.FuncDecl:
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(decl.Name); obj != nil {
|
|
|
|
symbols = append(symbols, funcSymbol(decl, obj, fset, q))
|
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
case *ast.GenDecl:
|
|
|
|
for _, spec := range decl.Specs {
|
|
|
|
switch spec := spec.(type) {
|
|
|
|
case *ast.TypeSpec:
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(spec.Name); obj != nil {
|
|
|
|
symbols = append(symbols, typeSymbol(spec, obj, fset, q))
|
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
case *ast.ValueSpec:
|
|
|
|
for _, name := range spec.Names {
|
2019-03-29 11:46:33 -06:00
|
|
|
if obj := info.ObjectOf(name); obj != nil {
|
|
|
|
symbols = append(symbols, varSymbol(decl, name, obj, fset, q))
|
|
|
|
}
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbols
|
|
|
|
}
|
|
|
|
|
2019-03-26 15:38:40 -06:00
|
|
|
func funcSymbol(decl *ast.FuncDecl, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
|
2019-03-18 16:43:08 -06:00
|
|
|
s := Symbol{
|
2019-03-26 15:38:40 -06:00
|
|
|
Name: obj.Name(),
|
2019-03-18 16:43:08 -06:00
|
|
|
Kind: FunctionSymbol,
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(decl, fset); err == nil {
|
2019-03-18 16:43:08 -06:00
|
|
|
s.Span = span
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(decl.Name, fset); err == nil {
|
|
|
|
s.SelectionSpan = span
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
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 += ")"
|
2019-03-18 16:43:08 -06:00
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2019-03-26 15:38:40 -06:00
|
|
|
func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
|
2019-03-18 16:43:08 -06:00
|
|
|
s := Symbol{
|
2019-03-26 15:38:40 -06:00
|
|
|
Name: obj.Name(),
|
2019-03-18 16:43:08 -06:00
|
|
|
Kind: StructSymbol,
|
|
|
|
}
|
2019-03-27 18:00:20 -06:00
|
|
|
if types.IsInterface(obj.Type()) {
|
|
|
|
s.Kind = InterfaceSymbol
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(spec, fset); err == nil {
|
2019-03-18 16:43:08 -06:00
|
|
|
s.Span = span
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(spec.Name, fset); err == nil {
|
|
|
|
s.SelectionSpan = span
|
|
|
|
}
|
|
|
|
s.Detail, _ = formatType(obj.Type(), q)
|
2019-03-18 16:43:08 -06:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2019-03-26 15:38:40 -06:00
|
|
|
func varSymbol(decl ast.Node, name *ast.Ident, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
|
2019-03-18 16:43:08 -06:00
|
|
|
s := Symbol{
|
2019-03-26 15:38:40 -06:00
|
|
|
Name: obj.Name(),
|
2019-03-18 16:43:08 -06:00
|
|
|
Kind: VariableSymbol,
|
|
|
|
}
|
2019-03-26 18:11:20 -06:00
|
|
|
if _, ok := obj.(*types.Const); ok {
|
|
|
|
s.Kind = ConstantSymbol
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(decl, fset); err == nil {
|
2019-03-18 16:43:08 -06:00
|
|
|
s.Span = span
|
|
|
|
}
|
2019-03-26 15:38:40 -06:00
|
|
|
if span, err := nodeSpan(name, fset); err == nil {
|
|
|
|
s.SelectionSpan = span
|
|
|
|
}
|
|
|
|
s.Detail = types.TypeString(obj.Type(), q)
|
2019-03-18 16:43:08 -06:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func nodeSpan(n ast.Node, fset *token.FileSet) (span.Span, error) {
|
|
|
|
r := span.NewRange(fset, n.Pos(), n.End())
|
|
|
|
return r.Span()
|
|
|
|
}
|