mirror of
https://github.com/golang/go
synced 2024-11-19 00:54:42 -07:00
5091d647ee
Code generation has been unified, so that tsprotocol.go and tsserver.go are produced by the same program. tsprotocol.go is about 900 lines shorter, partly from removing boilerplate comments that golint no longer requires. (And partly by generating fewer unneeded types.) The choice made for a union type is commented with the set of types. There is no Go equivalent for union types, but making themn all interface{} would replace type checking at unmarshalling with checking runtime conversions. Intersection types (A&B) are sometimes embedded (struct{A;B;}, and sometimes expanded, as they have to be if A and B have fields with the same names. There are fewer embedded structs, which had been verbose and confusing to initialize. They have been replaced by types whose names end in Gn. Essentially all the generated *structs have been removed. This makes no difference in what the client sends, and the server may send a {} where it previously might have sent nothing. The benefit is that some nil tests can be removed. Thus 'omitempty' in json tags is just documentation that the element is optional in the protocol. The files that generate this code will be submitted later, but soon. Change-Id: I52b997d9c58de3d733fc8c6ce061e47ce2bdb100 Reviewed-on: https://go-review.googlesource.com/c/tools/+/207598 Run-TryBot: Peter Weinberger <pjw@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
187 lines
4.9 KiB
Go
187 lines
4.9 KiB
Go
package tests
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
)
|
|
|
|
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(items []protocol.CompletionItem) []protocol.CompletionItem {
|
|
var got []protocol.CompletionItem
|
|
for _, item := range items {
|
|
if isBuiltin(item.Label, item.Detail, item.Kind) {
|
|
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()
|
|
}
|