From 7414d4c1f71cec71978b1aec0539171a2e42d230 Mon Sep 17 00:00:00 2001 From: Andzej Maciusovic Date: Fri, 1 Feb 2019 06:40:03 +0000 Subject: [PATCH] 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 Reviewed-by: Rebecca Stambler --- internal/lsp/lsp_test.go | 2 +- internal/lsp/source/completion.go | 34 ++++++++++++++++++- .../lsp/testdata/func_rank/func_rank.go.in | 12 +++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 internal/lsp/testdata/func_rank/func_rank.go.in diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 4a7ecc54..c1078d3f 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -35,7 +35,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) { // 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 21cb4b54..a748a175 100644 --- a/internal/lsp/source/completion.go +++ b/internal/lsp/source/completion.go @@ -86,20 +86,24 @@ func Completion(ctx context.Context, f File, pos token.Pos) (items []CompletionI 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 @@ func selector(sel *ast.SelectorExpr, pos token.Pos, info *types.Info, found find 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