зеркало из https://github.com/golang/tools.git
130 строки
3.2 KiB
Go
130 строки
3.2 KiB
Go
// Copyright 2024 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package imports
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
"golang.org/x/tools/internal/gopathwalk"
|
|
)
|
|
|
|
// ProcessEnvSource implements the [Source] interface using the legacy
|
|
// [ProcessEnv] abstraction.
|
|
type ProcessEnvSource struct {
|
|
env *ProcessEnv
|
|
srcDir string
|
|
filename string
|
|
pkgName string
|
|
}
|
|
|
|
// NewProcessEnvSource returns a [ProcessEnvSource] wrapping the given
|
|
// env, to be used for fixing imports in the file with name filename in package
|
|
// named pkgName.
|
|
func NewProcessEnvSource(env *ProcessEnv, filename, pkgName string) (*ProcessEnvSource, error) {
|
|
abs, err := filepath.Abs(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
srcDir := filepath.Dir(abs)
|
|
return &ProcessEnvSource{
|
|
env: env,
|
|
srcDir: srcDir,
|
|
filename: filename,
|
|
pkgName: pkgName,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ProcessEnvSource) LoadPackageNames(ctx context.Context, srcDir string, unknown []string) (map[string]string, error) {
|
|
r, err := s.env.GetResolver()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.loadPackageNames(unknown, srcDir)
|
|
}
|
|
|
|
func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename string, refs map[string]map[string]bool) ([]*Result, error) {
|
|
var mu sync.Mutex
|
|
found := make(map[string][]pkgDistance)
|
|
callback := &scanCallback{
|
|
rootFound: func(gopathwalk.Root) bool {
|
|
return true // We want everything.
|
|
},
|
|
dirFound: func(pkg *pkg) bool {
|
|
return pkgIsCandidate(filename, refs, pkg)
|
|
},
|
|
packageNameLoaded: func(pkg *pkg) bool {
|
|
if _, want := refs[pkg.packageName]; !want {
|
|
return false
|
|
}
|
|
if pkg.dir == s.srcDir && s.pkgName == pkg.packageName {
|
|
// The candidate is in the same directory and has the
|
|
// same package name. Don't try to import ourselves.
|
|
return false
|
|
}
|
|
if !canUse(filename, pkg.dir) {
|
|
return false
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(s.srcDir, pkg.dir)})
|
|
return false // We'll do our own loading after we sort.
|
|
},
|
|
}
|
|
resolver, err := s.env.GetResolver()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := resolver.scan(ctx, callback); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
searcher := symbolSearcher{
|
|
logf: s.env.logf,
|
|
srcDir: s.srcDir,
|
|
xtest: strings.HasSuffix(s.pkgName, "_test"),
|
|
loadExports: resolver.loadExports,
|
|
}
|
|
|
|
var resultMu sync.Mutex
|
|
results := make(map[string]*Result, len(refs))
|
|
for pkgName, symbols := range refs {
|
|
g.Go(func() error {
|
|
found, err := searcher.search(ctx, found[pkgName], pkgName, symbols)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found == nil {
|
|
return nil // No matching package.
|
|
}
|
|
|
|
imp := &ImportInfo{
|
|
ImportPath: found.importPathShort,
|
|
}
|
|
pkg := &PackageInfo{
|
|
Name: pkgName,
|
|
Exports: symbols,
|
|
}
|
|
resultMu.Lock()
|
|
results[pkgName] = &Result{Import: imp, Package: pkg}
|
|
resultMu.Unlock()
|
|
return nil
|
|
})
|
|
}
|
|
if err := g.Wait(); err != nil {
|
|
return nil, err
|
|
}
|
|
var ans []*Result
|
|
for _, x := range results {
|
|
ans = append(ans, x)
|
|
}
|
|
return ans, nil
|
|
}
|