1
0
mirror of https://github.com/golang/go synced 2024-11-18 21:34:46 -07:00
go/internal/lsp/tests/completion.go
pjw 5091d647ee internal/lsp: reorganize the generated Go code for the lsp protocol
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>
2019-11-18 19:51:19 +00:00

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()
}