mirror of https://github.com/golang/go.git
internal/lsp/source: score in-memory unimported candidates
We were assuming that all in-memory packages were equally useful. That's not true for projects with a large dependency tree. Call into the imports code to score them. While I'm here, score the main module above direct deps. Updates golang/go#36591. Change-Id: I07c56dd3ff7338e76f3643e18d35abc1b52d6763 Reviewed-on: https://go-review.googlesource.com/c/tools/+/215023 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
84cebe1034
commit
6edc0a871e
|
|
@ -643,6 +643,14 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena
|
|||
return env.GetResolver().scan(ctx, scanFilter)
|
||||
}
|
||||
|
||||
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) map[string]int {
|
||||
result := make(map[string]int)
|
||||
for _, path := range paths {
|
||||
result[path] = env.GetResolver().scoreImportPath(ctx, path)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func PrimeCache(ctx context.Context, env *ProcessEnv) error {
|
||||
// Fully scan the disk for directories, but don't actually read any Go files.
|
||||
callback := &scanCallback{
|
||||
|
|
@ -874,6 +882,8 @@ type Resolver interface {
|
|||
// loadExports returns the set of exported symbols in the package at dir.
|
||||
// loadExports may be called concurrently.
|
||||
loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error)
|
||||
// scoreImportPath returns the relevance for an import path.
|
||||
scoreImportPath(ctx context.Context, path string) int
|
||||
|
||||
ClearForNewScan()
|
||||
}
|
||||
|
|
@ -1251,6 +1261,13 @@ func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *gopathResolver) scoreImportPath(ctx context.Context, path string) int {
|
||||
if _, ok := stdlib[path]; ok {
|
||||
return MaxRelevance
|
||||
}
|
||||
return MaxRelevance - 1
|
||||
}
|
||||
|
||||
func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []gopathwalk.Root {
|
||||
var result []gopathwalk.Root
|
||||
for _, root := range roots {
|
||||
|
|
|
|||
|
|
@ -2539,14 +2539,14 @@ func TestGetCandidates(t *testing.T) {
|
|||
|
||||
testConfig{
|
||||
modules: []packagestest.Module{
|
||||
{
|
||||
Name: "foo.com",
|
||||
Files: fm{"foo/foo.go": "package foo\n"},
|
||||
},
|
||||
{
|
||||
Name: "bar.com",
|
||||
Files: fm{"bar/bar.go": "package bar\n"},
|
||||
},
|
||||
{
|
||||
Name: "foo.com",
|
||||
Files: fm{"foo/foo.go": "package foo\n"},
|
||||
},
|
||||
},
|
||||
}.test(t, func(t *goimportTest) {
|
||||
var mu sync.Mutex
|
||||
|
|
|
|||
|
|
@ -480,6 +480,27 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) int {
|
||||
if _, ok := stdlib[path]; ok {
|
||||
return MaxRelevance
|
||||
}
|
||||
mod, _ := r.findPackage(path)
|
||||
return modRelevance(mod)
|
||||
}
|
||||
|
||||
func modRelevance(mod *ModuleJSON) int {
|
||||
switch {
|
||||
case mod == nil: // out of scope
|
||||
return MaxRelevance - 4
|
||||
case mod.Indirect:
|
||||
return MaxRelevance - 3
|
||||
case !mod.Main:
|
||||
return MaxRelevance - 2
|
||||
default:
|
||||
return MaxRelevance - 1 // main module ties with stdlib
|
||||
}
|
||||
}
|
||||
|
||||
// canonicalize gets the result of canonicalizing the packages using the results
|
||||
// of initializing the resolver from 'go list -m'.
|
||||
func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
|
||||
|
|
@ -494,14 +515,9 @@ func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
|
|||
}
|
||||
|
||||
importPath := info.nonCanonicalImportPath
|
||||
relevance := MaxRelevance - 3
|
||||
mod := r.findModuleByDir(info.dir)
|
||||
// Check if the directory is underneath a module that's in scope.
|
||||
if mod := r.findModuleByDir(info.dir); mod != nil {
|
||||
if mod.Indirect {
|
||||
relevance = MaxRelevance - 2
|
||||
} else {
|
||||
relevance = MaxRelevance - 1
|
||||
}
|
||||
if mod != nil {
|
||||
// It is. If dir is the target of a replace directive,
|
||||
// our guessed import path is wrong. Use the real one.
|
||||
if mod.Dir == info.dir {
|
||||
|
|
@ -519,7 +535,7 @@ func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
|
|||
res := &pkg{
|
||||
importPathShort: importPath,
|
||||
dir: info.dir,
|
||||
relevance: relevance,
|
||||
relevance: modRelevance(mod),
|
||||
}
|
||||
// We may have discovered a package that has a different version
|
||||
// in scope already. Canonicalize to that one if possible.
|
||||
|
|
|
|||
|
|
@ -880,13 +880,14 @@ import _ "rsc.io/quote"
|
|||
// Stdlib
|
||||
{7, "bytes", "bytes"},
|
||||
{7, "http", "net/http"},
|
||||
// Direct module deps
|
||||
{6, "quote", "rsc.io/quote"},
|
||||
// Main module
|
||||
{6, "rpackage", "example.com/rpackage"},
|
||||
// Direct module deps
|
||||
{5, "quote", "rsc.io/quote"},
|
||||
// Indirect deps
|
||||
{5, "language", "golang.org/x/text/language"},
|
||||
{4, "language", "golang.org/x/text/language"},
|
||||
// Out of scope modules
|
||||
{4, "quote", "rsc.io/quote/v2"},
|
||||
{3, "quote", "rsc.io/quote/v2"},
|
||||
}
|
||||
var mu sync.Mutex
|
||||
var got []res
|
||||
|
|
|
|||
|
|
@ -656,12 +656,24 @@ func (c *completer) unimportedMembers(id *ast.Ident) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var paths []string
|
||||
for path, pkg := range known {
|
||||
if pkg.GetTypes().Name() != id.Name {
|
||||
continue
|
||||
}
|
||||
paths = append(paths, path)
|
||||
}
|
||||
var relevances map[string]int
|
||||
if len(paths) != 0 {
|
||||
c.snapshot.View().RunProcessEnvFunc(c.ctx, func(opts *imports.Options) error {
|
||||
relevances = imports.ScoreImportPaths(c.ctx, opts.Env, paths)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
for path, pkg := range known {
|
||||
if pkg.GetTypes().Name() != id.Name {
|
||||
continue
|
||||
}
|
||||
// We don't know what this is, so assign it the highest score.
|
||||
score := 0.01 * imports.MaxRelevance
|
||||
imp := &importInfo{
|
||||
importPath: path,
|
||||
pkg: pkg,
|
||||
|
|
@ -669,7 +681,7 @@ func (c *completer) unimportedMembers(id *ast.Ident) error {
|
|||
if imports.ImportPathToAssumedName(path) != pkg.GetTypes().Name() {
|
||||
imp.name = pkg.GetTypes().Name()
|
||||
}
|
||||
c.packageMembers(pkg.GetTypes(), score, imp)
|
||||
c.packageMembers(pkg.GetTypes(), .01*float64(relevances[path]), imp)
|
||||
if len(c.items) >= unimportedTarget {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue