diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go index 664e075901..846a54b467 100644 --- a/internal/lsp/completion_test.go +++ b/internal/lsp/completion_test.go @@ -15,6 +15,7 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, 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 @@ func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.C Placeholders: placeholders, Deep: true, FuzzyMatching: true, + Literal: true, }) got := tests.FindItem(list, *items[expected.CompletionItem]) want := expected.PlainSnippet @@ -99,6 +101,7 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi 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 342d095c64..9d948d8b7c 100644 --- a/internal/lsp/source/completion.go +++ b/internal/lsp/source/completion.go @@ -322,7 +322,11 @@ func (c *completer) found(obj types.Object, score float64, imp *importInfo) { } 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 95286718d0..7f62e57d30 100644 --- a/internal/lsp/source/completion_literal.go +++ b/internal/lsp/source/completion_literal.go @@ -20,6 +20,10 @@ import ( // 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 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) { 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 21ac309478..1c378ead69 100644 --- a/internal/lsp/source/options.go +++ b/internal/lsp/source/options.go @@ -64,6 +64,7 @@ var ( Documentation: true, Deep: true, FuzzyMatching: true, + Literal: true, Budget: 100 * time.Millisecond, }, ComputeEdits: myers.ComputeEdits, @@ -123,6 +124,7 @@ type CompletionOptions struct { 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 fbed335070..41bf6bcdeb 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -110,6 +110,7 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, 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 @@ func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.C _, 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 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi 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 57489fb3e3..7233b07e69 100644 --- a/internal/lsp/testdata/snippets/literal_snippets.go.in +++ b/internal/lsp/testdata/snippets/literal_snippets.go.in @@ -160,3 +160,16 @@ func _() { 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 31e701c6f5..14dc1bd1c2 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