internal/lsp: offer basic type conversion candidates
When the expected type is a basic type, we will now offer a
corresponding type conversion candidate. For example:
var foo int64
foo = // offer "int64(<>)" as a candidate
The type conversion candidate will be ranked below matching concrete
candidates but above the sea of non-matching candidates.
This change broke almost every completion test. I added a new
completion option for literal candidates so tests can selectively ask
for literal completions.
Updates golang/go#36015.
Change-Id: I63fbdb33436d662a666c1ffd3b2d918d840dccc7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210288
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go
index 664e075..846a54b 100644
--- a/internal/lsp/completion_test.go
+++ b/internal/lsp/completion_test.go
@@ -15,6 +15,7 @@
Deep: false,
FuzzyMatching: false,
Documentation: true,
+ Literal: strings.Contains(string(src.URI()), "literal"),
})
if !strings.Contains(string(src.URI()), "builtins") {
got = tests.FilterBuiltins(got)
@@ -30,6 +31,7 @@
Placeholders: placeholders,
Deep: true,
FuzzyMatching: true,
+ Literal: true,
})
got := tests.FindItem(list, *items[expected.CompletionItem])
want := expected.PlainSnippet
@@ -99,6 +101,7 @@
got := r.callCompletion(t, src, source.CompletionOptions{
FuzzyMatching: true,
Deep: true,
+ Literal: true,
})
want := expected(t, test, items)
if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 342d095..9d948d8 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -322,7 +322,11 @@
} else if isTypeName(obj) {
// If obj is a *types.TypeName that didn't otherwise match, check
// if a literal object of this type makes a good candidate.
- c.literal(obj.Type(), imp)
+
+ // We only care about named types (i.e. don't want builtin types).
+ if _, isNamed := obj.Type().(*types.Named); isNamed {
+ c.literal(obj.Type(), imp)
+ }
}
// Favor shallow matches by lowering weight according to depth.
diff --git a/internal/lsp/source/completion_literal.go b/internal/lsp/source/completion_literal.go
index 9528671..7f62e57 100644
--- a/internal/lsp/source/completion_literal.go
+++ b/internal/lsp/source/completion_literal.go
@@ -20,6 +20,10 @@
// literal generates composite literal, function literal, and make()
// completion items.
func (c *completer) literal(literalType types.Type, imp *importInfo) {
+ if !c.opts.Literal {
+ return
+ }
+
expType := c.expectedType.objType
if c.expectedType.variadic {
@@ -124,8 +128,9 @@
case *types.Basic, *types.Signature:
// Add a literal completion for basic types that implement our
// expected interface (e.g. named string type http.Dir
- // implements http.FileSystem).
- if isInterface(expType) {
+ // implements http.FileSystem), or are identical to our expected
+ // type (i.e. yielding a type conversion such as "float64()").
+ if isInterface(expType) || types.Identical(expType, literalType) {
c.basicLiteral(t, typeName, float64(score), addlEdits)
}
}
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 21ac309..1c378ea 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -64,6 +64,7 @@
Documentation: true,
Deep: true,
FuzzyMatching: true,
+ Literal: true,
Budget: 100 * time.Millisecond,
},
ComputeEdits: myers.ComputeEdits,
@@ -123,6 +124,7 @@
Documentation bool
FullDocumentation bool
Placeholders bool
+ Literal bool
// Budget is the soft latency goal for completion requests. Most
// requests finish in a couple milliseconds, but in some cases deep
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index fbed335..41bf6bc 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -110,6 +110,7 @@
prefix, list := r.callCompletion(t, src, source.CompletionOptions{
Documentation: true,
FuzzyMatching: true,
+ Literal: strings.Contains(string(src.URI()), "literal"),
})
if !strings.Contains(string(src.URI()), "builtins") {
list = tests.FilterBuiltins(list)
@@ -130,6 +131,7 @@
_, list := r.callCompletion(t, src, source.CompletionOptions{
Placeholders: placeholders,
Deep: true,
+ Literal: true,
})
got := tests.FindItem(list, *items[expected.CompletionItem])
want := expected.PlainSnippet
@@ -234,6 +236,7 @@
prefix, list := r.callCompletion(t, src, source.CompletionOptions{
FuzzyMatching: true,
Deep: true,
+ Literal: true,
})
fuzzyMatcher := fuzzy.NewMatcher(prefix)
var got []protocol.CompletionItem
diff --git a/internal/lsp/testdata/snippets/literal_snippets.go.in b/internal/lsp/testdata/snippets/literal_snippets.go.in
index 57489fb..7233b07 100644
--- a/internal/lsp/testdata/snippets/literal_snippets.go.in
+++ b/internal/lsp/testdata/snippets/literal_snippets.go.in
@@ -160,3 +160,16 @@
sf = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}")
sf = foo. //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}")
}
+
+func _() {
+ float64() //@item(litFloat64, "float64()", "float64", "var")
+
+ var f float64
+ f = fl //@complete(" //", litFloat64),snippet(" //", litFloat64, "float64($0)", "float64($0)")
+
+ type myInt int
+ myInt() //@item(litMyInt, "myInt()", "", "var")
+
+ var mi myInt
+ mi = my //@snippet(" //", litMyInt, "myInt($0)", "myInt($0)")
+}
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 31e701c..14dc1bd 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -1,6 +1,6 @@
-- summary --
-CompletionsCount = 221
-CompletionSnippetCount = 51
+CompletionsCount = 222
+CompletionSnippetCount = 53
UnimportedCompletionsCount = 4
DeepCompletionsCount = 5
FuzzyCompletionsCount = 7