зеркало из https://github.com/golang/tools.git
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:
Родитель
2898d834dc
Коммит
8f05a32dce
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче