mirror of
https://github.com/golang/go.git
synced 2025-05-31 23:25:39 +00:00
Folding ranges need to be computed to present folding ranges that make sense when lineFoldingOnly is true. This change computes the folding ranges to include the lines that are contained within the start and end parenthesis/braces when applicable. Folding ranges are not returned when the contained nodes begin or end on the same lines as the parenthesis/brace. This is to avoid misleading folding ranges like the following in unformatted code: if true { fmt.Println("true") } else { fmt.Println("false") } ---folding "if true {}"---> if true { fmt.Println("false") } Change-Id: I2931d02837ad5f2dd96cc93da5ede59afd6bcdce Reviewed-on: https://go-review.googlesource.com/c/tools/+/192678 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
685 lines
20 KiB
Go
685 lines
20 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 tests
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/tools/go/expect"
|
|
"golang.org/x/tools/go/packages"
|
|
"golang.org/x/tools/go/packages/packagestest"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
"golang.org/x/tools/internal/span"
|
|
"golang.org/x/tools/internal/txtar"
|
|
)
|
|
|
|
// We hardcode the expected number of test cases to ensure that all tests
|
|
// are being executed. If a test is added, this number must be changed.
|
|
const (
|
|
ExpectedCompletionsCount = 155
|
|
ExpectedCompletionSnippetCount = 15
|
|
ExpectedDiagnosticsCount = 21
|
|
ExpectedFormatCount = 6
|
|
ExpectedImportCount = 2
|
|
ExpectedDefinitionsCount = 39
|
|
ExpectedTypeDefinitionsCount = 2
|
|
ExpectedFoldingRangesCount = 2
|
|
ExpectedHighlightsCount = 2
|
|
ExpectedReferencesCount = 6
|
|
ExpectedRenamesCount = 20
|
|
ExpectedPrepareRenamesCount = 8
|
|
ExpectedSymbolsCount = 1
|
|
ExpectedSignaturesCount = 21
|
|
ExpectedLinksCount = 4
|
|
)
|
|
|
|
const (
|
|
overlayFileSuffix = ".overlay"
|
|
goldenFileSuffix = ".golden"
|
|
inFileSuffix = ".in"
|
|
testModule = "golang.org/x/tools/internal/lsp"
|
|
)
|
|
|
|
var updateGolden = flag.Bool("golden", false, "Update golden files")
|
|
|
|
type Diagnostics map[span.URI][]source.Diagnostic
|
|
type CompletionItems map[token.Pos]*source.CompletionItem
|
|
type Completions map[span.Span][]token.Pos
|
|
type CompletionSnippets map[span.Span]CompletionSnippet
|
|
type FoldingRanges []span.Span
|
|
type Formats []span.Span
|
|
type Imports []span.Span
|
|
type Definitions map[span.Span]Definition
|
|
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 Signatures map[span.Span]*source.SignatureInformation
|
|
type Links map[span.URI][]Link
|
|
|
|
type Data struct {
|
|
Config packages.Config
|
|
Exported *packagestest.Exported
|
|
Diagnostics Diagnostics
|
|
CompletionItems CompletionItems
|
|
Completions Completions
|
|
CompletionSnippets CompletionSnippets
|
|
FoldingRanges FoldingRanges
|
|
Formats Formats
|
|
Imports Imports
|
|
Definitions Definitions
|
|
Highlights Highlights
|
|
References References
|
|
Renames Renames
|
|
PrepareRenames PrepareRenames
|
|
Symbols Symbols
|
|
symbolsChildren SymbolsChildren
|
|
Signatures Signatures
|
|
Links Links
|
|
|
|
t testing.TB
|
|
fragments map[string]string
|
|
dir string
|
|
golden map[string]*Golden
|
|
}
|
|
|
|
type Tests interface {
|
|
Diagnostics(*testing.T, Diagnostics)
|
|
Completion(*testing.T, Completions, CompletionSnippets, CompletionItems)
|
|
FoldingRange(*testing.T, FoldingRanges)
|
|
Format(*testing.T, Formats)
|
|
Import(*testing.T, Imports)
|
|
Definition(*testing.T, Definitions)
|
|
Highlight(*testing.T, Highlights)
|
|
Reference(*testing.T, References)
|
|
Rename(*testing.T, Renames)
|
|
PrepareRename(*testing.T, PrepareRenames)
|
|
Symbol(*testing.T, Symbols)
|
|
SignatureHelp(*testing.T, Signatures)
|
|
Link(*testing.T, Links)
|
|
}
|
|
|
|
type Definition struct {
|
|
Name string
|
|
IsType bool
|
|
OnlyHover bool
|
|
Src, Def span.Span
|
|
}
|
|
|
|
type CompletionSnippet struct {
|
|
CompletionItem token.Pos
|
|
PlainSnippet string
|
|
PlaceholderSnippet string
|
|
}
|
|
|
|
type Link struct {
|
|
Src span.Span
|
|
Target string
|
|
NotePosition token.Position
|
|
}
|
|
|
|
type Golden struct {
|
|
Filename string
|
|
Archive *txtar.Archive
|
|
Modified bool
|
|
}
|
|
|
|
func Context(t testing.TB) context.Context {
|
|
return context.Background()
|
|
}
|
|
|
|
func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
|
t.Helper()
|
|
|
|
data := &Data{
|
|
Diagnostics: make(Diagnostics),
|
|
CompletionItems: make(CompletionItems),
|
|
Completions: make(Completions),
|
|
CompletionSnippets: make(CompletionSnippets),
|
|
Definitions: make(Definitions),
|
|
Highlights: make(Highlights),
|
|
References: make(References),
|
|
Renames: make(Renames),
|
|
PrepareRenames: make(PrepareRenames),
|
|
Symbols: make(Symbols),
|
|
symbolsChildren: make(SymbolsChildren),
|
|
Signatures: make(Signatures),
|
|
Links: make(Links),
|
|
|
|
t: t,
|
|
dir: dir,
|
|
fragments: map[string]string{},
|
|
golden: map[string]*Golden{},
|
|
}
|
|
|
|
files := packagestest.MustCopyFileTree(dir)
|
|
overlays := map[string][]byte{}
|
|
for fragment, operation := range files {
|
|
if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment {
|
|
delete(files, fragment)
|
|
goldFile := filepath.Join(dir, fragment)
|
|
archive, err := txtar.ParseFile(goldFile)
|
|
if err != nil {
|
|
t.Fatalf("could not read golden file %v: %v", fragment, err)
|
|
}
|
|
data.golden[trimmed] = &Golden{
|
|
Filename: goldFile,
|
|
Archive: archive,
|
|
}
|
|
} else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment {
|
|
delete(files, fragment)
|
|
files[trimmed] = operation
|
|
} else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 {
|
|
delete(files, fragment)
|
|
partial := fragment[:index] + fragment[index+len(overlayFileSuffix):]
|
|
contents, err := ioutil.ReadFile(filepath.Join(dir, fragment))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
overlays[partial] = contents
|
|
}
|
|
}
|
|
modules := []packagestest.Module{
|
|
{
|
|
Name: testModule,
|
|
Files: files,
|
|
Overlay: overlays,
|
|
},
|
|
}
|
|
data.Exported = packagestest.Export(t, exporter, modules)
|
|
for fragment, _ := range files {
|
|
filename := data.Exported.File(testModule, fragment)
|
|
data.fragments[filename] = fragment
|
|
}
|
|
data.Exported.Config.Logf = nil
|
|
|
|
// Merge the exported.Config with the view.Config.
|
|
data.Config = *data.Exported.Config
|
|
data.Config.Fset = token.NewFileSet()
|
|
data.Config.Logf = nil
|
|
data.Config.Context = Context(nil)
|
|
data.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
|
|
panic("ParseFile should not be called")
|
|
}
|
|
|
|
// Do a first pass to collect special markers for completion.
|
|
if err := data.Exported.Expect(map[string]interface{}{
|
|
"item": func(name string, r packagestest.Range, _ []string) {
|
|
data.Exported.Mark(name, r)
|
|
},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Collect any data that needs to be used by subsequent tests.
|
|
if err := data.Exported.Expect(map[string]interface{}{
|
|
"diag": data.collectDiagnostics,
|
|
"item": data.collectCompletionItems,
|
|
"complete": data.collectCompletions,
|
|
"fold": data.collectFoldingRanges,
|
|
"format": data.collectFormats,
|
|
"import": data.collectImports,
|
|
"godef": data.collectDefinitions,
|
|
"typdef": data.collectTypeDefinitions,
|
|
"hover": data.collectHoverDefinitions,
|
|
"highlight": data.collectHighlights,
|
|
"refs": data.collectReferences,
|
|
"rename": data.collectRenames,
|
|
"prepare": data.collectPrepareRenames,
|
|
"symbol": data.collectSymbols,
|
|
"signature": data.collectSignatures,
|
|
"snippet": data.collectCompletionSnippets,
|
|
"link": data.collectLinks,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, symbols := range data.Symbols {
|
|
for i := range symbols {
|
|
children := data.symbolsChildren[symbols[i].Name]
|
|
symbols[i].Children = children
|
|
}
|
|
}
|
|
// Collect names for the entries that require golden files.
|
|
if err := data.Exported.Expect(map[string]interface{}{
|
|
"godef": data.collectDefinitionNames,
|
|
"hover": data.collectDefinitionNames,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return data
|
|
}
|
|
|
|
func Run(t *testing.T, tests Tests, data *Data) {
|
|
t.Helper()
|
|
t.Run("Completion", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Completions) != ExpectedCompletionsCount {
|
|
t.Errorf("got %v completions expected %v", len(data.Completions), ExpectedCompletionsCount)
|
|
}
|
|
if len(data.CompletionSnippets) != ExpectedCompletionSnippetCount {
|
|
t.Errorf("got %v snippets expected %v", len(data.CompletionSnippets), ExpectedCompletionSnippetCount)
|
|
}
|
|
tests.Completion(t, data.Completions, data.CompletionSnippets, data.CompletionItems)
|
|
})
|
|
|
|
t.Run("Diagnostics", func(t *testing.T) {
|
|
t.Helper()
|
|
diagnosticsCount := 0
|
|
for _, want := range data.Diagnostics {
|
|
diagnosticsCount += len(want)
|
|
}
|
|
if diagnosticsCount != ExpectedDiagnosticsCount {
|
|
t.Errorf("got %v diagnostics expected %v", diagnosticsCount, ExpectedDiagnosticsCount)
|
|
}
|
|
tests.Diagnostics(t, data.Diagnostics)
|
|
})
|
|
|
|
t.Run("FoldingRange", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.FoldingRanges) != ExpectedFoldingRangesCount {
|
|
t.Errorf("got %v folding ranges expected %v", len(data.FoldingRanges), ExpectedFoldingRangesCount)
|
|
}
|
|
tests.FoldingRange(t, data.FoldingRanges)
|
|
})
|
|
|
|
t.Run("Format", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Formats) != ExpectedFormatCount {
|
|
t.Errorf("got %v formats expected %v", len(data.Formats), ExpectedFormatCount)
|
|
}
|
|
tests.Format(t, data.Formats)
|
|
})
|
|
|
|
t.Run("Import", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Imports) != ExpectedImportCount {
|
|
t.Errorf("got %v imports expected %v", len(data.Imports), ExpectedImportCount)
|
|
}
|
|
tests.Import(t, data.Imports)
|
|
})
|
|
|
|
t.Run("Definition", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Definitions) != ExpectedDefinitionsCount {
|
|
t.Errorf("got %v definitions expected %v", len(data.Definitions), ExpectedDefinitionsCount)
|
|
}
|
|
tests.Definition(t, data.Definitions)
|
|
})
|
|
|
|
t.Run("Highlight", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Highlights) != ExpectedHighlightsCount {
|
|
t.Errorf("got %v highlights expected %v", len(data.Highlights), ExpectedHighlightsCount)
|
|
}
|
|
tests.Highlight(t, data.Highlights)
|
|
})
|
|
|
|
t.Run("References", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.References) != ExpectedReferencesCount {
|
|
t.Errorf("got %v references expected %v", len(data.References), ExpectedReferencesCount)
|
|
}
|
|
tests.Reference(t, data.References)
|
|
})
|
|
|
|
t.Run("Renames", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Renames) != ExpectedRenamesCount {
|
|
t.Errorf("got %v renames expected %v", len(data.Renames), ExpectedRenamesCount)
|
|
}
|
|
tests.Rename(t, data.Renames)
|
|
})
|
|
|
|
t.Run("PrepareRenames", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.PrepareRenames) != ExpectedPrepareRenamesCount {
|
|
t.Errorf("got %v prepare renames expected %v", len(data.PrepareRenames), ExpectedPrepareRenamesCount)
|
|
}
|
|
|
|
tests.PrepareRename(t, data.PrepareRenames)
|
|
})
|
|
|
|
t.Run("Symbols", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Symbols) != ExpectedSymbolsCount {
|
|
t.Errorf("got %v symbols expected %v", len(data.Symbols), ExpectedSymbolsCount)
|
|
}
|
|
tests.Symbol(t, data.Symbols)
|
|
})
|
|
|
|
t.Run("SignatureHelp", func(t *testing.T) {
|
|
t.Helper()
|
|
if len(data.Signatures) != ExpectedSignaturesCount {
|
|
t.Errorf("got %v signatures expected %v", len(data.Signatures), ExpectedSignaturesCount)
|
|
}
|
|
tests.SignatureHelp(t, data.Signatures)
|
|
})
|
|
|
|
t.Run("Link", func(t *testing.T) {
|
|
t.Helper()
|
|
linksCount := 0
|
|
for _, want := range data.Links {
|
|
linksCount += len(want)
|
|
}
|
|
if linksCount != ExpectedLinksCount {
|
|
t.Errorf("got %v links expected %v", linksCount, ExpectedLinksCount)
|
|
}
|
|
tests.Link(t, data.Links)
|
|
})
|
|
|
|
if *updateGolden {
|
|
for _, golden := range data.golden {
|
|
if !golden.Modified {
|
|
continue
|
|
}
|
|
sort.Slice(golden.Archive.Files, func(i, j int) bool {
|
|
return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name
|
|
})
|
|
if err := ioutil.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (data *Data) Golden(tag string, target string, update func() ([]byte, error)) []byte {
|
|
data.t.Helper()
|
|
fragment, found := data.fragments[target]
|
|
if !found {
|
|
if filepath.IsAbs(target) {
|
|
data.t.Fatalf("invalid golden file fragment %v", target)
|
|
}
|
|
fragment = target
|
|
}
|
|
golden := data.golden[fragment]
|
|
if golden == nil {
|
|
if !*updateGolden {
|
|
data.t.Fatalf("could not find golden file %v: %v", fragment, tag)
|
|
}
|
|
golden = &Golden{
|
|
Filename: filepath.Join(data.dir, fragment+goldenFileSuffix),
|
|
Archive: &txtar.Archive{},
|
|
Modified: true,
|
|
}
|
|
data.golden[fragment] = golden
|
|
}
|
|
var file *txtar.File
|
|
for i := range golden.Archive.Files {
|
|
f := &golden.Archive.Files[i]
|
|
if f.Name == tag {
|
|
file = f
|
|
break
|
|
}
|
|
}
|
|
if *updateGolden {
|
|
if file == nil {
|
|
golden.Archive.Files = append(golden.Archive.Files, txtar.File{
|
|
Name: tag,
|
|
})
|
|
file = &golden.Archive.Files[len(golden.Archive.Files)-1]
|
|
}
|
|
contents, err := update()
|
|
if err != nil {
|
|
data.t.Fatalf("could not update golden file %v: %v", fragment, err)
|
|
}
|
|
file.Data = append(contents, '\n') // add trailing \n for txtar
|
|
golden.Modified = true
|
|
}
|
|
if file == nil {
|
|
data.t.Fatalf("could not find golden contents %v: %v", fragment, tag)
|
|
}
|
|
return file.Data[:len(file.Data)-1] // drop the trailing \n
|
|
}
|
|
|
|
func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg string) {
|
|
if _, ok := data.Diagnostics[spn.URI()]; !ok {
|
|
data.Diagnostics[spn.URI()] = []source.Diagnostic{}
|
|
}
|
|
severity := source.SeverityError
|
|
if strings.Contains(string(spn.URI()), "analyzer") {
|
|
severity = source.SeverityWarning
|
|
}
|
|
// This is not the correct way to do this,
|
|
// but it seems excessive to do the full conversion here.
|
|
want := source.Diagnostic{
|
|
URI: spn.URI(),
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: float64(spn.Start().Line()) - 1,
|
|
Character: float64(spn.Start().Column()) - 1,
|
|
},
|
|
End: protocol.Position{
|
|
Line: float64(spn.End().Line()) - 1,
|
|
Character: float64(spn.End().Column()) - 1,
|
|
},
|
|
},
|
|
Severity: severity,
|
|
Source: msgSource,
|
|
Message: msg,
|
|
}
|
|
data.Diagnostics[spn.URI()] = append(data.Diagnostics[spn.URI()], want)
|
|
}
|
|
|
|
// diffDiagnostics prints the diff between expected and actual diagnostics test
|
|
// results.
|
|
func DiffDiagnostics(uri span.URI, want, got []source.Diagnostic) string {
|
|
sortDiagnostics(want)
|
|
sortDiagnostics(got)
|
|
|
|
if len(got) != len(want) {
|
|
return summarizeDiagnostics(-1, want, got, "different lengths got %v want %v", len(got), len(want))
|
|
}
|
|
for i, w := range want {
|
|
g := got[i]
|
|
if w.Message != g.Message {
|
|
return summarizeDiagnostics(i, want, got, "incorrect Message got %v want %v", g.Message, w.Message)
|
|
}
|
|
if protocol.ComparePosition(w.Range.Start, g.Range.Start) != 0 {
|
|
return summarizeDiagnostics(i, want, got, "incorrect Start got %v want %v", g.Range.Start, w.Range.Start)
|
|
}
|
|
// Special case for diagnostics on parse errors.
|
|
if strings.Contains(string(uri), "noparse") {
|
|
if protocol.ComparePosition(g.Range.Start, g.Range.End) != 0 || protocol.ComparePosition(w.Range.Start, g.Range.End) != 0 {
|
|
return summarizeDiagnostics(i, want, got, "incorrect End got %v want %v", g.Range.End, w.Range.Start)
|
|
}
|
|
} else if !protocol.IsPoint(g.Range) { // Accept any 'want' range if the diagnostic returns a zero-length range.
|
|
if protocol.ComparePosition(w.Range.End, g.Range.End) != 0 {
|
|
return summarizeDiagnostics(i, want, got, "incorrect End got %v want %v", g.Range.End, w.Range.End)
|
|
}
|
|
}
|
|
if w.Severity != g.Severity {
|
|
return summarizeDiagnostics(i, want, got, "incorrect Severity got %v want %v", g.Severity, w.Severity)
|
|
}
|
|
if w.Source != g.Source {
|
|
return summarizeDiagnostics(i, want, got, "incorrect Source got %v want %v", g.Source, w.Source)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func sortDiagnostics(d []source.Diagnostic) {
|
|
sort.Slice(d, func(i int, j int) bool {
|
|
return compareDiagnostic(d[i], d[j]) < 0
|
|
})
|
|
}
|
|
|
|
func compareDiagnostic(a, b source.Diagnostic) int {
|
|
if r := span.CompareURI(a.URI, b.URI); r != 0 {
|
|
return r
|
|
}
|
|
if r := protocol.CompareRange(a.Range, b.Range); r != 0 {
|
|
return r
|
|
}
|
|
if a.Message < b.Message {
|
|
return -1
|
|
}
|
|
if a.Message == b.Message {
|
|
return 0
|
|
} else {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func summarizeDiagnostics(i int, want []source.Diagnostic, got []source.Diagnostic, reason string, args ...interface{}) string {
|
|
msg := &bytes.Buffer{}
|
|
fmt.Fprint(msg, "diagnostics failed")
|
|
if i >= 0 {
|
|
fmt.Fprintf(msg, " at %d", i)
|
|
}
|
|
fmt.Fprint(msg, " because of ")
|
|
fmt.Fprintf(msg, reason, args...)
|
|
fmt.Fprint(msg, ":\nexpected:\n")
|
|
for _, d := range want {
|
|
fmt.Fprintf(msg, " %s:%v: %s\n", d.URI, d.Range, d.Message)
|
|
}
|
|
fmt.Fprintf(msg, "got:\n")
|
|
for _, d := range got {
|
|
fmt.Fprintf(msg, " %s:%v: %s\n", d.URI, d.Range, d.Message)
|
|
}
|
|
return msg.String()
|
|
}
|
|
|
|
func (data *Data) collectCompletions(src span.Span, expected []token.Pos) {
|
|
data.Completions[src] = expected
|
|
}
|
|
|
|
func (data *Data) collectCompletionItems(pos token.Pos, args []string) {
|
|
if len(args) < 3 {
|
|
return
|
|
}
|
|
label, detail, kind := args[0], args[1], args[2]
|
|
var documentation string
|
|
if len(args) == 4 {
|
|
documentation = args[3]
|
|
}
|
|
data.CompletionItems[pos] = &source.CompletionItem{
|
|
Label: label,
|
|
Detail: detail,
|
|
Kind: source.ParseCompletionItemKind(kind),
|
|
Documentation: documentation,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectFoldingRanges(spn span.Span) {
|
|
data.FoldingRanges = append(data.FoldingRanges, spn)
|
|
}
|
|
|
|
func (data *Data) collectFormats(spn span.Span) {
|
|
data.Formats = append(data.Formats, spn)
|
|
}
|
|
|
|
func (data *Data) collectImports(spn span.Span) {
|
|
data.Imports = append(data.Imports, spn)
|
|
}
|
|
|
|
func (data *Data) collectDefinitions(src, target span.Span) {
|
|
data.Definitions[src] = Definition{
|
|
Src: src,
|
|
Def: target,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectHoverDefinitions(src, target span.Span) {
|
|
data.Definitions[src] = Definition{
|
|
Src: src,
|
|
Def: target,
|
|
OnlyHover: true,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectTypeDefinitions(src, target span.Span) {
|
|
data.Definitions[src] = Definition{
|
|
Src: src,
|
|
Def: target,
|
|
IsType: true,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectDefinitionNames(src span.Span, name string) {
|
|
d := data.Definitions[src]
|
|
d.Name = name
|
|
data.Definitions[src] = d
|
|
}
|
|
|
|
func (data *Data) collectHighlights(name string, rng span.Span) {
|
|
data.Highlights[name] = append(data.Highlights[name], rng)
|
|
}
|
|
|
|
func (data *Data) collectReferences(src span.Span, expected []span.Span) {
|
|
data.References[src] = expected
|
|
}
|
|
|
|
func (data *Data) collectRenames(src span.Span, newText string) {
|
|
data.Renames[src] = newText
|
|
}
|
|
|
|
func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placeholder string) {
|
|
if int(rng.End-rng.Start) != len(placeholder) {
|
|
// If the length of the placeholder and the length of the range do not match,
|
|
// make the range just be the start.
|
|
rng = span.NewRange(rng.FileSet, rng.Start, rng.Start)
|
|
}
|
|
|
|
data.PrepareRenames[src] = &source.PrepareItem{
|
|
Range: rng,
|
|
Text: placeholder,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectSymbols(name string, spn span.Span, kind string, parentName string) {
|
|
sym := source.Symbol{
|
|
Name: name,
|
|
Kind: source.ParseSymbolKind(kind),
|
|
SelectionSpan: spn,
|
|
}
|
|
if parentName == "" {
|
|
data.Symbols[spn.URI()] = append(data.Symbols[spn.URI()], sym)
|
|
} else {
|
|
data.symbolsChildren[parentName] = append(data.symbolsChildren[parentName], sym)
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectSignatures(spn span.Span, signature string, activeParam int64) {
|
|
data.Signatures[spn] = &source.SignatureInformation{
|
|
Label: signature,
|
|
ActiveParameter: int(activeParam),
|
|
}
|
|
// Hardcode special case to test the lack of a signature.
|
|
if signature == "" && activeParam == 0 {
|
|
data.Signatures[spn] = nil
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectCompletionSnippets(spn span.Span, item token.Pos, plain, placeholder string) {
|
|
data.CompletionSnippets[spn] = CompletionSnippet{
|
|
CompletionItem: item,
|
|
PlainSnippet: plain,
|
|
PlaceholderSnippet: placeholder,
|
|
}
|
|
}
|
|
|
|
func (data *Data) collectLinks(spn span.Span, link string, note *expect.Note, fset *token.FileSet) {
|
|
position := fset.Position(note.Pos)
|
|
uri := spn.URI()
|
|
data.Links[uri] = append(data.Links[uri], Link{
|
|
Src: spn,
|
|
Target: link,
|
|
NotePosition: position,
|
|
})
|
|
}
|