lsp/completion: support completing to Elem() types
For array, slice, maps and chan candidates, we now support
transforming them to their element type in completions. For example:
var m map[string]int
var _ int = m<>
At <> we complete to "m[]" because we see that the map value type
matches our expected type.
Fixes golang/go#46045.
Change-Id: Ibee088550193a53744f93217cc365f67f301ae90
Reviewed-on: https://go-review.googlesource.com/c/tools/+/323451
Run-TryBot: Muir Manders <muir@mnd.rs>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/source/completion/builtin.go b/internal/lsp/source/completion/builtin.go
index 086e3f7..39732d8 100644
--- a/internal/lsp/source/completion/builtin.go
+++ b/internal/lsp/source/completion/builtin.go
@@ -73,6 +73,9 @@
// Infer first append() arg type as apparent return type of
// append().
inf.objType = parentInf.objType
+ if parentInf.variadic {
+ inf.objType = types.NewSlice(inf.objType)
+ }
break
}
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index 14a2052..741e6b3 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -1777,6 +1777,7 @@
invoke // make a function call: "()" in "foo()"
takeSlice // take slice of array: "[:]" in "foo[:]"
takeDotDotDot // turn slice into variadic args: "..." in "foo..."
+ index // index into slice/array: "[0]" in "foo[0]"
)
type objKind int
@@ -2404,6 +2405,9 @@
return true
}
case *types.Array:
+ if f(t.Elem(), true, index) {
+ return true
+ }
// Try converting array to slice.
if f(types.NewSlice(t.Elem()), false, takeSlice) {
return true
@@ -2412,6 +2416,18 @@
if f(t.Elem(), false, dereference) {
return true
}
+ case *types.Slice:
+ if f(t.Elem(), true, index) {
+ return true
+ }
+ case *types.Map:
+ if f(t.Elem(), false, index) {
+ return true
+ }
+ case *types.Chan:
+ if f(t.Elem(), false, chanRead) {
+ return true
+ }
}
// Check if c is addressable and a pointer to c matches our type inference.
diff --git a/internal/lsp/source/completion/deep_completion.go b/internal/lsp/source/completion/deep_completion.go
index e7c4854..45a02ff 100644
--- a/internal/lsp/source/completion/deep_completion.go
+++ b/internal/lsp/source/completion/deep_completion.go
@@ -260,6 +260,12 @@
cand.score *= 1.1
}
+ // Slight penalty for index modifier (e.g. changing "foo" to
+ // "foo[]") to curb false positives.
+ if cand.hasMod(index) {
+ cand.score *= 0.9
+ }
+
// Favor shallow matches by lowering score according to depth.
cand.score -= cand.score * c.deepState.scorePenalty(cand)
diff --git a/internal/lsp/source/completion/format.go b/internal/lsp/source/completion/format.go
index 08b933f..5a20633 100644
--- a/internal/lsp/source/completion/format.go
+++ b/internal/lsp/source/completion/format.go
@@ -132,6 +132,10 @@
suffix += "[:]"
case takeDotDotDot:
suffix += "..."
+ case index:
+ snip.WriteText("[")
+ snip.WritePlaceholder(nil)
+ snip.WriteText("]")
}
}
diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden
index 1c1b780..ce4d7e4 100644
--- a/internal/lsp/testdata/summary.txt.golden
+++ b/internal/lsp/testdata/summary.txt.golden
@@ -2,7 +2,7 @@
CallHierarchyCount = 2
CodeLensCount = 5
CompletionsCount = 265
-CompletionSnippetCount = 102
+CompletionSnippetCount = 103
UnimportedCompletionsCount = 5
DeepCompletionsCount = 5
FuzzyCompletionsCount = 8
diff --git a/internal/lsp/testdata/typemods/type_mods.go b/internal/lsp/testdata/typemods/type_mods.go
index 479b481..f5f0f80 100644
--- a/internal/lsp/testdata/typemods/type_mods.go
+++ b/internal/lsp/testdata/typemods/type_mods.go
@@ -13,3 +13,9 @@
func _() {
var _ int = foo //@snippet(" //", modFooFunc, "fooFunc()()", "fooFunc()()"),snippet(" //", modFooPtr, "*fooPtr()", "*fooPtr()")
}
+
+func _() {
+ var m map[int][]chan int //@item(modMapChanPtr, "m", "map[int]chan *int", "var")
+
+ var _ int = m //@snippet(" //", modMapChanPtr, "<-m[${1:}][${2:}]", "<-m[${1:}][${2:}]")
+}