mirror of
https://github.com/golang/go
synced 2024-11-18 18:44:42 -07:00
internal/lsp: cache the imports ProcessEnv across imports calls
The imports ProcessEnv contains cached module and filesystem state. This change allows gopls to use the same ProcessEnv and resolver across multiple calls to the internal/imports library. A ProcessEnv belongs to a view, because the cached module state depends on the module that is open in the workspace. Since we do not yet track whether the 'go.mod' file has changed, we conservatively reset the cached state in the module resolver before every call to imports.Process. Change-Id: I27c8e212cb0b477ff425d5ed98a544b27b7d92ee Reviewed-on: https://go-review.googlesource.com/c/tools/+/184921 Run-TryBot: Suzy Mueller <suzmue@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
b0712d6011
commit
fdb8f0bb4e
@ -272,7 +272,7 @@ func (p *pass) loadPackageNames(imports []*importInfo) error {
|
|||||||
unknown = append(unknown, imp.importPath)
|
unknown = append(unknown, imp.importPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
names, err := p.env.getResolver().loadPackageNames(unknown, p.srcDir)
|
names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -595,7 +595,7 @@ type ProcessEnv struct {
|
|||||||
// Logf is the default logger for the ProcessEnv.
|
// Logf is the default logger for the ProcessEnv.
|
||||||
Logf func(format string, args ...interface{})
|
Logf func(format string, args ...interface{})
|
||||||
|
|
||||||
resolver resolver
|
resolver Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) env() []string {
|
func (e *ProcessEnv) env() []string {
|
||||||
@ -617,7 +617,7 @@ func (e *ProcessEnv) env() []string {
|
|||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) getResolver() resolver {
|
func (e *ProcessEnv) GetResolver() Resolver {
|
||||||
if e.resolver != nil {
|
if e.resolver != nil {
|
||||||
return e.resolver
|
return e.resolver
|
||||||
}
|
}
|
||||||
@ -631,7 +631,7 @@ func (e *ProcessEnv) getResolver() resolver {
|
|||||||
e.resolver = &gopathResolver{env: e}
|
e.resolver = &gopathResolver{env: e}
|
||||||
return e.resolver
|
return e.resolver
|
||||||
}
|
}
|
||||||
e.resolver = &moduleResolver{env: e}
|
e.resolver = &ModuleResolver{env: e}
|
||||||
return e.resolver
|
return e.resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,15 +700,15 @@ func addStdlibCandidates(pass *pass, refs references) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A resolver does the build-system-specific parts of goimports.
|
// A Resolver does the build-system-specific parts of goimports.
|
||||||
type resolver interface {
|
type Resolver interface {
|
||||||
// loadPackageNames loads the package names in importPaths.
|
// loadPackageNames loads the package names in importPaths.
|
||||||
loadPackageNames(importPaths []string, srcDir string) (map[string]string, error)
|
loadPackageNames(importPaths []string, srcDir string) (map[string]string, error)
|
||||||
// scan finds (at least) the packages satisfying refs. The returned slice is unordered.
|
// scan finds (at least) the packages satisfying refs. The returned slice is unordered.
|
||||||
scan(refs references) ([]*pkg, error)
|
scan(refs references) ([]*pkg, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gopathResolver implements resolver for GOPATH and module workspaces using go/packages.
|
// gopackagesResolver implements resolver for GOPATH and module workspaces using go/packages.
|
||||||
type goPackagesResolver struct {
|
type goPackagesResolver struct {
|
||||||
env *ProcessEnv
|
env *ProcessEnv
|
||||||
}
|
}
|
||||||
@ -758,7 +758,7 @@ func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addExternalCandidates(pass *pass, refs references, filename string) error {
|
func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||||
dirScan, err := pass.env.getResolver().scan(refs)
|
dirScan, err := pass.env.GetResolver().scan(refs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1855,7 +1855,7 @@ func TestImportPathToNameGoPathParse(t *testing.T) {
|
|||||||
if strings.Contains(t.Name(), "GoPackages") {
|
if strings.Contains(t.Name(), "GoPackages") {
|
||||||
t.Skip("go/packages does not ignore package main")
|
t.Skip("go/packages does not ignore package main")
|
||||||
}
|
}
|
||||||
r := t.env.getResolver()
|
r := t.env.GetResolver()
|
||||||
srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go"))
|
srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go"))
|
||||||
names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir)
|
names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,37 +18,37 @@ import (
|
|||||||
"golang.org/x/tools/internal/module"
|
"golang.org/x/tools/internal/module"
|
||||||
)
|
)
|
||||||
|
|
||||||
// moduleResolver implements resolver for modules using the go command as little
|
// ModuleResolver implements resolver for modules using the go command as little
|
||||||
// as feasible.
|
// as feasible.
|
||||||
type moduleResolver struct {
|
type ModuleResolver struct {
|
||||||
env *ProcessEnv
|
env *ProcessEnv
|
||||||
|
|
||||||
initialized bool
|
Initialized bool
|
||||||
main *moduleJSON
|
Main *ModuleJSON
|
||||||
modsByModPath []*moduleJSON // All modules, ordered by # of path components in module Path...
|
ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
|
||||||
modsByDir []*moduleJSON // ...or Dir.
|
ModsByDir []*ModuleJSON // ...or Dir.
|
||||||
}
|
}
|
||||||
|
|
||||||
type moduleJSON struct {
|
type ModuleJSON struct {
|
||||||
Path string // module path
|
Path string // module path
|
||||||
Version string // module version
|
Version string // module version
|
||||||
Versions []string // available module versions (with -versions)
|
Versions []string // available module versions (with -versions)
|
||||||
Replace *moduleJSON // replaced by this module
|
Replace *ModuleJSON // replaced by this module
|
||||||
Time *time.Time // time version was created
|
Time *time.Time // time version was created
|
||||||
Update *moduleJSON // available update, if any (with -u)
|
Update *ModuleJSON // available update, if any (with -u)
|
||||||
Main bool // is this the main module?
|
Main bool // is this the main module?
|
||||||
Indirect bool // is this module only an indirect dependency of main module?
|
Indirect bool // is this module only an indirect dependency of main module?
|
||||||
Dir string // directory holding files for this module, if any
|
Dir string // directory holding files for this module, if any
|
||||||
GoMod string // path to go.mod file for this module, if any
|
GoMod string // path to go.mod file for this module, if any
|
||||||
Error *moduleErrorJSON // error loading module
|
Error *ModuleErrorJSON // error loading module
|
||||||
}
|
}
|
||||||
|
|
||||||
type moduleErrorJSON struct {
|
type ModuleErrorJSON struct {
|
||||||
Err string // the error itself
|
Err string // the error itself
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *moduleResolver) init() error {
|
func (r *ModuleResolver) init() error {
|
||||||
if r.initialized {
|
if r.Initialized {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stdout, err := r.env.invokeGo("list", "-m", "-json", "...")
|
stdout, err := r.env.invokeGo("list", "-m", "-json", "...")
|
||||||
@ -56,7 +56,7 @@ func (r *moduleResolver) init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for dec := json.NewDecoder(stdout); dec.More(); {
|
for dec := json.NewDecoder(stdout); dec.More(); {
|
||||||
mod := &moduleJSON{}
|
mod := &ModuleJSON{}
|
||||||
if err := dec.Decode(mod); err != nil {
|
if err := dec.Decode(mod); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -67,34 +67,34 @@ func (r *moduleResolver) init() error {
|
|||||||
// Can't do anything with a module that's not downloaded.
|
// Can't do anything with a module that's not downloaded.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.modsByModPath = append(r.modsByModPath, mod)
|
r.ModsByModPath = append(r.ModsByModPath, mod)
|
||||||
r.modsByDir = append(r.modsByDir, mod)
|
r.ModsByDir = append(r.ModsByDir, mod)
|
||||||
if mod.Main {
|
if mod.Main {
|
||||||
r.main = mod
|
r.Main = mod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(r.modsByModPath, func(i, j int) bool {
|
sort.Slice(r.ModsByModPath, func(i, j int) bool {
|
||||||
count := func(x int) int {
|
count := func(x int) int {
|
||||||
return strings.Count(r.modsByModPath[x].Path, "/")
|
return strings.Count(r.ModsByModPath[x].Path, "/")
|
||||||
}
|
}
|
||||||
return count(j) < count(i) // descending order
|
return count(j) < count(i) // descending order
|
||||||
})
|
})
|
||||||
sort.Slice(r.modsByDir, func(i, j int) bool {
|
sort.Slice(r.ModsByDir, func(i, j int) bool {
|
||||||
count := func(x int) int {
|
count := func(x int) int {
|
||||||
return strings.Count(r.modsByDir[x].Dir, "/")
|
return strings.Count(r.ModsByDir[x].Dir, "/")
|
||||||
}
|
}
|
||||||
return count(j) < count(i) // descending order
|
return count(j) < count(i) // descending order
|
||||||
})
|
})
|
||||||
|
|
||||||
r.initialized = true
|
r.Initialized = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findPackage returns the module and directory that contains the package at
|
// findPackage returns the module and directory that contains the package at
|
||||||
// the given import path, or returns nil, "" if no module is in scope.
|
// the given import path, or returns nil, "" if no module is in scope.
|
||||||
func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) {
|
func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
|
||||||
for _, m := range r.modsByModPath {
|
for _, m := range r.ModsByModPath {
|
||||||
if !strings.HasPrefix(importPath, m.Path) {
|
if !strings.HasPrefix(importPath, m.Path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func (r *moduleResolver) findPackage(importPath string) (*moduleJSON, string) {
|
|||||||
|
|
||||||
// findModuleByDir returns the module that contains dir, or nil if no such
|
// findModuleByDir returns the module that contains dir, or nil if no such
|
||||||
// module is in scope.
|
// module is in scope.
|
||||||
func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON {
|
func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
|
||||||
// This is quite tricky and may not be correct. dir could be:
|
// This is quite tricky and may not be correct. dir could be:
|
||||||
// - a package in the main module.
|
// - a package in the main module.
|
||||||
// - a replace target underneath the main module's directory.
|
// - a replace target underneath the main module's directory.
|
||||||
@ -134,7 +134,7 @@ func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON {
|
|||||||
// - in /vendor/ in -mod=vendor mode.
|
// - in /vendor/ in -mod=vendor mode.
|
||||||
// - nested module? Dunno.
|
// - nested module? Dunno.
|
||||||
// Rumor has it that replace targets cannot contain other replace targets.
|
// Rumor has it that replace targets cannot contain other replace targets.
|
||||||
for _, m := range r.modsByDir {
|
for _, m := range r.ModsByDir {
|
||||||
if !strings.HasPrefix(dir, m.Dir) {
|
if !strings.HasPrefix(dir, m.Dir) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ func (r *moduleResolver) findModuleByDir(dir string) *moduleJSON {
|
|||||||
|
|
||||||
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
||||||
// mod, not actually in mod.
|
// mod, not actually in mod.
|
||||||
func dirIsNestedModule(dir string, mod *moduleJSON) bool {
|
func dirIsNestedModule(dir string, mod *ModuleJSON) bool {
|
||||||
if !strings.HasPrefix(dir, mod.Dir) {
|
if !strings.HasPrefix(dir, mod.Dir) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func findModFile(dir string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||||
if err := r.init(); err != nil {
|
if err := r.init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -195,7 +195,7 @@ func (r *moduleResolver) loadPackageNames(importPaths []string, srcDir string) (
|
|||||||
return names, nil
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
|
func (r *ModuleResolver) scan(_ references) ([]*pkg, error) {
|
||||||
if err := r.init(); err != nil {
|
if err := r.init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -204,15 +204,15 @@ func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
|
|||||||
roots := []gopathwalk.Root{
|
roots := []gopathwalk.Root{
|
||||||
{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
|
{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
|
||||||
}
|
}
|
||||||
if r.main != nil {
|
if r.Main != nil {
|
||||||
roots = append(roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
|
roots = append(roots, gopathwalk.Root{r.Main.Dir, gopathwalk.RootCurrentModule})
|
||||||
}
|
}
|
||||||
for _, p := range filepath.SplitList(r.env.GOPATH) {
|
for _, p := range filepath.SplitList(r.env.GOPATH) {
|
||||||
roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
|
roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk replace targets, just in case they're not in any of the above.
|
// Walk replace targets, just in case they're not in any of the above.
|
||||||
for _, mod := range r.modsByModPath {
|
for _, mod := range r.ModsByModPath {
|
||||||
if mod.Replace != nil {
|
if mod.Replace != nil {
|
||||||
roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
|
roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
|
|||||||
}
|
}
|
||||||
switch root.Type {
|
switch root.Type {
|
||||||
case gopathwalk.RootCurrentModule:
|
case gopathwalk.RootCurrentModule:
|
||||||
importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
|
importPath = path.Join(r.Main.Path, filepath.ToSlash(subdir))
|
||||||
case gopathwalk.RootModuleCache:
|
case gopathwalk.RootModuleCache:
|
||||||
matches := modCacheRegexp.FindStringSubmatch(subdir)
|
matches := modCacheRegexp.FindStringSubmatch(subdir)
|
||||||
modPath, err := module.DecodePath(filepath.ToSlash(matches[1]))
|
modPath, err := module.DecodePath(filepath.ToSlash(matches[1]))
|
||||||
|
@ -140,7 +140,7 @@ import _ "rsc.io/quote"
|
|||||||
|
|
||||||
mt.env.GOFLAGS = ""
|
mt.env.GOFLAGS = ""
|
||||||
// Clear out the resolver's cache, since we've changed the environment.
|
// Clear out the resolver's cache, since we've changed the environment.
|
||||||
mt.resolver = &moduleResolver{env: mt.env}
|
mt.resolver = &ModuleResolver{env: mt.env}
|
||||||
mt.assertModuleFoundInDir("rsc.io/quote", "quote", `pkg.*mod.*/quote@.*$`)
|
mt.assertModuleFoundInDir("rsc.io/quote", "quote", `pkg.*mod.*/quote@.*$`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +486,7 @@ var proxyDir string
|
|||||||
type modTest struct {
|
type modTest struct {
|
||||||
*testing.T
|
*testing.T
|
||||||
env *ProcessEnv
|
env *ProcessEnv
|
||||||
resolver *moduleResolver
|
resolver *ModuleResolver
|
||||||
cleanup func()
|
cleanup func()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,7 +538,7 @@ func setup(t *testing.T, main, wd string) *modTest {
|
|||||||
return &modTest{
|
return &modTest{
|
||||||
T: t,
|
T: t,
|
||||||
env: env,
|
env: env,
|
||||||
resolver: &moduleResolver{env: env},
|
resolver: &ModuleResolver{env: env},
|
||||||
cleanup: func() {
|
cleanup: func() {
|
||||||
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
48
internal/lsp/cache/view.go
vendored
48
internal/lsp/cache/view.go
vendored
@ -12,9 +12,11 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
|
"golang.org/x/tools/internal/imports"
|
||||||
"golang.org/x/tools/internal/lsp/debug"
|
"golang.org/x/tools/internal/lsp/debug"
|
||||||
"golang.org/x/tools/internal/lsp/source"
|
"golang.org/x/tools/internal/lsp/source"
|
||||||
"golang.org/x/tools/internal/lsp/xlog"
|
"golang.org/x/tools/internal/lsp/xlog"
|
||||||
@ -49,6 +51,10 @@ type view struct {
|
|||||||
// env is the environment to use when invoking underlying tools.
|
// env is the environment to use when invoking underlying tools.
|
||||||
env []string
|
env []string
|
||||||
|
|
||||||
|
// process is the process env for this view.
|
||||||
|
// Note: this contains cached module and filesystem state.
|
||||||
|
processEnv *imports.ProcessEnv
|
||||||
|
|
||||||
// buildFlags is the build flags to use when invoking underlying tools.
|
// buildFlags is the build flags to use when invoking underlying tools.
|
||||||
buildFlags []string
|
buildFlags []string
|
||||||
|
|
||||||
@ -133,6 +139,47 @@ func (v *view) Config() *packages.Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *view) ProcessEnv() *imports.ProcessEnv {
|
||||||
|
v.mu.Lock()
|
||||||
|
defer v.mu.Unlock()
|
||||||
|
|
||||||
|
if v.processEnv == nil {
|
||||||
|
v.processEnv = v.buildProcessEnv()
|
||||||
|
}
|
||||||
|
return v.processEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *view) buildProcessEnv() *imports.ProcessEnv {
|
||||||
|
cfg := v.Config()
|
||||||
|
env := &imports.ProcessEnv{
|
||||||
|
WorkingDir: cfg.Dir,
|
||||||
|
Logf: func(format string, u ...interface{}) {
|
||||||
|
xlog.Infof(v.backgroundCtx, format, u...)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, kv := range cfg.Env {
|
||||||
|
split := strings.Split(kv, "=")
|
||||||
|
if len(split) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch split[0] {
|
||||||
|
case "GOPATH":
|
||||||
|
env.GOPATH = split[1]
|
||||||
|
case "GOROOT":
|
||||||
|
env.GOROOT = split[1]
|
||||||
|
case "GO111MODULE":
|
||||||
|
env.GO111MODULE = split[1]
|
||||||
|
case "GOPROXY":
|
||||||
|
env.GOROOT = split[1]
|
||||||
|
case "GOFLAGS":
|
||||||
|
env.GOFLAGS = split[1]
|
||||||
|
case "GOSUMDB":
|
||||||
|
env.GOSUMDB = split[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
func (v *view) Env() []string {
|
func (v *view) Env() []string {
|
||||||
v.mu.Lock()
|
v.mu.Lock()
|
||||||
defer v.mu.Unlock()
|
defer v.mu.Unlock()
|
||||||
@ -144,6 +191,7 @@ func (v *view) SetEnv(env []string) {
|
|||||||
defer v.mu.Unlock()
|
defer v.mu.Unlock()
|
||||||
//TODO: this should invalidate the entire view
|
//TODO: this should invalidate the entire view
|
||||||
v.env = env
|
v.env = env
|
||||||
|
v.processEnv = nil // recompute process env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *view) SetBuildFlags(buildFlags []string) {
|
func (v *view) SetBuildFlags(buildFlags []string) {
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
@ -68,8 +67,17 @@ func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]TextEd
|
|||||||
if hasListErrors(pkg.GetErrors()) {
|
if hasListErrors(pkg.GetErrors()) {
|
||||||
return nil, fmt.Errorf("%s has list errors, not running goimports", f.URI())
|
return nil, fmt.Errorf("%s has list errors, not running goimports", f.URI())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resolver, ok := view.ProcessEnv().GetResolver().(*imports.ModuleResolver); ok && resolver.Initialized {
|
||||||
|
// TODO(suzmue): only reset this state when necessary (eg when the go.mod files of this
|
||||||
|
// module or modules with replace directive changes).
|
||||||
|
resolver.Initialized = false
|
||||||
|
resolver.Main = nil
|
||||||
|
resolver.ModsByModPath = nil
|
||||||
|
resolver.ModsByDir = nil
|
||||||
|
}
|
||||||
options := &imports.Options{
|
options := &imports.Options{
|
||||||
Env: buildProcessEnv(ctx, view),
|
Env: view.ProcessEnv(),
|
||||||
// Defaults.
|
// Defaults.
|
||||||
AllErrors: true,
|
AllErrors: true,
|
||||||
Comments: true,
|
Comments: true,
|
||||||
@ -103,37 +111,6 @@ func hasListErrors(errors []packages.Error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildProcessEnv(ctx context.Context, view View) *imports.ProcessEnv {
|
|
||||||
cfg := view.Config()
|
|
||||||
env := &imports.ProcessEnv{
|
|
||||||
WorkingDir: cfg.Dir,
|
|
||||||
Logf: func(format string, v ...interface{}) {
|
|
||||||
xlog.Infof(ctx, format, v...)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, kv := range cfg.Env {
|
|
||||||
split := strings.Split(kv, "=")
|
|
||||||
if len(split) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch split[0] {
|
|
||||||
case "GOPATH":
|
|
||||||
env.GOPATH = split[1]
|
|
||||||
case "GOROOT":
|
|
||||||
env.GOROOT = split[1]
|
|
||||||
case "GO111MODULE":
|
|
||||||
env.GO111MODULE = split[1]
|
|
||||||
case "GOPROXY":
|
|
||||||
env.GOROOT = split[1]
|
|
||||||
case "GOFLAGS":
|
|
||||||
env.GOFLAGS = split[1]
|
|
||||||
case "GOSUMDB":
|
|
||||||
env.GOSUMDB = split[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return env
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeTextEdits(ctx context.Context, file File, formatted string) (edits []TextEdit) {
|
func computeTextEdits(ctx context.Context, file File, formatted string) (edits []TextEdit) {
|
||||||
ctx, done := trace.StartSpan(ctx, "source.computeTextEdits")
|
ctx, done := trace.StartSpan(ctx, "source.computeTextEdits")
|
||||||
defer done()
|
defer done()
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/tools/go/analysis"
|
"golang.org/x/tools/go/analysis"
|
||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
|
"golang.org/x/tools/internal/imports"
|
||||||
"golang.org/x/tools/internal/lsp/diff"
|
"golang.org/x/tools/internal/lsp/diff"
|
||||||
"golang.org/x/tools/internal/span"
|
"golang.org/x/tools/internal/span"
|
||||||
)
|
)
|
||||||
@ -208,6 +209,15 @@ type View interface {
|
|||||||
Ignore(span.URI) bool
|
Ignore(span.URI) bool
|
||||||
|
|
||||||
Config() *packages.Config
|
Config() *packages.Config
|
||||||
|
|
||||||
|
// Process returns the process for this view.
|
||||||
|
// Note: this contains cached module and filesystem state, which must
|
||||||
|
// be invalidated after a 'go.mod' change.
|
||||||
|
//
|
||||||
|
// TODO(suzmue): the state cached in the process env is specific to each view,
|
||||||
|
// however, there is state that can be shared between views that is not currently
|
||||||
|
// cached, like the module cache.
|
||||||
|
ProcessEnv() *imports.ProcessEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
// File represents a source file of any type.
|
// File represents a source file of any type.
|
||||||
|
Loading…
Reference in New Issue
Block a user