1
0
mirror of https://github.com/golang/go synced 2024-10-01 08:18:32 -06:00
go/internal/lsp/completion_test.go
Muir Manders cf2d1e09c8 internal/lsp/source: offer smart "append()" completions
Assigning a slice to the appendage of itself is common and tedious
enough to warrant a special case completion candidate. We now offer
smarter "append()" candidates:

    var foo []int
    foo = app<> // offer "append(foo, <>)"
    fo<> // offer "foo = append(foo, <>)"

The latter is only offered if the best completion candidate is a
slice. It is inserted as the second-best candidate because it seems
impossible to avoid annoying false positives if it is ranked first.

I added a new debug option to disable literal completions. This was to
clean up some test logic that was disabling snippets for all tests
just to defeat literal completions. My tests were failing mysteriously
due to having snippets disabled, and it was hard to figure out why.

Change-Id: I3e8313e00a1409840cb99d5d71c593435a7aeb71
Reviewed-on: https://go-review.googlesource.com/c/tools/+/221777
Run-TryBot: Muir Manders <muir@mnd.rs>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-05-20 22:05:37 +00:00

150 lines
4.5 KiB
Go

package lsp
import (
"strings"
"testing"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/span"
)
func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {
opts.DeepCompletion = false
opts.Matcher = source.CaseInsensitive
opts.UnimportedCompletion = false
opts.InsertTextFormat = protocol.SnippetTextFormat
if !strings.Contains(string(src.URI()), "literal") {
opts.LiteralCompletions = false
}
})
got = tests.FilterBuiltins(src, got)
want := expected(t, test, items)
if diff := tests.DiffCompletionItems(want, got); diff != "" {
t.Errorf("%s", diff)
}
}
func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
list := r.callCompletion(t, src, func(opts *source.Options) {
opts.Placeholders = placeholders
opts.DeepCompletion = true
opts.Matcher = source.Fuzzy
opts.UnimportedCompletion = false
})
got := tests.FindItem(list, *items[expected.CompletionItem])
want := expected.PlainSnippet
if placeholders {
want = expected.PlaceholderSnippet
}
if diff := tests.DiffSnippets(want, got); diff != "" {
t.Errorf("%s", diff)
}
}
func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {})
got = tests.FilterBuiltins(src, got)
want := expected(t, test, items)
if diff := tests.CheckCompletionOrder(want, got, false); diff != "" {
t.Errorf("%s", diff)
}
}
func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {
opts.DeepCompletion = true
opts.Matcher = source.CaseInsensitive
opts.UnimportedCompletion = false
})
got = tests.FilterBuiltins(src, got)
want := expected(t, test, items)
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s", msg)
}
}
func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {
opts.DeepCompletion = true
opts.Matcher = source.Fuzzy
opts.UnimportedCompletion = false
})
got = tests.FilterBuiltins(src, got)
want := expected(t, test, items)
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s", msg)
}
}
func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {
opts.Matcher = source.CaseSensitive
opts.UnimportedCompletion = false
})
got = tests.FilterBuiltins(src, got)
want := expected(t, test, items)
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s", msg)
}
}
func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, func(opts *source.Options) {
opts.DeepCompletion = true
opts.Matcher = source.Fuzzy
opts.UnimportedCompletion = false
})
want := expected(t, test, items)
if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
t.Errorf("%s", msg)
}
}
func expected(t *testing.T, test tests.Completion, items tests.CompletionItems) []protocol.CompletionItem {
t.Helper()
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
item := items[pos]
want = append(want, tests.ToProtocolCompletionItem(*item))
}
return want
}
func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) []protocol.CompletionItem {
t.Helper()
view, err := r.server.session.ViewOf(src.URI())
if err != nil {
t.Fatal(err)
}
original := view.Options()
modified := original
options(&modified)
view, err = view.SetOptions(r.ctx, modified)
if err != nil {
t.Error(err)
return nil
}
defer view.SetOptions(r.ctx, original)
list, err := r.server.Completion(r.ctx, &protocol.CompletionParams{
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: protocol.URIFromSpanURI(src.URI()),
},
Position: protocol.Position{
Line: float64(src.Start().Line() - 1),
Character: float64(src.Start().Column() - 1),
},
},
})
if err != nil {
t.Fatal(err)
}
return list.Items
}