internal/lsp: switch to using protocol positions for document symbols

Change-Id: I8e550b753328b8e536bff3bb61b4ff4486fcd4f9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/193722
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-09-05 18:54:05 -04:00
parent bb4ee55d3d
commit 04840ef8f3
13 changed files with 210 additions and 317 deletions

View File

@ -9,47 +9,29 @@ import (
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/telemetry"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/log"
"golang.org/x/tools/internal/telemetry/tag"
)
func (s *Server) documentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.session.ViewOf(uri)
f, err := getGoFile(ctx, view, uri)
rngs, err := source.Highlight(ctx, view, uri, params.Position)
if err != nil {
return nil, err
log.Error(ctx, "no highlight", err, telemetry.URI.Of(uri))
}
m, err := getMapper(ctx, f)
if err != nil {
return nil, err
}
spn, err := m.PointSpan(params.Position)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
spans, err := source.Highlight(ctx, f, rng.Start)
if err != nil {
log.Error(ctx, "no highlight", err, tag.Of("Span", spn))
}
return toProtocolHighlight(m, spans), nil
return toProtocolHighlight(rngs), nil
}
func toProtocolHighlight(m *protocol.ColumnMapper, spans []span.Span) []protocol.DocumentHighlight {
result := make([]protocol.DocumentHighlight, 0, len(spans))
func toProtocolHighlight(rngs []protocol.Range) []protocol.DocumentHighlight {
result := make([]protocol.DocumentHighlight, 0, len(rngs))
kind := protocol.Text
for _, span := range spans {
r, err := m.Range(span)
if err != nil {
continue
}
h := protocol.DocumentHighlight{Kind: &kind, Range: r}
result = append(result, h)
for _, rng := range rngs {
result = append(result, protocol.DocumentHighlight{
Kind: &kind,
Range: rng,
})
}
return result
}

View File

@ -678,7 +678,6 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if err != nil {
t.Fatal(err)
}
sedits, err := source.FromProtocolEdits(m, edits)
if err != nil {
t.Error(err)
@ -791,30 +790,22 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
}
}
func (r *runner) diffSymbols(t *testing.T, uri span.URI, want []source.Symbol, got []protocol.DocumentSymbol) string {
func (r *runner) diffSymbols(t *testing.T, uri span.URI, want []protocol.DocumentSymbol, got []protocol.DocumentSymbol) string {
sort.Slice(want, func(i, j int) bool { return want[i].Name < want[j].Name })
sort.Slice(got, func(i, j int) bool { return got[i].Name < got[j].Name })
m, err := r.mapper(uri)
if err != nil {
t.Fatal(err)
}
if len(got) != len(want) {
return summarizeSymbols(-1, want, got, "different lengths got %v want %v", len(got), len(want))
return summarizeSymbols(t, -1, want, got, "different lengths got %v want %v", len(got), len(want))
}
for i, w := range want {
g := got[i]
if w.Name != g.Name {
return summarizeSymbols(i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
return summarizeSymbols(t, i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
}
if wkind := toProtocolSymbolKind(w.Kind); wkind != g.Kind {
return summarizeSymbols(i, want, got, "incorrect kind got %v want %v", g.Kind, wkind)
if w.Kind != g.Kind {
return summarizeSymbols(t, i, want, got, "incorrect kind got %v want %v", g.Kind, w.Kind)
}
spn, err := m.RangeSpan(g.SelectionRange)
if err != nil {
return summarizeSymbols(i, want, got, "%v", err)
}
if w.SelectionSpan != spn {
return summarizeSymbols(i, want, got, "incorrect span got %v want %v", spn, w.SelectionSpan)
if protocol.CompareRange(g.SelectionRange, w.SelectionRange) != 0 {
return summarizeSymbols(t, i, want, got, "incorrect span got %v want %v", g.SelectionRange, w.SelectionRange)
}
if msg := r.diffSymbols(t, uri, w.Children, g.Children); msg != "" {
return fmt.Sprintf("children of %s: %s", w.Name, msg)
@ -823,7 +814,7 @@ func (r *runner) diffSymbols(t *testing.T, uri span.URI, want []source.Symbol, g
return ""
}
func summarizeSymbols(i int, want []source.Symbol, got []protocol.DocumentSymbol, reason string, args ...interface{}) string {
func summarizeSymbols(t *testing.T, i int, want []protocol.DocumentSymbol, got []protocol.DocumentSymbol, reason string, args ...interface{}) string {
msg := &bytes.Buffer{}
fmt.Fprint(msg, "document symbols failed")
if i >= 0 {
@ -833,7 +824,7 @@ func summarizeSymbols(i int, want []source.Symbol, got []protocol.DocumentSymbol
fmt.Fprintf(msg, reason, args...)
fmt.Fprint(msg, ":\nexpected:\n")
for _, s := range want {
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionSpan)
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionRange)
}
fmt.Fprintf(msg, "got:\n")
for _, s := range got {

View File

@ -117,7 +117,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
if !c.opts.Documentation {
return item, nil
}
declRange, err := objToRange(c.ctx, c.view, obj)
declRange, err := objToMappedRange(c.ctx, c.view, obj)
if err != nil {
return item, nil
}

View File

@ -4,27 +4,16 @@
package source
import "fmt"
import (
"fmt"
)
var (
namesSymbolKind [int(FieldSymbol) + 1]string
namesDiagnosticSeverity [int(SeverityError) + 1]string
namesCompletionItemKind [int(PackageCompletionItem) + 1]string
)
func init() {
namesSymbolKind[PackageSymbol] = "Package"
namesSymbolKind[StructSymbol] = "Struct"
namesSymbolKind[VariableSymbol] = "Variable"
namesSymbolKind[ConstantSymbol] = "Constant"
namesSymbolKind[FunctionSymbol] = "Function"
namesSymbolKind[MethodSymbol] = "Method"
namesSymbolKind[InterfaceSymbol] = "Interface"
namesSymbolKind[NumberSymbol] = "Number"
namesSymbolKind[StringSymbol] = "String"
namesSymbolKind[BooleanSymbol] = "Boolean"
namesSymbolKind[FieldSymbol] = "Field"
namesDiagnosticSeverity[SeverityWarning] = "Warning"
namesDiagnosticSeverity[SeverityError] = "Error"
@ -62,14 +51,6 @@ func parseEnum(s string, names []string) int {
return 0
}
func (e SymbolKind) Format(f fmt.State, c rune) {
formatEnum(f, c, int(e), namesSymbolKind[:], "SymbolKind")
}
func ParseSymbolKind(s string) SymbolKind {
return SymbolKind(parseEnum(s, namesSymbolKind[:]))
}
func (e DiagnosticSeverity) Format(f fmt.State, c rune) {
formatEnum(f, c, int(e), namesDiagnosticSeverity[:], "DiagnosticSeverity")
}

View File

@ -7,38 +7,45 @@ package source
import (
"context"
"go/ast"
"go/token"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
func Highlight(ctx context.Context, f GoFile, pos token.Pos) ([]span.Span, error) {
func Highlight(ctx context.Context, view View, uri span.URI, pos protocol.Position) ([]protocol.Range, error) {
ctx, done := trace.StartSpan(ctx, "source.Highlight")
defer done()
file, err := f.GetAST(ctx, ParseFull)
if file == nil {
file, _, m, err := fileToMapper(ctx, view, uri)
if err != nil {
return nil, err
}
fset := f.FileSet()
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
spn, err := m.PointSpan(pos)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
path, _ := astutil.PathEnclosingInterval(file, rng.Start, rng.Start)
if len(path) == 0 {
return nil, errors.Errorf("no enclosing position found for %s", fset.Position(pos))
return nil, errors.Errorf("no enclosing position found for %s", pos)
}
id, ok := path[0].(*ast.Ident)
if !ok {
return nil, errors.Errorf("%s is not an identifier", fset.Position(pos))
return nil, errors.Errorf("%s is not an identifier", pos)
}
var result []span.Span
var result []protocol.Range
if id.Obj != nil {
ast.Inspect(path[len(path)-1], func(n ast.Node) bool {
if n, ok := n.(*ast.Ident); ok && n.Obj == id.Obj {
s, err := nodeSpan(n, fset)
rng, err := nodeToProtocolRange(ctx, view, n)
if err == nil {
result = append(result, s)
result = append(result, rng)
}
}
return true

View File

@ -140,7 +140,7 @@ func identifier(ctx context.Context, view View, f GoFile, pkg Package, file *ast
return nil, errors.Errorf("no declaration for %s", result.Name)
}
result.Declaration.node = decl
if result.Declaration.mappedRange, err = nameToRange(ctx, view, decl.Pos(), result.Name); err != nil {
if result.Declaration.mappedRange, err = nameToMappedRange(ctx, view, decl.Pos(), result.Name); err != nil {
return nil, err
}
return result, nil
@ -165,7 +165,7 @@ func identifier(ctx context.Context, view View, f GoFile, pkg Package, file *ast
}
}
if result.Declaration.mappedRange, err = objToRange(ctx, view, result.Declaration.obj); err != nil {
if result.Declaration.mappedRange, err = objToMappedRange(ctx, view, result.Declaration.obj); err != nil {
return nil, err
}
if result.Declaration.node, err = objToNode(ctx, f.View(), pkg.GetTypes(), result.Declaration.obj, result.Declaration.mappedRange.spanRange); err != nil {
@ -182,7 +182,7 @@ func identifier(ctx context.Context, view View, f GoFile, pkg Package, file *ast
if hasErrorType(result.Type.Object) {
return result, nil
}
if result.Type.mappedRange, err = objToRange(ctx, view, result.Type.Object); err != nil {
if result.Type.mappedRange, err = objToMappedRange(ctx, view, result.Type.Object); err != nil {
return nil, err
}
}
@ -204,47 +204,6 @@ func hasErrorType(obj types.Object) bool {
return types.IsInterface(obj.Type()) && obj.Pkg() == nil && obj.Name() == "error"
}
func objToRange(ctx context.Context, view View, obj types.Object) (mappedRange, error) {
if pkgName, ok := obj.(*types.PkgName); ok {
// An imported Go package has a package-local, unqualified name.
// When the name matches the imported package name, there is no
// identifier in the import spec with the local package name.
//
// For example:
// import "go/ast" // name "ast" matches package name
// import a "go/ast" // name "a" does not match package name
//
// When the identifier does not appear in the source, have the range
// of the object be the point at the beginning of the declaration.
if pkgName.Imported().Name() == pkgName.Name() {
return nameToRange(ctx, view, obj.Pos(), "")
}
}
return nameToRange(ctx, view, obj.Pos(), obj.Name())
}
func nameToRange(ctx context.Context, view View, pos token.Pos, name string) (mappedRange, error) {
return posToRange(ctx, view, pos, pos+token.Pos(len(name)))
}
func posToRange(ctx context.Context, view View, pos, end token.Pos) (mappedRange, error) {
if !pos.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", pos)
}
if !end.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", end)
}
posn := view.Session().Cache().FileSet().Position(pos)
_, m, err := cachedFileToMapper(ctx, view, span.FileURI(posn.Filename))
if err != nil {
return mappedRange{}, err
}
return mappedRange{
m: m,
spanRange: span.NewRange(view.Session().Cache().FileSet(), pos, end),
}, nil
}
func objToNode(ctx context.Context, view View, originPkg *types.Package, obj types.Object, rng span.Range) (ast.Decl, error) {
s, err := rng.Span()
if err != nil {

View File

@ -185,7 +185,7 @@ func getPkgNameIdentifier(ctx context.Context, ident *IdentifierInfo, pkgName *t
wasImplicit: true,
}
var err error
if decl.mappedRange, err = objToRange(ctx, ident.File.View(), decl.obj); err != nil {
if decl.mappedRange, err = objToMappedRange(ctx, ident.File.View(), decl.obj); err != nil {
return nil, err
}
if decl.node, err = objToNode(ctx, ident.File.View(), ident.pkg.GetTypes(), decl.obj, decl.mappedRange.spanRange); err != nil {

View File

@ -105,7 +105,7 @@ FindCall:
comment *ast.CommentGroup
)
if obj != nil {
rng, err := objToRange(ctx, view, obj)
rng, err := objToMappedRange(ctx, view, obj)
if err != nil {
return nil, err
}

View File

@ -492,7 +492,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err)
}
srcRng, err := spanToRange(r.data, d.Src)
_, srcRng, err := spanToRange(r.data, d.Src)
if err != nil {
t.Fatal(err)
}
@ -529,7 +529,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
t.Errorf("for %v got %q want %q", d.Src, hover, expectHover)
}
} else if !d.OnlyHover {
if defRng, err := spanToRange(r.data, d.Def); err != nil {
if _, defRng, err := spanToRange(r.data, d.Def); err != nil {
t.Fatal(err)
} else if rng != defRng {
t.Errorf("for %v got %v want %v", d.Src, rng, d.Def)
@ -544,25 +544,24 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
ctx := r.ctx
for name, locations := range data {
src := locations[0]
f, err := r.view.GetFile(ctx, src.URI())
m, srcRng, err := spanToRange(r.data, src)
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
t.Fatal(err)
}
tok, err := f.(source.GoFile).GetToken(ctx)
if err != nil {
t.Fatalf("failed to get token for %s: %v", src.URI(), err)
}
pos := tok.Pos(src.Start().Offset())
highlights, err := source.Highlight(ctx, f.(source.GoFile), pos)
highlights, err := source.Highlight(ctx, r.view, src.URI(), srcRng.Start)
if err != nil {
t.Errorf("highlight failed for %s: %v", src.URI(), err)
}
if len(highlights) != len(locations) {
t.Errorf("got %d highlights for %s, expected %d", len(highlights), name, len(locations))
}
for i, h := range highlights {
if h != locations[i] {
t.Errorf("want %v, got %v\n", locations[i], h)
for i, got := range highlights {
want, err := m.Range(locations[i])
if err != nil {
t.Fatal(err)
}
if got != want {
t.Errorf("want %v, got %v\n", want, got)
}
}
}
@ -575,7 +574,7 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
srcRng, err := spanToRange(r.data, src)
_, srcRng, err := spanToRange(r.data, src)
if err != nil {
t.Fatal(err)
}
@ -624,7 +623,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
srcRng, err := spanToRange(r.data, spn)
_, srcRng, err := spanToRange(r.data, spn)
if err != nil {
t.Fatal(err)
}
@ -704,7 +703,7 @@ func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
if err != nil {
t.Fatal(err)
}
srcRng, err := spanToRange(r.data, src)
_, srcRng, err := spanToRange(r.data, src)
if err != nil {
t.Fatal(err)
}
@ -751,7 +750,7 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
if err != nil {
t.Fatalf("failed for %v: %v", uri, err)
}
symbols, err := source.DocumentSymbols(ctx, f.(source.GoFile))
symbols, err := source.DocumentSymbols(ctx, r.view, f.(source.GoFile))
if err != nil {
t.Errorf("symbols failed for %s: %v", uri, err)
}
@ -759,37 +758,37 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
continue
}
if diff := r.diffSymbols(uri, expectedSymbols, symbols); diff != "" {
if diff := r.diffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
t.Error(diff)
}
}
}
func (r *runner) diffSymbols(uri span.URI, want []source.Symbol, got []source.Symbol) string {
func (r *runner) diffSymbols(t *testing.T, uri span.URI, want, got []protocol.DocumentSymbol) string {
sort.Slice(want, func(i, j int) bool { return want[i].Name < want[j].Name })
sort.Slice(got, func(i, j int) bool { return got[i].Name < got[j].Name })
if len(got) != len(want) {
return summarizeSymbols(-1, want, got, "different lengths got %v want %v", len(got), len(want))
return summarizeSymbols(t, -1, want, got, "different lengths got %v want %v", len(got), len(want))
}
for i, w := range want {
g := got[i]
if w.Name != g.Name {
return summarizeSymbols(i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
return summarizeSymbols(t, i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
}
if w.Kind != g.Kind {
return summarizeSymbols(i, want, got, "incorrect kind got %v want %v", g.Kind, w.Kind)
return summarizeSymbols(t, i, want, got, "incorrect kind got %v want %v", g.Kind, w.Kind)
}
if w.SelectionSpan != g.SelectionSpan {
return summarizeSymbols(i, want, got, "incorrect span got %v want %v", g.SelectionSpan, w.SelectionSpan)
if protocol.CompareRange(w.SelectionRange, g.SelectionRange) != 0 {
return summarizeSymbols(t, i, want, got, "incorrect span got %v want %v", g.SelectionRange, w.SelectionRange)
}
if msg := r.diffSymbols(uri, w.Children, g.Children); msg != "" {
if msg := r.diffSymbols(t, uri, w.Children, g.Children); msg != "" {
return fmt.Sprintf("children of %s: %s", w.Name, msg)
}
}
return ""
}
func summarizeSymbols(i int, want []source.Symbol, got []source.Symbol, reason string, args ...interface{}) string {
func summarizeSymbols(t *testing.T, i int, want, got []protocol.DocumentSymbol, reason string, args ...interface{}) string {
msg := &bytes.Buffer{}
fmt.Fprint(msg, "document symbols failed")
if i >= 0 {
@ -799,11 +798,11 @@ func summarizeSymbols(i int, want []source.Symbol, got []source.Symbol, reason s
fmt.Fprintf(msg, reason, args...)
fmt.Fprint(msg, ":\nexpected:\n")
for _, s := range want {
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionSpan)
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionRange)
}
fmt.Fprintf(msg, "got:\n")
for _, s := range got {
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionSpan)
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionRange)
}
return msg.String()
}
@ -815,7 +814,7 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
rng, err := spanToRange(r.data, spn)
_, rng, err := spanToRange(r.data, spn)
if err != nil {
t.Fatal(err)
}
@ -863,15 +862,15 @@ func (r *runner) Link(t *testing.T, data tests.Links) {
// This is a pure LSP feature, no source level functionality to be tested.
}
func spanToRange(data *tests.Data, span span.Span) (protocol.Range, error) {
func spanToRange(data *tests.Data, span span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
contents, err := data.Exported.FileContents(span.URI().Filename())
if err != nil {
return protocol.Range{}, err
return nil, protocol.Range{}, err
}
m := protocol.NewColumnMapper(span.URI(), span.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
srcRng, err := m.Range(span)
if err != nil {
return protocol.Range{}, err
return nil, protocol.Range{}, err
}
return srcRng, nil
return m, srcRng, nil
}

View File

@ -8,44 +8,16 @@ import (
"context"
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
type SymbolKind int
const (
PackageSymbol SymbolKind = iota
StructSymbol
VariableSymbol
ConstantSymbol
FunctionSymbol
MethodSymbol
InterfaceSymbol
NumberSymbol
StringSymbol
BooleanSymbol
FieldSymbol
)
type Symbol struct {
Name string
Detail string
Span span.Span
SelectionSpan span.Span
Kind SymbolKind
Children []Symbol
}
func DocumentSymbols(ctx context.Context, f GoFile) ([]Symbol, error) {
func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.DocumentSymbol, error) {
ctx, done := trace.StartSpan(ctx, "source.DocumentSymbols")
defer done()
fset := f.FileSet()
file, err := f.GetAST(ctx, ParseFull)
if file == nil {
return nil, err
@ -57,14 +29,14 @@ func DocumentSymbols(ctx context.Context, f GoFile) ([]Symbol, error) {
info := pkg.GetTypesInfo()
q := qualifier(file, pkg.GetTypes(), info)
methodsToReceiver := make(map[types.Type][]Symbol)
methodsToReceiver := make(map[types.Type][]protocol.DocumentSymbol)
symbolsToReceiver := make(map[types.Type]int)
var symbols []Symbol
var symbols []protocol.DocumentSymbol
for _, decl := range file.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
if obj := info.ObjectOf(decl.Name); obj != nil {
if fs := funcSymbol(decl, obj, fset, q); fs.Kind == MethodSymbol {
if fs := funcSymbol(ctx, view, decl, obj, q); fs.Kind == protocol.Method {
// Store methods separately, as we want them to appear as children
// of the corresponding type (which we may not have seen yet).
rtype := obj.Type().(*types.Signature).Recv().Type()
@ -78,14 +50,14 @@ func DocumentSymbols(ctx context.Context, f GoFile) ([]Symbol, error) {
switch spec := spec.(type) {
case *ast.TypeSpec:
if obj := info.ObjectOf(spec.Name); obj != nil {
ts := typeSymbol(info, spec, obj, fset, q)
ts := typeSymbol(ctx, view, info, spec, obj, q)
symbols = append(symbols, ts)
symbolsToReceiver[obj.Type()] = len(symbols) - 1
}
case *ast.ValueSpec:
for _, name := range spec.Names {
if obj := info.ObjectOf(name); obj != nil {
symbols = append(symbols, varSymbol(decl, name, obj, fset, q))
symbols = append(symbols, varSymbol(ctx, view, decl, name, obj, q))
}
}
}
@ -109,21 +81,21 @@ func DocumentSymbols(ctx context.Context, f GoFile) ([]Symbol, error) {
return symbols, nil
}
func funcSymbol(decl *ast.FuncDecl, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{
func funcSymbol(ctx context.Context, view View, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) protocol.DocumentSymbol {
s := protocol.DocumentSymbol{
Name: obj.Name(),
Kind: FunctionSymbol,
Kind: protocol.Function,
}
if span, err := nodeSpan(decl, fset); err == nil {
s.Span = span
if span, err := nodeToProtocolRange(ctx, view, decl); err == nil {
s.Range = span
}
if span, err := nodeSpan(decl.Name, fset); err == nil {
s.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, decl.Name); err == nil {
s.SelectionRange = span
}
sig, _ := obj.Type().(*types.Signature)
if sig != nil {
if sig.Recv() != nil {
s.Kind = MethodSymbol
s.Kind = protocol.Method
}
s.Detail += "("
for i := 0; i < sig.Params().Len(); i++ {
@ -142,16 +114,16 @@ func funcSymbol(decl *ast.FuncDecl, obj types.Object, fset *token.FileSet, q typ
return s
}
func setKind(s *Symbol, typ types.Type, q types.Qualifier) {
func setKind(s *protocol.DocumentSymbol, typ types.Type, q types.Qualifier) {
switch typ := typ.Underlying().(type) {
case *types.Interface:
s.Kind = InterfaceSymbol
s.Kind = protocol.Interface
case *types.Struct:
s.Kind = StructSymbol
s.Kind = protocol.Struct
case *types.Signature:
s.Kind = FunctionSymbol
s.Kind = protocol.Function
if typ.Recv() != nil {
s.Kind = MethodSymbol
s.Kind = protocol.Method
}
case *types.Named:
setKind(s, typ.Underlying(), q)
@ -159,45 +131,48 @@ func setKind(s *Symbol, typ types.Type, q types.Qualifier) {
i := typ.Info()
switch {
case i&types.IsNumeric != 0:
s.Kind = NumberSymbol
s.Kind = protocol.Number
case i&types.IsBoolean != 0:
s.Kind = BooleanSymbol
s.Kind = protocol.Boolean
case i&types.IsString != 0:
s.Kind = StringSymbol
s.Kind = protocol.String
}
default:
s.Kind = VariableSymbol
s.Kind = protocol.Variable
}
}
func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{Name: obj.Name()}
func typeSymbol(ctx context.Context, view View, info *types.Info, spec *ast.TypeSpec, obj types.Object, q types.Qualifier) protocol.DocumentSymbol {
s := protocol.DocumentSymbol{
Name: obj.Name(),
}
s.Detail, _ = formatType(obj.Type(), q)
setKind(&s, obj.Type(), q)
if span, err := nodeSpan(spec, fset); err == nil {
s.Span = span
if span, err := nodeToProtocolRange(ctx, view, spec); err == nil {
s.Range = span
}
if span, err := nodeSpan(spec.Name, fset); err == nil {
s.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, spec.Name); err == nil {
s.SelectionRange = span
}
t, objIsStruct := obj.Type().Underlying().(*types.Struct)
st, specIsStruct := spec.Type.(*ast.StructType)
if objIsStruct && specIsStruct {
for i := 0; i < t.NumFields(); i++ {
f := t.Field(i)
child := Symbol{Name: f.Name(), Kind: FieldSymbol}
child := protocol.DocumentSymbol{
Name: f.Name(),
Kind: protocol.Field,
}
child.Detail, _ = formatType(f.Type(), q)
spanNode, selectionNode := nodesForStructField(i, st)
if span, err := nodeSpan(spanNode, fset); err == nil {
child.Span = span
if span, err := nodeToProtocolRange(ctx, view, spanNode); err == nil {
child.Range = span
}
if span, err := nodeSpan(selectionNode, fset); err == nil {
child.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil {
child.SelectionRange = span
}
s.Children = append(s.Children, child)
}
}
@ -207,9 +182,9 @@ func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *to
if objIsInterface && specIsInterface {
for i := 0; i < ti.NumExplicitMethods(); i++ {
method := ti.ExplicitMethod(i)
child := Symbol{
child := protocol.DocumentSymbol{
Name: method.Name(),
Kind: MethodSymbol,
Kind: protocol.Method,
}
var spanNode, selectionNode ast.Node
@ -222,11 +197,11 @@ func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *to
}
}
}
if span, err := nodeSpan(spanNode, fset); err == nil {
child.Span = span
if span, err := nodeToProtocolRange(ctx, view, spanNode); err == nil {
child.Range = span
}
if span, err := nodeSpan(selectionNode, fset); err == nil {
child.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil {
child.SelectionRange = span
}
s.Children = append(s.Children, child)
}
@ -238,7 +213,9 @@ func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *to
continue
}
child := Symbol{Name: types.TypeString(embedded, q)}
child := protocol.DocumentSymbol{
Name: types.TypeString(embedded, q),
}
setKind(&child, embedded, q)
var spanNode, selectionNode ast.Node
Embeddeds:
@ -252,12 +229,11 @@ func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *to
break Embeddeds
}
}
if span, err := nodeSpan(spanNode, fset); err == nil {
child.Span = span
if rng, err := nodeToProtocolRange(ctx, view, spanNode); err == nil {
child.Range = rng
}
if span, err := nodeSpan(selectionNode, fset); err == nil {
child.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil {
child.SelectionRange = span
}
s.Children = append(s.Children, child)
}
@ -285,28 +261,20 @@ func nodesForStructField(i int, st *ast.StructType) (span, selection ast.Node) {
return nil, nil
}
func varSymbol(decl ast.Node, name *ast.Ident, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{
func varSymbol(ctx context.Context, view View, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) protocol.DocumentSymbol {
s := protocol.DocumentSymbol{
Name: obj.Name(),
Kind: VariableSymbol,
Kind: protocol.Variable,
}
if _, ok := obj.(*types.Const); ok {
s.Kind = ConstantSymbol
s.Kind = protocol.Constant
}
if span, err := nodeSpan(decl, fset); err == nil {
s.Span = span
if rng, err := nodeToProtocolRange(ctx, view, decl); err == nil {
s.Range = rng
}
if span, err := nodeSpan(name, fset); err == nil {
s.SelectionSpan = span
if span, err := nodeToProtocolRange(ctx, view, name); err == nil {
s.SelectionRange = span
}
s.Detail = types.TypeString(obj.Type(), q)
return s
}
func nodeSpan(n ast.Node, fset *token.FileSet) (span.Span, error) {
if n == nil {
return span.Span{}, errors.New("no span for nil node")
}
r := span.NewRange(fset, n.Pos(), n.End())
return r.Span()
}

View File

@ -158,6 +158,59 @@ func IsGenerated(ctx context.Context, view View, uri span.URI) bool {
return false
}
func nodeToProtocolRange(ctx context.Context, view View, n ast.Node) (protocol.Range, error) {
mrng, err := nodeToMappedRange(ctx, view, n)
if err != nil {
return protocol.Range{}, err
}
return mrng.Range()
}
func objToMappedRange(ctx context.Context, view View, obj types.Object) (mappedRange, error) {
if pkgName, ok := obj.(*types.PkgName); ok {
// An imported Go package has a package-local, unqualified name.
// When the name matches the imported package name, there is no
// identifier in the import spec with the local package name.
//
// For example:
// import "go/ast" // name "ast" matches package name
// import a "go/ast" // name "a" does not match package name
//
// When the identifier does not appear in the source, have the range
// of the object be the point at the beginning of the declaration.
if pkgName.Imported().Name() == pkgName.Name() {
return nameToMappedRange(ctx, view, obj.Pos(), "")
}
}
return nameToMappedRange(ctx, view, obj.Pos(), obj.Name())
}
func nameToMappedRange(ctx context.Context, view View, pos token.Pos, name string) (mappedRange, error) {
return posToRange(ctx, view, pos, pos+token.Pos(len(name)))
}
func nodeToMappedRange(ctx context.Context, view View, n ast.Node) (mappedRange, error) {
return posToRange(ctx, view, n.Pos(), n.End())
}
func posToRange(ctx context.Context, view View, pos, end token.Pos) (mappedRange, error) {
if !pos.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", pos)
}
if !end.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", end)
}
posn := view.Session().Cache().FileSet().Position(pos)
_, m, err := cachedFileToMapper(ctx, view, span.FileURI(posn.Filename))
if err != nil {
return mappedRange{}, err
}
return mappedRange{
m: m,
spanRange: span.NewRange(view.Session().Cache().FileSet(), pos, end),
}, nil
}
// Matches cgo generated comment as well as the proposed standard:
// https://golang.org/s/generatedcode
var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)

View File

@ -16,68 +16,12 @@ import (
func (s *Server) documentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
ctx, done := trace.StartSpan(ctx, "lsp.Server.documentSymbol")
defer done()
uri := span.NewURI(params.TextDocument.URI)
view := s.session.ViewOf(uri)
f, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
m, err := getMapper(ctx, f)
if err != nil {
return nil, err
}
symbols, err := source.DocumentSymbols(ctx, f)
if err != nil {
return nil, err
}
return toProtocolDocumentSymbols(m, symbols), nil
}
func toProtocolDocumentSymbols(m *protocol.ColumnMapper, symbols []source.Symbol) []protocol.DocumentSymbol {
result := make([]protocol.DocumentSymbol, 0, len(symbols))
for _, s := range symbols {
ps := protocol.DocumentSymbol{
Name: s.Name,
Kind: toProtocolSymbolKind(s.Kind),
Detail: s.Detail,
Children: toProtocolDocumentSymbols(m, s.Children),
}
if r, err := m.Range(s.Span); err == nil {
ps.Range = r
}
if r, err := m.Range(s.SelectionSpan); err == nil {
ps.SelectionRange = r
}
result = append(result, ps)
}
return result
}
func toProtocolSymbolKind(kind source.SymbolKind) protocol.SymbolKind {
switch kind {
case source.StructSymbol:
return protocol.Struct
case source.PackageSymbol:
return protocol.Package
case source.VariableSymbol:
return protocol.Variable
case source.ConstantSymbol:
return protocol.Constant
case source.FunctionSymbol:
return protocol.Function
case source.MethodSymbol:
return protocol.Method
case source.InterfaceSymbol:
return protocol.Interface
case source.NumberSymbol:
return protocol.Number
case source.StringSymbol:
return protocol.String
case source.BooleanSymbol:
return protocol.Boolean
case source.FieldSymbol:
return protocol.Field
default:
return 0
}
return source.DocumentSymbols(ctx, view, f)
}

View File

@ -67,8 +67,8 @@ type Highlights map[string][]span.Span
type References map[span.Span][]span.Span
type Renames map[span.Span]string
type PrepareRenames map[span.Span]*source.PrepareItem
type Symbols map[span.URI][]source.Symbol
type SymbolsChildren map[string][]source.Symbol
type Symbols map[span.URI][]protocol.DocumentSymbol
type SymbolsChildren map[string][]protocol.DocumentSymbol
type Signatures map[span.Span]*source.SignatureInformation
type Links map[span.URI][]Link
@ -642,10 +642,19 @@ func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placehold
}
func (data *Data) collectSymbols(name string, spn span.Span, kind string, parentName string) {
sym := source.Symbol{
Name: name,
Kind: source.ParseSymbolKind(kind),
SelectionSpan: spn,
contents, err := data.Exported.FileContents(spn.URI().Filename())
if err != nil {
return
}
m := protocol.NewColumnMapper(spn.URI(), spn.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
rng, err := m.Range(spn)
if err != nil {
return
}
sym := protocol.DocumentSymbol{
Name: name,
Kind: protocol.ParseSymbolKind(kind),
SelectionRange: rng,
}
if parentName == "" {
data.Symbols[spn.URI()] = append(data.Symbols[spn.URI()], sym)