From a044388aa56f5a38fbbd1edf55194a3b748b701f Mon Sep 17 00:00:00 2001 From: Muir Manders Date: Mon, 16 Sep 2019 19:07:16 -0700 Subject: [PATCH] internal/lsp: add literal completions for basic types Normally you don't want literal candidates for basic types (e.g. "int(0)") since you can type the literal value without the type name. One exception is if you are creating a named basic type that implements an interface. For example: http.Handle("/", http.FileServer(<>)) will now give "http.Dir()" as a candidate since http.Dir is a named string type that implements the required interface http.FileSystem. Change-Id: Id2470c45e469ea25cd0f9849cfdad19ac0e784bb Reviewed-on: https://go-review.googlesource.com/c/tools/+/195838 Run-TryBot: Rebecca Stambler TryBot-Result: Gobot Gobot Reviewed-by: Rebecca Stambler --- internal/lsp/source/completion_literal.go | 27 +++++++++++++++++++ .../lsp/testdata/snippets/literal_snippets.go | 8 ++++++ internal/lsp/tests/tests.go | 2 +- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/internal/lsp/source/completion_literal.go b/internal/lsp/source/completion_literal.go index cf59079e7d..9254605013 100644 --- a/internal/lsp/source/completion_literal.go +++ b/internal/lsp/source/completion_literal.go @@ -85,6 +85,13 @@ func (c *completer) literal(literalType types.Type) { switch t := literalType.Underlying().(type) { case *types.Struct, *types.Array, *types.Slice, *types.Map: c.compositeLiteral(t, typeName, float64(score)) + case *types.Basic: + // Add a literal completion for basic types that implement our + // expected interface (e.g. named string type http.Dir + // implements http.FileSystem). + if isInterface(c.expectedType.objType) { + c.basicLiteral(t, typeName, float64(score)) + } } } @@ -261,6 +268,26 @@ func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore f }) } +// basicLiteral adds a literal completion item for the given basic +// type name typeName. +func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64) { + snip := &snippet.Builder{} + snip.WriteText(typeName + "(") + snip.WriteFinalTabstop() + snip.WriteText(")") + + nonSnippet := typeName + "()" + + c.items = append(c.items, CompletionItem{ + Label: nonSnippet, + InsertText: nonSnippet, + Detail: T.String(), + Score: matchScore * literalCandidateScore, + Kind: VariableCompletionItem, + snippet: snip, + }) +} + // makeCall adds a completion item for a "make()" call given a specific type. func (c *completer) makeCall(typeName string, secondArg string, matchScore float64) { // Keep it simple and don't add any placeholders for optional "make()" arguments. diff --git a/internal/lsp/testdata/snippets/literal_snippets.go b/internal/lsp/testdata/snippets/literal_snippets.go index 92fe6a389d..658baf39cd 100644 --- a/internal/lsp/testdata/snippets/literal_snippets.go +++ b/internal/lsp/testdata/snippets/literal_snippets.go @@ -66,6 +66,10 @@ func (myImpl) foo() {} func (*myImpl) bar() {} +type myBasicImpl string + +func (myBasicImpl) foo() {} + func _() { type myIntf interface { foo() @@ -76,6 +80,10 @@ func _() { var mi myIntf mi = m //@snippet(" //", litImpl, "myImpl{\\}", "myImpl{\\}") + myBasicImpl() //@item(litBasicImpl, "myBasicImpl()", "string", "var") + + mi = m //@snippet(" //", litBasicImpl, "myBasicImpl($0)", "myBasicImpl($0)") + // only satisfied by pointer to myImpl type myPtrIntf interface { bar() diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index 766ddd33e9..cc134f43bd 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -30,7 +30,7 @@ import ( // are being executed. If a test is added, this number must be changed. const ( ExpectedCompletionsCount = 152 - ExpectedCompletionSnippetCount = 35 + ExpectedCompletionSnippetCount = 36 ExpectedUnimportedCompletionsCount = 1 ExpectedDeepCompletionsCount = 5 ExpectedFuzzyCompletionsCount = 6