mirror of
https://github.com/golang/go
synced 2024-11-19 02:14:43 -07:00
130 lines
2.5 KiB
Go
130 lines
2.5 KiB
Go
|
// 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 (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"go/ast"
|
||
|
"go/format"
|
||
|
"go/token"
|
||
|
|
||
|
"golang.org/x/tools/internal/span"
|
||
|
)
|
||
|
|
||
|
type SymbolKind int
|
||
|
|
||
|
const (
|
||
|
PackageSymbol SymbolKind = iota
|
||
|
StructSymbol
|
||
|
VariableSymbol
|
||
|
ConstantSymbol
|
||
|
FunctionSymbol
|
||
|
MethodSymbol
|
||
|
)
|
||
|
|
||
|
type Symbol struct {
|
||
|
Name string
|
||
|
Detail string
|
||
|
Span span.Span
|
||
|
Kind SymbolKind
|
||
|
Children []Symbol
|
||
|
}
|
||
|
|
||
|
func DocumentSymbols(ctx context.Context, f File) []Symbol {
|
||
|
var symbols []Symbol
|
||
|
fset := f.GetFileSet(ctx)
|
||
|
astFile := f.GetAST(ctx)
|
||
|
for _, decl := range astFile.Decls {
|
||
|
switch decl := decl.(type) {
|
||
|
case *ast.FuncDecl:
|
||
|
symbols = append(symbols, funcSymbol(decl, fset))
|
||
|
case *ast.GenDecl:
|
||
|
for _, spec := range decl.Specs {
|
||
|
switch spec := spec.(type) {
|
||
|
case *ast.ImportSpec:
|
||
|
symbols = append(symbols, importSymbol(spec, fset))
|
||
|
case *ast.TypeSpec:
|
||
|
symbols = append(symbols, typeSymbol(spec, fset))
|
||
|
case *ast.ValueSpec:
|
||
|
for _, name := range spec.Names {
|
||
|
symbols = append(symbols, varSymbol(decl, name, fset))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return symbols
|
||
|
}
|
||
|
|
||
|
func funcSymbol(decl *ast.FuncDecl, fset *token.FileSet) Symbol {
|
||
|
s := Symbol{
|
||
|
Name: decl.Name.String(),
|
||
|
Kind: FunctionSymbol,
|
||
|
}
|
||
|
|
||
|
if decl.Recv != nil {
|
||
|
s.Kind = MethodSymbol
|
||
|
}
|
||
|
|
||
|
span, err := nodeSpan(decl, fset)
|
||
|
if err == nil {
|
||
|
s.Span = span
|
||
|
}
|
||
|
buf := &bytes.Buffer{}
|
||
|
if err := format.Node(buf, fset, decl); err == nil {
|
||
|
s.Detail = buf.String()
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func importSymbol(spec *ast.ImportSpec, fset *token.FileSet) Symbol {
|
||
|
s := Symbol{
|
||
|
Name: spec.Path.Value,
|
||
|
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,
|
||
|
}
|
||
|
span, err := nodeSpan(spec, fset)
|
||
|
if err == nil {
|
||
|
s.Span = span
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func varSymbol(decl *ast.GenDecl, name *ast.Ident, fset *token.FileSet) Symbol {
|
||
|
s := Symbol{
|
||
|
Name: name.Name,
|
||
|
Kind: VariableSymbol,
|
||
|
}
|
||
|
|
||
|
if decl.Tok == token.CONST {
|
||
|
s.Kind = ConstantSymbol
|
||
|
}
|
||
|
|
||
|
span, err := nodeSpan(name, fset)
|
||
|
if err == nil {
|
||
|
s.Span = span
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func nodeSpan(n ast.Node, fset *token.FileSet) (span.Span, error) {
|
||
|
r := span.NewRange(fset, n.Pos(), n.End())
|
||
|
return r.Span()
|
||
|
}
|