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>
This commit is contained in:
Andzej Maciusovic 2019-02-01 06:40:03 +00:00 committed by Rebecca Stambler
parent 718ddee956
commit 7414d4c1f7
3 changed files with 46 additions and 2 deletions

View File

@ -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 // 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. // are being executed. If a test is added, this number must be changed.
const expectedCompletionsCount = 60 const expectedCompletionsCount = 63
const expectedDiagnosticsCount = 13 const expectedDiagnosticsCount = 13
const expectedFormatCount = 3 const expectedFormatCount = 3
const expectedDefinitionsCount = 16 const expectedDefinitionsCount = 16

View File

@ -86,20 +86,24 @@ func Completion(ctx context.Context, f File, pos token.Pos) (items []CompletionI
typ := expectedType(path, pos, pkg.TypesInfo) typ := expectedType(path, pos, pkg.TypesInfo)
sig := enclosingFunction(path, pos, pkg.TypesInfo) sig := enclosingFunction(path, pos, pkg.TypesInfo)
pkgStringer := qualifier(file, pkg.Types, pkg.TypesInfo) pkgStringer := qualifier(file, pkg.Types, pkg.TypesInfo)
preferTypeNames := wantTypeNames(pos, path)
seen := make(map[types.Object]bool) seen := make(map[types.Object]bool)
// found adds a candidate completion. // found adds a candidate completion.
// Only the first candidate of a given name is considered. // Only the first candidate of a given name is considered.
found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem { found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem {
if obj.Pkg() != nil && obj.Pkg() != pkg.Types && !obj.Exported() { if obj.Pkg() != nil && obj.Pkg() != pkg.Types && !obj.Exported() {
return items // inaccessible return items // inaccessible
} }
if !seen[obj] { if !seen[obj] {
seen[obj] = true seen[obj] = true
if typ != nil && matchingTypes(typ, obj.Type()) { if typ != nil && matchingTypes(typ, obj.Type()) {
weight *= 10.0 weight *= 10.0
} }
if _, ok := obj.(*types.TypeName); !ok && preferTypeNames {
weight *= 0.01
}
item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool { item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
return isParameter(sig, v) return isParameter(sig, v)
}) })
@ -202,6 +206,34 @@ func selector(sel *ast.SelectorExpr, pos token.Pos, info *types.Info, found find
return items, nil 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. // 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) { 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]. var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].

View File

@ -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)