internal/lsp: rank types higher when completing in return of a function
Completion now ranks type names higher for func receiver, type params and type results (e.g. func (<>) foo(<>) (<>) {}).
Fixes golang/go#29152
Change-Id: Icdd18b1b344c1cd617a4f45a7b071e53c1345478
GitHub-Last-Rev: e6acb1f2d2a7e571ffcecc500e407fdefd118fed
GitHub-Pull-Request: golang/tools#73
Reviewed-on: https://go-review.googlesource.com/c/159797
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 4a7ecc5..c1078d3 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -35,7 +35,7 @@
// We hardcode the expected number of test cases to ensure that all tests
// are being executed. If a test is added, this number must be changed.
- const expectedCompletionsCount = 60
+ const expectedCompletionsCount = 63
const expectedDiagnosticsCount = 13
const expectedFormatCount = 3
const expectedDefinitionsCount = 16
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 21cb4b5..a748a17 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -86,20 +86,24 @@
typ := expectedType(path, pos, pkg.TypesInfo)
sig := enclosingFunction(path, pos, pkg.TypesInfo)
pkgStringer := qualifier(file, pkg.Types, pkg.TypesInfo)
+ preferTypeNames := wantTypeNames(pos, path)
seen := make(map[types.Object]bool)
-
// found adds a candidate completion.
// Only the first candidate of a given name is considered.
found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem {
if obj.Pkg() != nil && obj.Pkg() != pkg.Types && !obj.Exported() {
return items // inaccessible
}
+
if !seen[obj] {
seen[obj] = true
if typ != nil && matchingTypes(typ, obj.Type()) {
weight *= 10.0
}
+ if _, ok := obj.(*types.TypeName); !ok && preferTypeNames {
+ weight *= 0.01
+ }
item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
return isParameter(sig, v)
})
@@ -202,6 +206,34 @@
return items, nil
}
+// wantTypeNames checks if given token position is inside func receiver, type params
+// or type results (e.g func (<>) foo(<>) (<>) {} ).
+func wantTypeNames(pos token.Pos, path []ast.Node) bool {
+ for _, p := range path {
+ switch n := p.(type) {
+ case *ast.FuncDecl:
+ recv := n.Recv
+ if recv != nil && recv.Pos() <= pos && pos <= recv.End() {
+ return true
+ }
+
+ if n.Type != nil {
+ params := n.Type.Params
+ if params != nil && params.Pos() <= pos && pos <= params.End() {
+ return true
+ }
+
+ results := n.Type.Results
+ if results != nil && results.Pos() <= pos && pos <= results.End() {
+ return true
+ }
+ }
+ return false
+ }
+ }
+ return false
+}
+
// lexical finds completions in the lexical environment.
func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem) {
var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
diff --git a/internal/lsp/testdata/func_rank/func_rank.go.in b/internal/lsp/testdata/func_rank/func_rank.go.in
new file mode 100644
index 0000000..cb5a1b4
--- /dev/null
+++ b/internal/lsp/testdata/func_rank/func_rank.go.in
@@ -0,0 +1,12 @@
+package func_rank
+
+var stringAVar = "var" //@item(stringAVar, "stringAVar", "string", "var")
+func stringBFunc() string { return "str" } //@item(stringBFunc, "stringBFunc()", "string", "func")
+type stringer struct{} //@item(stringer, "stringer", "struct{...}", "struct")
+
+func _() stringer //@complete("tr", stringer, stringAVar, stringBFunc)
+
+func _(val stringer) {} //@complete("tr", stringer, stringAVar, stringBFunc)
+
+func (stringer) _() {} //@complete("tr", stringer, stringAVar, stringBFunc)
+