internal/lsp: correctly report interface symbols

Updates golang/go#30915

Change-Id: I9c5faa615df506cf1d015a9eb48196fa9b0387ee
Reviewed-on: https://go-review.googlesource.com/c/tools/+/169682
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Zac Bergquist 2019-03-27 18:00:20 -06:00 коммит произвёл Rebecca Stambler
Родитель 2898d834dc
Коммит 8f05a32dce
4 изменённых файлов: 112 добавлений и 0 удалений

Просмотреть файл

@ -14,6 +14,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort"
"strings" "strings"
"testing" "testing"
@ -43,6 +44,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
const expectedDefinitionsCount = 16 const expectedDefinitionsCount = 16
const expectedTypeDefinitionsCount = 2 const expectedTypeDefinitionsCount = 2
const expectedHighlightsCount = 2 const expectedHighlightsCount = 2
const expectedSymbolsCount = 1
files := packagestest.MustCopyFileTree(dir) files := packagestest.MustCopyFileTree(dir)
for fragment, operation := range files { for fragment, operation := range files {
@ -87,6 +89,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
expectedDefinitions := make(definitions) expectedDefinitions := make(definitions)
expectedTypeDefinitions := make(definitions) expectedTypeDefinitions := make(definitions)
expectedHighlights := make(highlights) expectedHighlights := make(highlights)
expectedSymbols := make(symbols)
// Collect any data that needs to be used by subsequent tests. // Collect any data that needs to be used by subsequent tests.
if err := exported.Expect(map[string]interface{}{ if err := exported.Expect(map[string]interface{}{
@ -97,6 +100,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
"godef": expectedDefinitions.collect, "godef": expectedDefinitions.collect,
"typdef": expectedTypeDefinitions.collect, "typdef": expectedTypeDefinitions.collect,
"highlight": expectedHighlights.collect, "highlight": expectedHighlights.collect,
"symbol": expectedSymbols.collect,
}); err != nil { }); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -168,6 +172,16 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
} }
expectedHighlights.test(t, s) expectedHighlights.test(t, s)
}) })
t.Run("Symbols", func(t *testing.T) {
t.Helper()
if goVersion111 { // TODO(rstambler): Remove this when we no longer support Go 1.10.
if len(expectedSymbols) != expectedSymbolsCount {
t.Errorf("got %v symbols expected %v", len(expectedSymbols), expectedSymbolsCount)
}
}
expectedSymbols.test(t, s)
})
} }
type diagnostics map[span.URI][]protocol.Diagnostic type diagnostics map[span.URI][]protocol.Diagnostic
@ -176,6 +190,7 @@ type completions map[token.Position][]token.Pos
type formats map[string]string type formats map[string]string
type definitions map[protocol.Location]protocol.Location type definitions map[protocol.Location]protocol.Location
type highlights map[string][]protocol.Location type highlights map[string][]protocol.Location
type symbols map[span.URI][]protocol.DocumentSymbol
func (d diagnostics) test(t *testing.T, v source.View) int { func (d diagnostics) test(t *testing.T, v source.View) int {
count := 0 count := 0
@ -503,6 +518,70 @@ func (h highlights) test(t *testing.T, s *server) {
} }
} }
func (s symbols) collect(e *packagestest.Exported, fset *token.FileSet, name string, rng span.Range, kind int64) {
f := fset.File(rng.Start)
if f == nil {
return
}
content, err := e.FileContents(f.Name())
if err != nil {
return
}
spn, err := rng.Span()
if err != nil {
return
}
m := protocol.NewColumnMapper(spn.URI(), fset, f, content)
prng, err := m.Range(spn)
if err != nil {
return
}
s[spn.URI()] = append(s[spn.URI()], protocol.DocumentSymbol{
Name: name,
Kind: protocol.SymbolKind(kind),
SelectionRange: prng,
})
}
func (s symbols) test(t *testing.T, server *server) {
for uri, expectedSymbols := range s {
params := &protocol.DocumentSymbolParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: string(uri),
},
}
symbols, err := server.DocumentSymbol(context.Background(), params)
if err != nil {
t.Fatal(err)
}
if len(symbols) != len(expectedSymbols) {
t.Errorf("want %d symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
continue
}
sort.Slice(symbols, func(i, j int) bool { return symbols[i].Name < symbols[j].Name })
sort.Slice(expectedSymbols, func(i, j int) bool { return expectedSymbols[i].Name < expectedSymbols[j].Name })
for i, w := range expectedSymbols {
g := symbols[i]
if w.Name != g.Name {
t.Errorf("%s: want symbol %q, got %q", uri, w.Name, g.Name)
continue
}
if w.Kind != g.Kind {
t.Errorf("%s: want kind %v for %s, got %v", uri, w.Kind, w.Name, g.Kind)
}
if w.SelectionRange != g.SelectionRange {
t.Errorf("%s: want selection range %v for %s, got %v", uri, w.SelectionRange, w.Name, g.SelectionRange)
}
}
}
}
func testLocation(e *packagestest.Exported, fset *token.FileSet, rng packagestest.Range) (span.Span, *protocol.ColumnMapper) { func testLocation(e *packagestest.Exported, fset *token.FileSet, rng packagestest.Range) (span.Span, *protocol.ColumnMapper) {
spn, err := span.NewRange(fset, rng.Start, rng.End).Span() spn, err := span.NewRange(fset, rng.Start, rng.End).Span()
if err != nil { if err != nil {

Просмотреть файл

@ -23,6 +23,7 @@ const (
ConstantSymbol ConstantSymbol
FunctionSymbol FunctionSymbol
MethodSymbol MethodSymbol
InterfaceSymbol
) )
type Symbol struct { type Symbol struct {
@ -100,6 +101,9 @@ func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q typ
Name: obj.Name(), Name: obj.Name(),
Kind: StructSymbol, Kind: StructSymbol,
} }
if types.IsInterface(obj.Type()) {
s.Kind = InterfaceSymbol
}
if span, err := nodeSpan(spec, fset); err == nil { if span, err := nodeSpan(spec, fset); err == nil {
s.Span = span s.Span = span
} }

Просмотреть файл

@ -43,6 +43,8 @@ func toProtocolSymbolKind(kind source.SymbolKind) protocol.SymbolKind {
return protocol.Function return protocol.Function
case source.MethodSymbol: case source.MethodSymbol:
return protocol.Method return protocol.Method
case source.InterfaceSymbol:
return protocol.Interface
default: default:
return 0 return 0
} }

27
internal/lsp/testdata/symbols/main.go поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
package main
var x = 42 //@symbol("x", "x", 13)
const y = 43 //@symbol("y", "y", 14)
type Foo struct { //@symbol("Foo", "Foo", 23)
Quux
Bar int
baz string
}
type Quux struct { //@symbol("Quux", "Quux", 23)
X float64
}
func (f Foo) Baz() string { //@symbol("Baz", "Baz", 6)
return f.baz
}
func main() { //@symbol("main", "main", 12)
}
type Stringer interface { //@symbol("Stringer", "Stringer", 11)
String() string
}