mirror of https://github.com/golang/go.git
internal/lsp: move all of the test helpers functions into one file
Each of these files had a couple of functions that were very similar in nature. There's no need to have separate files for all of these. Change-Id: I4ca648d1b7e90539f274871d45b7c97a8111631f Reviewed-on: https://go-review.googlesource.com/c/tools/+/218319 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
parent
f8c9a4c3ab
commit
37215997d4
|
|
@ -1,197 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
func ToProtocolCompletionItems(items []source.CompletionItem) []protocol.CompletionItem {
|
||||
var result []protocol.CompletionItem
|
||||
for _, item := range items {
|
||||
result = append(result, ToProtocolCompletionItem(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToProtocolCompletionItem(item source.CompletionItem) protocol.CompletionItem {
|
||||
pItem := protocol.CompletionItem{
|
||||
Label: item.Label,
|
||||
Kind: item.Kind,
|
||||
Detail: item.Detail,
|
||||
Documentation: item.Documentation,
|
||||
InsertText: item.InsertText,
|
||||
TextEdit: &protocol.TextEdit{
|
||||
NewText: item.Snippet(),
|
||||
},
|
||||
// Negate score so best score has lowest sort text like real API.
|
||||
SortText: fmt.Sprint(-item.Score),
|
||||
}
|
||||
if pItem.InsertText == "" {
|
||||
pItem.InsertText = pItem.Label
|
||||
}
|
||||
return pItem
|
||||
}
|
||||
|
||||
func FilterBuiltins(src span.Span, items []protocol.CompletionItem) []protocol.CompletionItem {
|
||||
var (
|
||||
got []protocol.CompletionItem
|
||||
wantBuiltins = strings.Contains(string(src.URI()), "builtins")
|
||||
wantKeywords = strings.Contains(string(src.URI()), "keywords")
|
||||
)
|
||||
for _, item := range items {
|
||||
if !wantBuiltins && isBuiltin(item.Label, item.Detail, item.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !wantKeywords && token.Lookup(item.Label).IsKeyword() {
|
||||
continue
|
||||
}
|
||||
|
||||
got = append(got, item)
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func isBuiltin(label, detail string, kind protocol.CompletionItemKind) bool {
|
||||
if detail == "" && kind == protocol.ClassCompletion {
|
||||
return true
|
||||
}
|
||||
// Remaining builtin constants, variables, interfaces, and functions.
|
||||
trimmed := label
|
||||
if i := strings.Index(trimmed, "("); i >= 0 {
|
||||
trimmed = trimmed[:i]
|
||||
}
|
||||
switch trimmed {
|
||||
case "append", "cap", "close", "complex", "copy", "delete",
|
||||
"error", "false", "imag", "iota", "len", "make", "new",
|
||||
"nil", "panic", "print", "println", "real", "recover", "true":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CheckCompletionOrder(want, got []protocol.CompletionItem, strictScores bool) string {
|
||||
var (
|
||||
matchedIdxs []int
|
||||
lastGotIdx int
|
||||
lastGotSort float64
|
||||
inOrder = true
|
||||
errorMsg = "completions out of order"
|
||||
)
|
||||
for _, w := range want {
|
||||
var found bool
|
||||
for i, g := range got {
|
||||
if w.Label == g.Label && w.Detail == g.Detail && w.Kind == g.Kind {
|
||||
matchedIdxs = append(matchedIdxs, i)
|
||||
found = true
|
||||
|
||||
if i < lastGotIdx {
|
||||
inOrder = false
|
||||
}
|
||||
lastGotIdx = i
|
||||
|
||||
sort, _ := strconv.ParseFloat(g.SortText, 64)
|
||||
if strictScores && len(matchedIdxs) > 1 && sort <= lastGotSort {
|
||||
inOrder = false
|
||||
errorMsg = "candidate scores not strictly decreasing"
|
||||
}
|
||||
lastGotSort = sort
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return summarizeCompletionItems(-1, []protocol.CompletionItem{w}, got, "didn't find expected completion")
|
||||
}
|
||||
}
|
||||
|
||||
sort.Ints(matchedIdxs)
|
||||
matched := make([]protocol.CompletionItem, 0, len(matchedIdxs))
|
||||
for _, idx := range matchedIdxs {
|
||||
matched = append(matched, got[idx])
|
||||
}
|
||||
|
||||
if !inOrder {
|
||||
return summarizeCompletionItems(-1, want, matched, errorMsg)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func DiffSnippets(want string, got *protocol.CompletionItem) string {
|
||||
if want == "" {
|
||||
if got != nil {
|
||||
return fmt.Sprintf("expected no snippet but got %s", got.TextEdit.NewText)
|
||||
}
|
||||
} else {
|
||||
if got == nil {
|
||||
return fmt.Sprintf("couldn't find completion matching %q", want)
|
||||
}
|
||||
if want != got.TextEdit.NewText {
|
||||
return fmt.Sprintf("expected snippet %q, got %q", want, got.TextEdit.NewText)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func FindItem(list []protocol.CompletionItem, want source.CompletionItem) *protocol.CompletionItem {
|
||||
for _, item := range list {
|
||||
if item.Label == want.Label {
|
||||
return &item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiffCompletionItems prints the diff between expected and actual completion
|
||||
// test results.
|
||||
func DiffCompletionItems(want, got []protocol.CompletionItem) string {
|
||||
if len(got) != len(want) {
|
||||
return summarizeCompletionItems(-1, want, got, "different lengths got %v want %v", len(got), len(want))
|
||||
}
|
||||
for i, w := range want {
|
||||
g := got[i]
|
||||
if w.Label != g.Label {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Label got %v want %v", g.Label, w.Label)
|
||||
}
|
||||
if w.Detail != g.Detail {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Detail got %v want %v", g.Detail, w.Detail)
|
||||
}
|
||||
if w.Documentation != "" && !strings.HasPrefix(w.Documentation, "@") {
|
||||
if w.Documentation != g.Documentation {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Documentation got %v want %v", g.Documentation, w.Documentation)
|
||||
}
|
||||
}
|
||||
if w.Kind != g.Kind {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Kind got %v want %v", g.Kind, w.Kind)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeCompletionItems(i int, want, got []protocol.CompletionItem, reason string, args ...interface{}) string {
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprint(msg, "completion 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, " %v\n", d)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, d := range got {
|
||||
fmt.Fprintf(msg, " %v\n", d)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// DiffDiagnostics prints the diff between expected and actual diagnostics test
|
||||
// results.
|
||||
func DiffDiagnostics(uri span.URI, want, got []source.Diagnostic) string {
|
||||
source.SortDiagnostics(want)
|
||||
source.SortDiagnostics(got)
|
||||
|
||||
if len(got) != len(want) {
|
||||
return summarizeDiagnostics(-1, uri, 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, uri, want, got, "incorrect Message got %v want %v", g.Message, w.Message)
|
||||
}
|
||||
if w.Severity != g.Severity {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Severity got %v want %v", g.Severity, w.Severity)
|
||||
}
|
||||
if w.Source != g.Source {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Source got %v want %v", g.Source, w.Source)
|
||||
}
|
||||
// Don't check the range on the badimport test.
|
||||
if strings.Contains(uri.Filename(), "badimport") {
|
||||
continue
|
||||
}
|
||||
if protocol.ComparePosition(w.Range.Start, g.Range.Start) != 0 {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Start got %v want %v", g.Range.Start, w.Range.Start)
|
||||
}
|
||||
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, uri, want, got, "incorrect End got %v want %v", g.Range.End, w.Range.End)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeDiagnostics(i int, uri span.URI, 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", uri, d.Range, d.Message)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, d := range got {
|
||||
fmt.Fprintf(msg, " %s:%v: %s\n", uri, d.Range, d.Message)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
// 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 (
|
||||
"fmt"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// DiffLinks takes the links we got and checks if they are located within the source or a Note.
|
||||
// If the link is within a Note, the link is removed.
|
||||
// Returns an diff comment if there are differences and empty string if no diffs
|
||||
func DiffLinks(mapper *protocol.ColumnMapper, wantLinks []Link, gotLinks []protocol.DocumentLink) string {
|
||||
var notePositions []token.Position
|
||||
links := make(map[span.Span]string, len(wantLinks))
|
||||
for _, link := range wantLinks {
|
||||
links[link.Src] = link.Target
|
||||
notePositions = append(notePositions, link.NotePosition)
|
||||
}
|
||||
for _, link := range gotLinks {
|
||||
spn, err := mapper.RangeSpan(link.Range)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%v", err)
|
||||
}
|
||||
linkInNote := false
|
||||
for _, notePosition := range notePositions {
|
||||
// Drop the links found inside expectation notes arguments as this links are not collected by expect package
|
||||
if notePosition.Line == spn.Start().Line() &&
|
||||
notePosition.Column <= spn.Start().Column() {
|
||||
delete(links, spn)
|
||||
linkInNote = true
|
||||
}
|
||||
}
|
||||
if linkInNote {
|
||||
continue
|
||||
}
|
||||
if target, ok := links[spn]; ok {
|
||||
delete(links, spn)
|
||||
if target != link.Target {
|
||||
return fmt.Sprintf("for %v want %v, got %v\n", spn, link.Target, target)
|
||||
}
|
||||
} else {
|
||||
return fmt.Sprintf("unexpected link %v:%v\n", spn, link.Target)
|
||||
}
|
||||
}
|
||||
for spn, target := range links {
|
||||
return fmt.Sprintf("missing link %v:%v\n", spn, target)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
// Copyright 2020 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"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// DiffSymbols prints the diff between expected and actual symbols test results.
|
||||
func 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(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(t, i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
|
||||
}
|
||||
if w.Kind != g.Kind {
|
||||
return summarizeSymbols(t, i, want, got, "incorrect kind got %v want %v", g.Kind, w.Kind)
|
||||
}
|
||||
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 := DiffSymbols(t, uri, w.Children, g.Children); msg != "" {
|
||||
return fmt.Sprintf("children of %s: %s", w.Name, msg)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Fprintf(msg, " at %d", i)
|
||||
}
|
||||
fmt.Fprint(msg, " because of ")
|
||||
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.SelectionRange)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, s := range got {
|
||||
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionRange)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
|
@ -1,13 +1,164 @@
|
|||
// Copyright 2020 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"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
// DiffLinks takes the links we got and checks if they are located within the source or a Note.
|
||||
// If the link is within a Note, the link is removed.
|
||||
// Returns an diff comment if there are differences and empty string if no diffs
|
||||
func DiffLinks(mapper *protocol.ColumnMapper, wantLinks []Link, gotLinks []protocol.DocumentLink) string {
|
||||
var notePositions []token.Position
|
||||
links := make(map[span.Span]string, len(wantLinks))
|
||||
for _, link := range wantLinks {
|
||||
links[link.Src] = link.Target
|
||||
notePositions = append(notePositions, link.NotePosition)
|
||||
}
|
||||
for _, link := range gotLinks {
|
||||
spn, err := mapper.RangeSpan(link.Range)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%v", err)
|
||||
}
|
||||
linkInNote := false
|
||||
for _, notePosition := range notePositions {
|
||||
// Drop the links found inside expectation notes arguments as this links are not collected by expect package
|
||||
if notePosition.Line == spn.Start().Line() &&
|
||||
notePosition.Column <= spn.Start().Column() {
|
||||
delete(links, spn)
|
||||
linkInNote = true
|
||||
}
|
||||
}
|
||||
if linkInNote {
|
||||
continue
|
||||
}
|
||||
if target, ok := links[spn]; ok {
|
||||
delete(links, spn)
|
||||
if target != link.Target {
|
||||
return fmt.Sprintf("for %v want %v, got %v\n", spn, link.Target, target)
|
||||
}
|
||||
} else {
|
||||
return fmt.Sprintf("unexpected link %v:%v\n", spn, link.Target)
|
||||
}
|
||||
}
|
||||
for spn, target := range links {
|
||||
return fmt.Sprintf("missing link %v:%v\n", spn, target)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// DiffSymbols prints the diff between expected and actual symbols test results.
|
||||
func 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(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(t, i, want, got, "incorrect name got %v want %v", g.Name, w.Name)
|
||||
}
|
||||
if w.Kind != g.Kind {
|
||||
return summarizeSymbols(t, i, want, got, "incorrect kind got %v want %v", g.Kind, w.Kind)
|
||||
}
|
||||
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 := DiffSymbols(t, uri, w.Children, g.Children); msg != "" {
|
||||
return fmt.Sprintf("children of %s: %s", w.Name, msg)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Fprintf(msg, " at %d", i)
|
||||
}
|
||||
fmt.Fprint(msg, " because of ")
|
||||
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.SelectionRange)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, s := range got {
|
||||
fmt.Fprintf(msg, " %v %v %v\n", s.Name, s.Kind, s.SelectionRange)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
// DiffDiagnostics prints the diff between expected and actual diagnostics test
|
||||
// results.
|
||||
func DiffDiagnostics(uri span.URI, want, got []source.Diagnostic) string {
|
||||
source.SortDiagnostics(want)
|
||||
source.SortDiagnostics(got)
|
||||
|
||||
if len(got) != len(want) {
|
||||
return summarizeDiagnostics(-1, uri, 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, uri, want, got, "incorrect Message got %v want %v", g.Message, w.Message)
|
||||
}
|
||||
if w.Severity != g.Severity {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Severity got %v want %v", g.Severity, w.Severity)
|
||||
}
|
||||
if w.Source != g.Source {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Source got %v want %v", g.Source, w.Source)
|
||||
}
|
||||
// Don't check the range on the badimport test.
|
||||
if strings.Contains(uri.Filename(), "badimport") {
|
||||
continue
|
||||
}
|
||||
if protocol.ComparePosition(w.Range.Start, g.Range.Start) != 0 {
|
||||
return summarizeDiagnostics(i, uri, want, got, "incorrect Start got %v want %v", g.Range.Start, w.Range.Start)
|
||||
}
|
||||
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, uri, want, got, "incorrect End got %v want %v", g.Range.End, w.Range.End)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeDiagnostics(i int, uri span.URI, 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", uri, d.Range, d.Message)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, d := range got {
|
||||
fmt.Fprintf(msg, " %s:%v: %s\n", uri, d.Range, d.Message)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
func DiffSignatures(spn span.Span, want, got *protocol.SignatureHelp) string {
|
||||
decorate := func(f string, args ...interface{}) string {
|
||||
return fmt.Sprintf("Invalid signature at %s: %s", spn, fmt.Sprintf(f, args...))
|
||||
|
|
@ -42,3 +193,186 @@ func DiffSignatures(spn span.Span, want, got *protocol.SignatureHelp) string {
|
|||
|
||||
return ""
|
||||
}
|
||||
|
||||
func ToProtocolCompletionItems(items []source.CompletionItem) []protocol.CompletionItem {
|
||||
var result []protocol.CompletionItem
|
||||
for _, item := range items {
|
||||
result = append(result, ToProtocolCompletionItem(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToProtocolCompletionItem(item source.CompletionItem) protocol.CompletionItem {
|
||||
pItem := protocol.CompletionItem{
|
||||
Label: item.Label,
|
||||
Kind: item.Kind,
|
||||
Detail: item.Detail,
|
||||
Documentation: item.Documentation,
|
||||
InsertText: item.InsertText,
|
||||
TextEdit: &protocol.TextEdit{
|
||||
NewText: item.Snippet(),
|
||||
},
|
||||
// Negate score so best score has lowest sort text like real API.
|
||||
SortText: fmt.Sprint(-item.Score),
|
||||
}
|
||||
if pItem.InsertText == "" {
|
||||
pItem.InsertText = pItem.Label
|
||||
}
|
||||
return pItem
|
||||
}
|
||||
|
||||
func FilterBuiltins(src span.Span, items []protocol.CompletionItem) []protocol.CompletionItem {
|
||||
var (
|
||||
got []protocol.CompletionItem
|
||||
wantBuiltins = strings.Contains(string(src.URI()), "builtins")
|
||||
wantKeywords = strings.Contains(string(src.URI()), "keywords")
|
||||
)
|
||||
for _, item := range items {
|
||||
if !wantBuiltins && isBuiltin(item.Label, item.Detail, item.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !wantKeywords && token.Lookup(item.Label).IsKeyword() {
|
||||
continue
|
||||
}
|
||||
|
||||
got = append(got, item)
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func isBuiltin(label, detail string, kind protocol.CompletionItemKind) bool {
|
||||
if detail == "" && kind == protocol.ClassCompletion {
|
||||
return true
|
||||
}
|
||||
// Remaining builtin constants, variables, interfaces, and functions.
|
||||
trimmed := label
|
||||
if i := strings.Index(trimmed, "("); i >= 0 {
|
||||
trimmed = trimmed[:i]
|
||||
}
|
||||
switch trimmed {
|
||||
case "append", "cap", "close", "complex", "copy", "delete",
|
||||
"error", "false", "imag", "iota", "len", "make", "new",
|
||||
"nil", "panic", "print", "println", "real", "recover", "true":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CheckCompletionOrder(want, got []protocol.CompletionItem, strictScores bool) string {
|
||||
var (
|
||||
matchedIdxs []int
|
||||
lastGotIdx int
|
||||
lastGotSort float64
|
||||
inOrder = true
|
||||
errorMsg = "completions out of order"
|
||||
)
|
||||
for _, w := range want {
|
||||
var found bool
|
||||
for i, g := range got {
|
||||
if w.Label == g.Label && w.Detail == g.Detail && w.Kind == g.Kind {
|
||||
matchedIdxs = append(matchedIdxs, i)
|
||||
found = true
|
||||
|
||||
if i < lastGotIdx {
|
||||
inOrder = false
|
||||
}
|
||||
lastGotIdx = i
|
||||
|
||||
sort, _ := strconv.ParseFloat(g.SortText, 64)
|
||||
if strictScores && len(matchedIdxs) > 1 && sort <= lastGotSort {
|
||||
inOrder = false
|
||||
errorMsg = "candidate scores not strictly decreasing"
|
||||
}
|
||||
lastGotSort = sort
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return summarizeCompletionItems(-1, []protocol.CompletionItem{w}, got, "didn't find expected completion")
|
||||
}
|
||||
}
|
||||
|
||||
sort.Ints(matchedIdxs)
|
||||
matched := make([]protocol.CompletionItem, 0, len(matchedIdxs))
|
||||
for _, idx := range matchedIdxs {
|
||||
matched = append(matched, got[idx])
|
||||
}
|
||||
|
||||
if !inOrder {
|
||||
return summarizeCompletionItems(-1, want, matched, errorMsg)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func DiffSnippets(want string, got *protocol.CompletionItem) string {
|
||||
if want == "" {
|
||||
if got != nil {
|
||||
return fmt.Sprintf("expected no snippet but got %s", got.TextEdit.NewText)
|
||||
}
|
||||
} else {
|
||||
if got == nil {
|
||||
return fmt.Sprintf("couldn't find completion matching %q", want)
|
||||
}
|
||||
if want != got.TextEdit.NewText {
|
||||
return fmt.Sprintf("expected snippet %q, got %q", want, got.TextEdit.NewText)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func FindItem(list []protocol.CompletionItem, want source.CompletionItem) *protocol.CompletionItem {
|
||||
for _, item := range list {
|
||||
if item.Label == want.Label {
|
||||
return &item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiffCompletionItems prints the diff between expected and actual completion
|
||||
// test results.
|
||||
func DiffCompletionItems(want, got []protocol.CompletionItem) string {
|
||||
if len(got) != len(want) {
|
||||
return summarizeCompletionItems(-1, want, got, "different lengths got %v want %v", len(got), len(want))
|
||||
}
|
||||
for i, w := range want {
|
||||
g := got[i]
|
||||
if w.Label != g.Label {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Label got %v want %v", g.Label, w.Label)
|
||||
}
|
||||
if w.Detail != g.Detail {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Detail got %v want %v", g.Detail, w.Detail)
|
||||
}
|
||||
if w.Documentation != "" && !strings.HasPrefix(w.Documentation, "@") {
|
||||
if w.Documentation != g.Documentation {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Documentation got %v want %v", g.Documentation, w.Documentation)
|
||||
}
|
||||
}
|
||||
if w.Kind != g.Kind {
|
||||
return summarizeCompletionItems(i, want, got, "incorrect Kind got %v want %v", g.Kind, w.Kind)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeCompletionItems(i int, want, got []protocol.CompletionItem, reason string, args ...interface{}) string {
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprint(msg, "completion 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, " %v\n", d)
|
||||
}
|
||||
fmt.Fprintf(msg, "got:\n")
|
||||
for _, d := range got {
|
||||
fmt.Fprintf(msg, " %v\n", d)
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue