2018-11-05 12:48:08 -07:00
|
|
|
// Copyright 2018 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.
|
|
|
|
|
2019-09-19 14:53:53 -06:00
|
|
|
// Package cache implements the caching layer for gopls.
|
2018-12-05 15:00:36 -07:00
|
|
|
package cache
|
2018-09-27 16:15:45 -06:00
|
|
|
|
|
|
|
import (
|
2018-12-18 14:18:03 -07:00
|
|
|
"context"
|
2020-01-11 18:29:13 -07:00
|
|
|
"encoding/json"
|
2019-07-14 21:08:10 -06:00
|
|
|
"fmt"
|
2019-04-29 19:08:16 -06:00
|
|
|
"go/ast"
|
2020-01-23 22:40:20 -07:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2019-03-27 07:25:30 -06:00
|
|
|
"os"
|
2020-05-12 19:56:33 -06:00
|
|
|
"path"
|
2019-12-17 14:53:57 -07:00
|
|
|
"path/filepath"
|
2019-11-08 11:25:29 -07:00
|
|
|
"reflect"
|
2019-07-03 13:23:05 -06:00
|
|
|
"strings"
|
2018-09-27 16:15:45 -06:00
|
|
|
"sync"
|
internal/lsp/cache: only refresh imports cache every 30 seconds
Loading completion suggestions can be slow, especially in GOPATH mode
where basically anything can change at any time. As a compromise, cache
everything for 30 seconds. Specifically, after a completion operation
finishes, if the cache is more than 30 seconds old, refresh it
asynchronously. That keeps user-facing latency consistent, without
chewing up CPU when the editor isn't in use. It does mean that if you
walk away for an hour and come back, the first completion may be stale.
In module mode this is relatively benign. The only things the
longer caching affects are the main module and replace targets, and
relevant packages in those will generally be loaded by gopls, so they'll
have full, up-to-date type information regardless.
In GOPATH mode this may be more troublesome, since it affects
everything. In particular, go get -u of a package that isn't imported
yet won't be reflected until the cache period expires. I think that's a
rare enough case not to worry about.
Change-Id: Iaadfd0ff647cda2b1dcdead9254b5492b397e86e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/205163
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-11-04 12:05:41 -07:00
|
|
|
"time"
|
2018-09-27 16:15:45 -06:00
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2020-04-20 19:50:02 -06:00
|
|
|
"golang.org/x/tools/internal/event/keys"
|
2020-02-24 14:46:08 -07:00
|
|
|
"golang.org/x/tools/internal/gocommand"
|
2019-07-03 13:23:05 -06:00
|
|
|
"golang.org/x/tools/internal/imports"
|
2020-03-10 21:09:39 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
2018-12-05 15:00:36 -07:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2020-01-14 22:22:42 -07:00
|
|
|
"golang.org/x/tools/internal/memoize"
|
2019-02-19 19:11:15 -07:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-12-19 13:47:07 -07:00
|
|
|
"golang.org/x/tools/internal/xcontext"
|
2019-09-16 16:17:51 -06:00
|
|
|
errors "golang.org/x/xerrors"
|
2018-09-27 16:15:45 -06:00
|
|
|
)
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
type View struct {
|
2020-07-16 15:37:12 -06:00
|
|
|
memoize.Arg // allow as a memoize.Function arg
|
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
session *Session
|
2019-05-29 12:55:52 -06:00
|
|
|
id string
|
2019-05-15 10:24:49 -06:00
|
|
|
|
2020-04-02 10:14:48 -06:00
|
|
|
optionsMu sync.Mutex
|
|
|
|
options source.Options
|
2019-09-05 22:17:36 -06:00
|
|
|
|
2020-01-27 14:59:43 -07:00
|
|
|
// mu protects most mutable state of the view.
|
2019-03-04 16:01:51 -07:00
|
|
|
mu sync.Mutex
|
2018-11-02 14:15:31 -06:00
|
|
|
|
2019-03-29 17:04:29 -06:00
|
|
|
// baseCtx is the context handed to NewView. This is the parent of all
|
|
|
|
// background contexts created for this view.
|
|
|
|
baseCtx context.Context
|
|
|
|
|
2019-03-05 15:30:44 -07:00
|
|
|
// backgroundCtx is the current context used by background tasks initiated
|
|
|
|
// by the view.
|
|
|
|
backgroundCtx context.Context
|
|
|
|
|
|
|
|
// cancel is called when all action being performed by the current view
|
|
|
|
// should be stopped.
|
|
|
|
cancel context.CancelFunc
|
|
|
|
|
2020-07-20 23:34:22 -06:00
|
|
|
// name is the user visible name of this view.
|
2019-05-14 21:04:23 -06:00
|
|
|
name string
|
2019-03-28 06:49:42 -06:00
|
|
|
|
2020-07-20 23:34:22 -06:00
|
|
|
// folder is the folder with which this view was constructed.
|
2019-05-14 21:04:23 -06:00
|
|
|
folder span.URI
|
2019-03-28 06:49:42 -06:00
|
|
|
|
2020-07-20 23:34:22 -06:00
|
|
|
// root is the root directory of this view. If we are in GOPATH mode, this
|
|
|
|
// is just the folder. If we are in module mode, this is the module root.
|
|
|
|
root span.URI
|
|
|
|
|
2020-01-21 11:57:40 -07:00
|
|
|
// importsMu guards imports-related state, particularly the ProcessEnv.
|
|
|
|
importsMu sync.Mutex
|
2020-06-18 10:30:59 -06:00
|
|
|
|
|
|
|
// processEnv is the process env for this view.
|
|
|
|
// Some of its fields can be changed dynamically by modifications to
|
|
|
|
// the view's options. These fields are repopulated for every use.
|
2019-07-03 13:23:05 -06:00
|
|
|
// Note: this contains cached module and filesystem state.
|
2019-07-12 16:54:06 -06:00
|
|
|
//
|
|
|
|
// 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.
|
2020-07-07 13:18:31 -06:00
|
|
|
processEnv *imports.ProcessEnv
|
|
|
|
cleanupProcessEnv func()
|
|
|
|
cacheRefreshDuration time.Duration
|
|
|
|
cacheRefreshTimer *time.Timer
|
|
|
|
cachedModFileIdentifier string
|
|
|
|
cachedBuildFlags []string
|
2019-07-12 16:54:06 -06:00
|
|
|
|
2019-03-27 07:25:30 -06:00
|
|
|
// keep track of files by uri and by basename, a single file may be mapped
|
|
|
|
// to multiple uris, and the same basename may map to multiple files
|
2019-12-17 16:57:54 -07:00
|
|
|
filesByURI map[span.URI]*fileBase
|
|
|
|
filesByBase map[string][]*fileBase
|
2019-02-06 16:47:00 -07:00
|
|
|
|
2019-09-23 18:06:15 -06:00
|
|
|
snapshotMu sync.Mutex
|
|
|
|
snapshot *snapshot
|
2019-03-04 16:01:51 -07:00
|
|
|
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
// initialized is closed when the view has been fully initialized. On
|
|
|
|
// initialization, the view's workspace packages are loaded. All of the
|
|
|
|
// fields below are set as part of initialization. If we failed to load, we
|
|
|
|
// only retry if the go.mod file changes, to avoid too many go/packages
|
|
|
|
// calls.
|
|
|
|
//
|
|
|
|
// When the view is created, initializeOnce is non-nil, initialized is
|
|
|
|
// open, and initCancelFirstAttempt can be used to terminate
|
|
|
|
// initialization. Once initialization completes, initializedErr may be set
|
|
|
|
// and initializeOnce becomes nil. If initializedErr is non-nil,
|
|
|
|
// initialization may be retried (depending on how files are changed). To
|
|
|
|
// indicate that initialization should be retried, initializeOnce will be
|
|
|
|
// set. The next time a caller requests workspace packages, the
|
|
|
|
// initialization will retry.
|
|
|
|
initialized chan struct{}
|
|
|
|
initCancelFirstAttempt context.CancelFunc
|
|
|
|
|
|
|
|
// initializationSema is used as a mutex to guard initializeOnce and
|
|
|
|
// initializedErr, which will be updated after each attempt to initialize
|
|
|
|
// the view. We use a channel instead of a mutex to avoid blocking when a
|
|
|
|
// context is canceled.
|
|
|
|
initializationSema chan struct{}
|
|
|
|
initializeOnce *sync.Once
|
|
|
|
initializedErr error
|
2020-05-28 19:21:29 -06:00
|
|
|
|
2020-01-14 22:22:42 -07:00
|
|
|
// builtin pins the AST and package for builtin.go in memory.
|
|
|
|
builtin *builtinPackageHandle
|
2020-01-23 23:22:47 -07:00
|
|
|
|
|
|
|
// True if the view is either in GOPATH, a module, or some other
|
|
|
|
// non go command build system.
|
|
|
|
hasValidBuildConfiguration bool
|
|
|
|
|
2020-06-10 23:11:52 -06:00
|
|
|
// The real go.mod and go.sum files that are attributed to a view.
|
|
|
|
modURI, sumURI span.URI
|
|
|
|
|
|
|
|
// True if this view runs go commands using temporary mod files.
|
|
|
|
// Only possible with Go versions 1.14 and above.
|
|
|
|
tmpMod bool
|
2020-01-23 23:22:47 -07:00
|
|
|
|
|
|
|
// goCommand indicates if the user is using the go command or some other
|
|
|
|
// build system.
|
|
|
|
goCommand bool
|
|
|
|
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
// `go env` variables that need to be tracked by gopls.
|
|
|
|
gocache, gomodcache, gopath, goprivate string
|
|
|
|
|
2020-07-19 19:55:23 -06:00
|
|
|
// goEnv is the `go env` output collected when a view is created.
|
|
|
|
// It includes the values of the environment variables above.
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
goEnv map[string]string
|
2020-01-14 22:22:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type builtinPackageHandle struct {
|
|
|
|
handle *memoize.Handle
|
|
|
|
}
|
|
|
|
|
|
|
|
type builtinPackageData struct {
|
|
|
|
memoize.NoCopy
|
|
|
|
|
|
|
|
pkg *ast.Package
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf *source.ParsedGoFile
|
2020-01-14 22:22:42 -07:00
|
|
|
err error
|
2018-09-27 16:15:45 -06:00
|
|
|
}
|
|
|
|
|
2020-06-05 15:30:52 -06:00
|
|
|
func (d *builtinPackageData) Package() *ast.Package {
|
|
|
|
return d.pkg
|
|
|
|
}
|
|
|
|
|
2020-07-21 13:15:06 -06:00
|
|
|
func (d *builtinPackageData) ParsedFile() *source.ParsedGoFile {
|
|
|
|
return d.pgf
|
2020-06-05 15:30:52 -06:00
|
|
|
}
|
|
|
|
|
2020-01-06 16:08:39 -07:00
|
|
|
// fileBase holds the common functionality for all files.
|
|
|
|
// It is intended to be embedded in the file implementations
|
|
|
|
type fileBase struct {
|
|
|
|
uris []span.URI
|
|
|
|
fname string
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
view *View
|
2020-01-06 16:08:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fileBase) URI() span.URI {
|
|
|
|
return f.uris[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fileBase) filename() string {
|
|
|
|
return f.fname
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fileBase) addURI(uri span.URI) int {
|
|
|
|
f.uris = append(f.uris, uri)
|
|
|
|
return len(f.uris)
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) ID() string { return v.id }
|
|
|
|
|
|
|
|
func (v *View) ValidBuildConfiguration() bool {
|
2020-01-23 23:22:47 -07:00
|
|
|
return v.hasValidBuildConfiguration
|
2019-12-16 13:40:24 -07:00
|
|
|
}
|
|
|
|
|
2020-06-10 23:11:52 -06:00
|
|
|
func (v *View) ModFile() span.URI {
|
|
|
|
return v.modURI
|
|
|
|
}
|
|
|
|
|
|
|
|
// tempModFile creates a temporary go.mod file based on the contents of the
|
|
|
|
// given go.mod file. It is the caller's responsibility to clean up the files
|
|
|
|
// when they are done using them.
|
|
|
|
func tempModFile(modFh, sumFH source.FileHandle) (tmpURI span.URI, cleanup func(), err error) {
|
|
|
|
filenameHash := hashContents([]byte(modFh.URI().Filename()))
|
|
|
|
tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash))
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
defer tmpMod.Close()
|
|
|
|
|
|
|
|
tmpURI = span.URIFromPath(tmpMod.Name())
|
2020-06-19 17:07:57 -06:00
|
|
|
tmpSumName := sumFilename(tmpURI)
|
2020-06-10 23:11:52 -06:00
|
|
|
|
|
|
|
content, err := modFh.Read()
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := tmpMod.Write(content); err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup = func() {
|
|
|
|
_ = os.Remove(tmpSumName)
|
|
|
|
_ = os.Remove(tmpURI.Filename())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Be careful to clean up if we return an error from this function.
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
cleanup()
|
|
|
|
cleanup = nil
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Create an analogous go.sum, if one exists.
|
|
|
|
if sumFH != nil {
|
|
|
|
sumContents, err := sumFH.Read()
|
|
|
|
if err != nil {
|
2020-07-22 09:32:32 -06:00
|
|
|
return "", cleanup, err
|
2020-06-10 23:11:52 -06:00
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile(tmpSumName, sumContents, 0655); err != nil {
|
2020-07-22 09:32:32 -06:00
|
|
|
return "", cleanup, err
|
2020-06-10 23:11:52 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmpURI, cleanup, nil
|
2020-01-23 14:54:21 -07:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Session() source.Session {
|
2019-05-15 10:24:49 -06:00
|
|
|
return v.session
|
2018-09-27 16:15:45 -06:00
|
|
|
}
|
|
|
|
|
2019-05-14 21:04:23 -06:00
|
|
|
// Name returns the user visible name of this view.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Name() string {
|
2019-05-14 21:04:23 -06:00
|
|
|
return v.name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Folder returns the root of this view.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Folder() span.URI {
|
2019-05-14 21:04:23 -06:00
|
|
|
return v.folder
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Options() source.Options {
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Lock()
|
|
|
|
defer v.optionsMu.Unlock()
|
2019-09-05 22:17:36 -06:00
|
|
|
return v.options
|
|
|
|
}
|
|
|
|
|
2019-11-08 11:25:29 -07:00
|
|
|
func minorOptionsChange(a, b source.Options) bool {
|
|
|
|
// Check if any of the settings that modify our understanding of files have been changed
|
|
|
|
if !reflect.DeepEqual(a.Env, b.Env) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(a.BuildFlags, b.BuildFlags) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// the rest of the options are benign
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
|
2019-11-08 11:25:29 -07:00
|
|
|
// no need to rebuild the view if the options were not materially changed
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Lock()
|
2019-11-08 11:25:29 -07:00
|
|
|
if minorOptionsChange(v.options, options) {
|
|
|
|
v.options = options
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Unlock()
|
2019-11-08 11:25:29 -07:00
|
|
|
return v, nil
|
|
|
|
}
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Unlock()
|
2019-11-15 12:47:29 -07:00
|
|
|
newView, _, err := v.session.updateView(ctx, v, options)
|
2019-11-08 11:25:29 -07:00
|
|
|
return newView, err
|
2019-09-11 16:21:01 -06:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Rebuild(ctx context.Context) (source.Snapshot, error) {
|
2020-04-02 10:14:48 -06:00
|
|
|
_, snapshot, err := v.session.updateView(ctx, v, v.Options())
|
2020-01-23 11:58:25 -07:00
|
|
|
return snapshot, err
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:30:52 -06:00
|
|
|
func (v *View) BuiltinPackage(ctx context.Context) (source.BuiltinPackage, error) {
|
2020-01-27 18:25:48 -07:00
|
|
|
v.awaitInitialized(ctx)
|
|
|
|
|
2020-02-02 16:10:18 -07:00
|
|
|
if v.builtin == nil {
|
|
|
|
return nil, errors.Errorf("no builtin package for view %s", v.name)
|
|
|
|
}
|
2020-07-16 15:37:12 -06:00
|
|
|
data, err := v.builtin.handle.Get(ctx, v)
|
2020-06-23 20:18:23 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-01-24 15:08:04 -07:00
|
|
|
}
|
2020-01-14 22:22:42 -07:00
|
|
|
if data == nil {
|
|
|
|
return nil, errors.Errorf("unexpected nil builtin package")
|
|
|
|
}
|
|
|
|
d, ok := data.(*builtinPackageData)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("unexpected type %T", data)
|
|
|
|
}
|
|
|
|
if d.err != nil {
|
|
|
|
return nil, d.err
|
|
|
|
}
|
|
|
|
if d.pkg == nil || d.pkg.Scope == nil {
|
|
|
|
return nil, errors.Errorf("no builtin package")
|
|
|
|
}
|
2020-06-05 15:30:52 -06:00
|
|
|
return d, nil
|
2020-01-14 22:22:42 -07:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
|
2020-01-30 18:52:23 -07:00
|
|
|
if len(goFiles) != 1 {
|
|
|
|
return errors.Errorf("only expected 1 file, got %v", len(goFiles))
|
2020-01-14 22:22:42 -07:00
|
|
|
}
|
2020-02-12 14:36:46 -07:00
|
|
|
uri := span.URIFromPath(goFiles[0])
|
2020-01-14 22:22:42 -07:00
|
|
|
|
2020-02-06 14:20:50 -07:00
|
|
|
// Get the FileHandle through the cache to avoid adding it to the snapshot
|
|
|
|
// and to get the file content from disk.
|
2020-06-19 17:07:57 -06:00
|
|
|
fh, err := v.session.cache.getFile(ctx, uri)
|
internal/lsp: read files eagerly
We use file identities pervasively throughout gopls. Prior to this
change, the identity is the modification date of an unopened file, or
the hash of an opened file. That means that opening a file changes its
identity, which causes unnecessary churn in the cache.
Unfortunately, there isn't an easy way to fix this. Changing the
cache key to something else, such as the modification time, means that
we won't unify cache entries if a change is made and then undone. The
approach here is to read files eagerly in GetFile, so that we know their
hashes immediately. That resolves the churn, but means that we do a ton
of file IO at startup.
Incidental changes:
Remove the FileSystem interface; there was only one implementation and
it added a fair amount of cruft. We have many other places that assume
os.Stat and such work.
Add direct accessors to FileHandle for URI, Kind, and Version. Most uses
of (FileHandle).Identity were for stuff that we derive solely from the
URI, and this helped me disentangle them. It is a *ton* of churn,
though. I can revert it if you want.
Change-Id: Ia2133bc527f71daf81c9d674951726a232ca5bc9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237037
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-08 13:21:24 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-16 15:37:12 -06:00
|
|
|
h := v.session.cache.store.Bind(fh.Identity(), func(ctx context.Context, arg memoize.Arg) interface{} {
|
|
|
|
view := arg.(*View)
|
|
|
|
|
|
|
|
pgh := view.session.cache.parseGoHandle(ctx, fh, source.ParseFull)
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf, _, err := view.parseGo(ctx, pgh)
|
2020-01-14 22:22:42 -07:00
|
|
|
if err != nil {
|
2020-06-05 15:30:52 -06:00
|
|
|
return &builtinPackageData{err: err}
|
2020-01-14 22:22:42 -07:00
|
|
|
}
|
2020-07-16 15:37:12 -06:00
|
|
|
pkg, err := ast.NewPackage(view.session.cache.fset, map[string]*ast.File{
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf.URI.Filename(): pgf.File,
|
2020-01-14 22:22:42 -07:00
|
|
|
}, nil, nil)
|
2020-06-05 15:30:52 -06:00
|
|
|
if err != nil {
|
|
|
|
return &builtinPackageData{err: err}
|
|
|
|
}
|
|
|
|
return &builtinPackageData{
|
2020-07-21 13:15:06 -06:00
|
|
|
pgf: pgf,
|
2020-06-05 15:30:52 -06:00
|
|
|
pkg: pkg,
|
|
|
|
}
|
2020-01-14 22:22:42 -07:00
|
|
|
})
|
|
|
|
v.builtin = &builtinPackageHandle{
|
|
|
|
handle: h,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) WriteEnv(ctx context.Context, w io.Writer) error {
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Lock()
|
|
|
|
env, buildFlags := v.envLocked()
|
|
|
|
v.optionsMu.Unlock()
|
2020-06-10 23:11:52 -06:00
|
|
|
|
2020-07-19 19:55:23 -06:00
|
|
|
fullEnv := make(map[string]string)
|
|
|
|
for k, v := range v.goEnv {
|
|
|
|
fullEnv[k] = v
|
2020-03-23 20:47:52 -06:00
|
|
|
}
|
2020-07-19 19:55:23 -06:00
|
|
|
for _, v := range env {
|
2020-07-20 10:09:35 -06:00
|
|
|
s := strings.SplitN(v, "=", 2)
|
2020-07-19 19:55:23 -06:00
|
|
|
if len(s) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _, ok := fullEnv[s[0]]; ok {
|
|
|
|
fullEnv[s[0]] = s[1]
|
|
|
|
}
|
|
|
|
|
2020-03-23 20:47:52 -06:00
|
|
|
}
|
2020-07-20 23:34:22 -06:00
|
|
|
fmt.Fprintf(w, "go env for %v\n(root %s)\n(valid build configuration = %v)\n(build flags: %v)\n",
|
|
|
|
v.folder.Filename(), v.root.Filename(), v.hasValidBuildConfiguration, buildFlags)
|
2020-07-19 19:55:23 -06:00
|
|
|
for k, v := range fullEnv {
|
|
|
|
fmt.Fprintf(w, "%s=%s\n", k, v)
|
|
|
|
}
|
2020-03-23 20:47:52 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
|
2020-01-21 11:57:40 -07:00
|
|
|
v.importsMu.Lock()
|
|
|
|
defer v.importsMu.Unlock()
|
|
|
|
|
2020-07-07 13:18:31 -06:00
|
|
|
// Use temporary go.mod files, but always go to disk for the contents.
|
|
|
|
// Rebuilding the cache is expensive, and we don't want to do it for
|
|
|
|
// transient changes.
|
2020-06-18 10:30:59 -06:00
|
|
|
var modFH, sumFH source.FileHandle
|
2020-07-07 13:18:31 -06:00
|
|
|
var modFileIdentifier string
|
|
|
|
var err error
|
|
|
|
if v.modURI != "" {
|
2020-06-19 17:07:57 -06:00
|
|
|
modFH, err = v.session.cache.getFile(ctx, v.modURI)
|
2020-06-18 10:30:59 -06:00
|
|
|
if err != nil {
|
2019-08-28 14:02:38 -06:00
|
|
|
return err
|
|
|
|
}
|
2020-07-07 13:18:31 -06:00
|
|
|
modFileIdentifier = modFH.Identity().Identifier
|
2019-07-03 13:23:05 -06:00
|
|
|
}
|
2020-07-07 13:18:31 -06:00
|
|
|
if v.sumURI != "" {
|
|
|
|
sumFH, err = v.session.cache.getFile(ctx, v.sumURI)
|
internal/lsp: read files eagerly
We use file identities pervasively throughout gopls. Prior to this
change, the identity is the modification date of an unopened file, or
the hash of an opened file. That means that opening a file changes its
identity, which causes unnecessary churn in the cache.
Unfortunately, there isn't an easy way to fix this. Changing the
cache key to something else, such as the modification time, means that
we won't unify cache entries if a change is made and then undone. The
approach here is to read files eagerly in GetFile, so that we know their
hashes immediately. That resolves the churn, but means that we do a ton
of file IO at startup.
Incidental changes:
Remove the FileSystem interface; there was only one implementation and
it added a fair amount of cruft. We have many other places that assume
os.Stat and such work.
Add direct accessors to FileHandle for URI, Kind, and Version. Most uses
of (FileHandle).Identity were for stuff that we derive solely from the
URI, and this helped me disentangle them. It is a *ton* of churn,
though. I can revert it if you want.
Change-Id: Ia2133bc527f71daf81c9d674951726a232ca5bc9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/237037
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-08 13:21:24 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-07 13:18:31 -06:00
|
|
|
}
|
|
|
|
// v.goEnv is immutable -- changes make a new view. Options can change.
|
|
|
|
// We can't compare build flags directly because we may add -modfile.
|
|
|
|
v.optionsMu.Lock()
|
|
|
|
localPrefix := v.options.LocalPrefix
|
|
|
|
currentBuildFlags := v.options.BuildFlags
|
|
|
|
changed := !reflect.DeepEqual(currentBuildFlags, v.cachedBuildFlags) ||
|
|
|
|
v.options.VerboseOutput != (v.processEnv.Logf != nil) ||
|
|
|
|
modFileIdentifier != v.cachedModFileIdentifier
|
|
|
|
v.optionsMu.Unlock()
|
|
|
|
|
|
|
|
// If anything relevant to imports has changed, clear caches and
|
|
|
|
// update the processEnv. Clearing caches blocks on any background
|
|
|
|
// scans.
|
|
|
|
if changed {
|
|
|
|
// As a special case, skip cleanup the first time -- we haven't fully
|
|
|
|
// initialized the environment yet and calling GetResolver will do
|
|
|
|
// unnecessary work and potentially mess up the go.mod file.
|
|
|
|
if v.cleanupProcessEnv != nil {
|
2020-07-06 13:18:50 -06:00
|
|
|
if resolver, err := v.processEnv.GetResolver(); err == nil {
|
|
|
|
resolver.(*imports.ModuleResolver).ClearForNewMod()
|
|
|
|
}
|
2020-07-07 13:18:31 -06:00
|
|
|
v.cleanupProcessEnv()
|
|
|
|
}
|
2020-07-08 11:52:55 -06:00
|
|
|
v.cachedModFileIdentifier = modFileIdentifier
|
2020-07-07 13:18:31 -06:00
|
|
|
v.cachedBuildFlags = currentBuildFlags
|
|
|
|
v.cleanupProcessEnv, err = v.populateProcessEnv(ctx, modFH, sumFH)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-01-17 13:33:57 -07:00
|
|
|
}
|
2019-07-12 16:54:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the user function.
|
2019-12-27 12:07:33 -07:00
|
|
|
opts := &imports.Options{
|
|
|
|
// Defaults.
|
2020-07-06 13:18:50 -06:00
|
|
|
AllErrors: true,
|
|
|
|
Comments: true,
|
|
|
|
Fragment: true,
|
|
|
|
FormatOnly: false,
|
|
|
|
TabIndent: true,
|
|
|
|
TabWidth: 8,
|
|
|
|
Env: v.processEnv,
|
|
|
|
LocalPrefix: localPrefix,
|
2019-12-27 12:07:33 -07:00
|
|
|
}
|
|
|
|
|
2019-07-12 16:54:06 -06:00
|
|
|
if err := fn(opts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-18 17:31:48 -07:00
|
|
|
if v.cacheRefreshTimer == nil {
|
|
|
|
// Don't refresh more than twice per minute.
|
|
|
|
delay := 30 * time.Second
|
|
|
|
// Don't spend more than a couple percent of the time refreshing.
|
|
|
|
if adaptive := 50 * v.cacheRefreshDuration; adaptive > delay {
|
|
|
|
delay = adaptive
|
|
|
|
}
|
|
|
|
v.cacheRefreshTimer = time.AfterFunc(delay, v.refreshProcessEnv)
|
internal/lsp/cache: only refresh imports cache every 30 seconds
Loading completion suggestions can be slow, especially in GOPATH mode
where basically anything can change at any time. As a compromise, cache
everything for 30 seconds. Specifically, after a completion operation
finishes, if the cache is more than 30 seconds old, refresh it
asynchronously. That keeps user-facing latency consistent, without
chewing up CPU when the editor isn't in use. It does mean that if you
walk away for an hour and come back, the first completion may be stale.
In module mode this is relatively benign. The only things the
longer caching affects are the main module and replace targets, and
relevant packages in those will generally be loaded by gopls, so they'll
have full, up-to-date type information regardless.
In GOPATH mode this may be more troublesome, since it affects
everything. In particular, go get -u of a package that isn't imported
yet won't be reflected until the cache period expires. I think that's a
rare enough case not to worry about.
Change-Id: Iaadfd0ff647cda2b1dcdead9254b5492b397e86e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/205163
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-11-04 12:05:41 -07:00
|
|
|
}
|
2019-12-18 17:31:48 -07:00
|
|
|
|
2019-07-12 16:54:06 -06:00
|
|
|
return nil
|
2019-07-03 13:23:05 -06:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) refreshProcessEnv() {
|
2019-12-18 17:31:48 -07:00
|
|
|
start := time.Now()
|
|
|
|
|
2020-01-21 11:57:40 -07:00
|
|
|
v.importsMu.Lock()
|
2019-12-18 17:31:48 -07:00
|
|
|
env := v.processEnv
|
2020-07-06 13:18:50 -06:00
|
|
|
if resolver, err := v.processEnv.GetResolver(); err == nil {
|
|
|
|
resolver.ClearForNewScan()
|
|
|
|
}
|
2020-01-21 11:57:40 -07:00
|
|
|
v.importsMu.Unlock()
|
2019-12-18 17:31:48 -07:00
|
|
|
|
|
|
|
// We don't have a context handy to use for logging, so use the stdlib for now.
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(v.baseCtx, "background imports cache refresh starting")
|
2020-07-06 13:18:50 -06:00
|
|
|
if err := imports.PrimeCache(context.Background(), env); err == nil {
|
2020-04-25 08:13:34 -06:00
|
|
|
event.Log(v.baseCtx, fmt.Sprintf("background refresh finished after %v", time.Since(start)))
|
|
|
|
} else {
|
|
|
|
event.Log(v.baseCtx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err))
|
|
|
|
}
|
2020-01-21 11:57:40 -07:00
|
|
|
v.importsMu.Lock()
|
2019-12-18 17:31:48 -07:00
|
|
|
v.cacheRefreshDuration = time.Since(start)
|
|
|
|
v.cacheRefreshTimer = nil
|
2020-01-21 11:57:40 -07:00
|
|
|
v.importsMu.Unlock()
|
2019-12-18 17:31:48 -07:00
|
|
|
}
|
|
|
|
|
2020-06-18 10:30:59 -06:00
|
|
|
// populateProcessEnv sets the dynamically configurable fields for the view's
|
2020-07-07 13:18:31 -06:00
|
|
|
// process environment. Assumes that the caller is holding the s.view.importsMu.
|
2020-06-18 10:30:59 -06:00
|
|
|
func (v *View) populateProcessEnv(ctx context.Context, modFH, sumFH source.FileHandle) (cleanup func(), err error) {
|
|
|
|
cleanup = func() {}
|
2020-07-07 13:18:31 -06:00
|
|
|
pe := v.processEnv
|
2020-06-18 10:30:59 -06:00
|
|
|
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Lock()
|
2020-07-07 13:18:31 -06:00
|
|
|
pe.BuildFlags = append([]string(nil), v.options.BuildFlags...)
|
|
|
|
if v.options.VerboseOutput {
|
|
|
|
pe.Logf = func(format string, args ...interface{}) {
|
|
|
|
event.Log(ctx, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pe.Logf = nil
|
|
|
|
}
|
2020-04-02 10:14:48 -06:00
|
|
|
v.optionsMu.Unlock()
|
2020-06-10 23:11:52 -06:00
|
|
|
|
2020-06-18 10:30:59 -06:00
|
|
|
// Add -modfile to the build flags, if we are using it.
|
2020-07-07 13:18:31 -06:00
|
|
|
if v.tmpMod && modFH != nil {
|
2020-06-18 10:30:59 -06:00
|
|
|
var tmpURI span.URI
|
|
|
|
tmpURI, cleanup, err = tempModFile(modFH, sumFH)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pe.BuildFlags = append(pe.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
|
2020-02-28 15:03:27 -07:00
|
|
|
}
|
2020-06-18 10:30:59 -06:00
|
|
|
|
|
|
|
return cleanup, nil
|
2020-02-06 15:49:19 -07:00
|
|
|
}
|
|
|
|
|
2020-06-11 16:25:17 -06:00
|
|
|
// envLocked returns the environment and build flags for the current view.
|
|
|
|
// It assumes that the caller is holding the view's optionsMu.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) envLocked() ([]string, []string) {
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
env := append([]string{}, v.options.Env...)
|
2020-06-10 23:11:52 -06:00
|
|
|
buildFlags := append([]string{}, v.options.BuildFlags...)
|
2020-02-06 15:49:19 -07:00
|
|
|
return env, buildFlags
|
2019-07-03 13:23:05 -06:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) contains(uri span.URI) bool {
|
2020-07-20 23:34:22 -06:00
|
|
|
return strings.HasPrefix(string(uri), string(v.root))
|
2020-01-30 18:52:23 -07:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) mapFile(uri span.URI, f *fileBase) {
|
2020-01-06 16:08:39 -07:00
|
|
|
v.filesByURI[uri] = f
|
|
|
|
if f.addURI(uri) == 1 {
|
|
|
|
basename := basename(f.filename())
|
|
|
|
v.filesByBase[basename] = append(v.filesByBase[basename], f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func basename(filename string) string {
|
|
|
|
return strings.ToLower(filepath.Base(filename))
|
|
|
|
}
|
|
|
|
|
2020-06-20 14:11:01 -06:00
|
|
|
func (v *View) WorkspaceDirectories(ctx context.Context) ([]string, error) {
|
|
|
|
// If the view does not have a go.mod file, only the root directory
|
|
|
|
// is known. In GOPATH mode, we should really watch the entire GOPATH,
|
|
|
|
// but that's probably too expensive.
|
|
|
|
// TODO(rstambler): Figure out a better approach in the future.
|
|
|
|
if v.modURI == "" {
|
2020-07-20 23:34:22 -06:00
|
|
|
return []string{v.root.Filename()}, nil
|
2020-06-20 14:11:01 -06:00
|
|
|
}
|
|
|
|
// Anything inside of the module root is known.
|
|
|
|
dirs := []string{filepath.Dir(v.modURI.Filename())}
|
|
|
|
|
|
|
|
// Keep track of any directories mentioned in replace targets.
|
|
|
|
fh, err := v.session.GetFile(ctx, v.modURI)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pmh, err := v.Snapshot().ParseModHandle(ctx, fh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-16 15:37:12 -06:00
|
|
|
parsed, _, _, err := pmh.Parse(ctx, v.Snapshot())
|
2020-06-20 14:11:01 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, replace := range parsed.Replace {
|
|
|
|
dirs = append(dirs, replace.New.Path)
|
|
|
|
}
|
|
|
|
return dirs, nil
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) relevantChange(c source.FileModification) bool {
|
2020-02-12 13:18:16 -07:00
|
|
|
// If the file is known to the view, the change is relevant.
|
|
|
|
known := v.knownFile(c.URI)
|
|
|
|
|
|
|
|
// If the file is not known to the view, and the change is only on-disk,
|
|
|
|
// we should not invalidate the snapshot. This is necessary because Emacs
|
|
|
|
// sends didChangeWatchedFiles events for temp files.
|
|
|
|
if !known && c.OnDisk && (c.Action == source.Change || c.Action == source.Delete) {
|
|
|
|
return false
|
2020-02-10 12:45:18 -07:00
|
|
|
}
|
2020-02-12 13:18:16 -07:00
|
|
|
return v.contains(c.URI) || known
|
|
|
|
}
|
2020-02-10 12:45:18 -07:00
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) knownFile(uri span.URI) bool {
|
2020-01-06 16:08:39 -07:00
|
|
|
v.mu.Lock()
|
|
|
|
defer v.mu.Unlock()
|
|
|
|
|
2020-02-12 13:18:16 -07:00
|
|
|
f, err := v.findFile(uri)
|
2020-01-09 20:45:06 -07:00
|
|
|
return f != nil && err == nil
|
2020-01-06 16:08:39 -07:00
|
|
|
}
|
|
|
|
|
2020-01-21 13:36:50 -07:00
|
|
|
// getFile returns a file for the given URI. It will always succeed because it
|
2020-01-06 16:08:39 -07:00
|
|
|
// adds the file to the managed set if needed.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) getFile(uri span.URI) (*fileBase, error) {
|
2020-01-06 16:08:39 -07:00
|
|
|
v.mu.Lock()
|
|
|
|
defer v.mu.Unlock()
|
|
|
|
|
|
|
|
f, err := v.findFile(uri)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if f != nil {
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
f = &fileBase{
|
|
|
|
view: v,
|
|
|
|
fname: uri.Filename(),
|
|
|
|
}
|
|
|
|
v.mapFile(uri, f)
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// findFile checks the cache for any file matching the given uri.
|
|
|
|
//
|
|
|
|
// An error is only returned for an irreparable failure, for example, if the
|
|
|
|
// filename in question does not exist.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) findFile(uri span.URI) (*fileBase, error) {
|
2020-01-06 16:08:39 -07:00
|
|
|
if f := v.filesByURI[uri]; f != nil {
|
|
|
|
// a perfect match
|
|
|
|
return f, nil
|
|
|
|
}
|
|
|
|
// no exact match stored, time to do some real work
|
|
|
|
// check for any files with the same basename
|
|
|
|
fname := uri.Filename()
|
|
|
|
basename := basename(fname)
|
|
|
|
if candidates := v.filesByBase[basename]; candidates != nil {
|
|
|
|
pathStat, err := os.Stat(fname)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil // the file may exist, return without an error
|
|
|
|
}
|
|
|
|
for _, c := range candidates {
|
|
|
|
if cStat, err := os.Stat(c.filename()); err == nil {
|
|
|
|
if os.SameFile(pathStat, cStat) {
|
|
|
|
// same file, map it
|
|
|
|
v.mapFile(uri, c)
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// no file with a matching name was found, it wasn't in our cache
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Shutdown(ctx context.Context) {
|
2019-05-15 10:24:49 -06:00
|
|
|
v.session.removeView(ctx, v)
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) shutdown(ctx context.Context) {
|
2020-06-11 22:24:37 -06:00
|
|
|
// Cancel the initial workspace load if it is still running.
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
v.initCancelFirstAttempt()
|
2020-06-11 22:24:37 -06:00
|
|
|
|
2019-05-02 08:55:04 -06:00
|
|
|
v.mu.Lock()
|
|
|
|
defer v.mu.Unlock()
|
|
|
|
if v.cancel != nil {
|
|
|
|
v.cancel()
|
|
|
|
v.cancel = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) BackgroundContext() context.Context {
|
2019-03-05 15:30:44 -07:00
|
|
|
v.mu.Lock()
|
|
|
|
defer v.mu.Unlock()
|
|
|
|
|
|
|
|
return v.backgroundCtx
|
|
|
|
}
|
|
|
|
|
2020-06-22 12:38:32 -06:00
|
|
|
func (v *View) IgnoredFile(uri span.URI) bool {
|
|
|
|
filename := uri.Filename()
|
|
|
|
var prefixes []string
|
|
|
|
if v.modURI == "" {
|
|
|
|
for _, entry := range filepath.SplitList(v.gopath) {
|
|
|
|
prefixes = append(prefixes, filepath.Join(entry, "src"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mainMod := filepath.Dir(v.modURI.Filename())
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
prefixes = []string{mainMod, v.gomodcache}
|
2020-06-22 12:38:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, prefix := range prefixes {
|
|
|
|
if strings.HasPrefix(filename, prefix) {
|
|
|
|
return checkIgnored(filename[len(prefix):])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkIgnored implements go list's exclusion rules. go help list:
|
|
|
|
// Directory and file names that begin with "." or "_" are ignored
|
|
|
|
// by the go tool, as are directories named "testdata".
|
|
|
|
func checkIgnored(suffix string) bool {
|
|
|
|
for _, component := range strings.Split(suffix, string(filepath.Separator)) {
|
|
|
|
if len(component) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if component[0] == '.' || component[0] == '_' || component == "testdata" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) Snapshot() source.Snapshot {
|
2019-09-27 11:17:59 -06:00
|
|
|
return v.getSnapshot()
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) getSnapshot() *snapshot {
|
2019-09-27 11:17:59 -06:00
|
|
|
v.snapshotMu.Lock()
|
|
|
|
defer v.snapshotMu.Unlock()
|
|
|
|
|
|
|
|
return v.snapshot
|
|
|
|
}
|
|
|
|
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
func (v *View) initialize(ctx context.Context, s *snapshot, firstAttempt bool) {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case v.initializationSema <- struct{}{}:
|
|
|
|
}
|
2020-01-30 18:44:00 -07:00
|
|
|
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
defer func() {
|
|
|
|
<-v.initializationSema
|
|
|
|
}()
|
|
|
|
|
|
|
|
if v.initializeOnce == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v.initializeOnce.Do(func() {
|
|
|
|
defer func() {
|
|
|
|
v.initializeOnce = nil
|
|
|
|
if firstAttempt {
|
|
|
|
close(v.initialized)
|
2020-06-11 22:24:37 -06:00
|
|
|
}
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
}()
|
|
|
|
|
|
|
|
err := s.load(ctx, viewLoadScope("LOAD_VIEW"), packagePath("builtin"))
|
|
|
|
if ctx.Err() != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err != nil {
|
2020-03-07 19:28:21 -07:00
|
|
|
event.Error(ctx, "initial workspace load failed", err)
|
2020-01-27 18:25:48 -07:00
|
|
|
}
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
v.initializedErr = err
|
2019-12-19 12:31:39 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) awaitInitialized(ctx context.Context) {
|
2019-12-19 12:31:39 -07:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
return
|
2020-01-10 15:18:59 -07:00
|
|
|
case <-v.initialized:
|
2019-12-19 12:31:39 -07:00
|
|
|
}
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
// We typically prefer to run something as intensive as the IWL without
|
|
|
|
// blocking. I'm not sure if there is a way to do that here.
|
|
|
|
v.initialize(ctx, v.getSnapshot(), false)
|
2019-12-19 12:31:39 -07:00
|
|
|
}
|
|
|
|
|
2019-12-17 14:53:57 -07:00
|
|
|
// invalidateContent invalidates the content of a Go file,
|
|
|
|
// including any position and type information that depends on it.
|
|
|
|
// It returns true if we were already tracking the given file, false otherwise.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) invalidateContent(ctx context.Context, uris map[span.URI]source.FileHandle, forceReloadMetadata bool) source.Snapshot {
|
2019-12-19 13:47:07 -07:00
|
|
|
// Detach the context so that content invalidation cannot be canceled.
|
|
|
|
ctx = xcontext.Detach(ctx)
|
|
|
|
|
2019-12-17 14:53:57 -07:00
|
|
|
// Cancel all still-running previous requests, since they would be
|
|
|
|
// operating on stale data.
|
2020-01-22 19:58:50 -07:00
|
|
|
v.cancelBackground()
|
2019-12-17 14:53:57 -07:00
|
|
|
|
2020-01-10 15:18:59 -07:00
|
|
|
// Do not clone a snapshot until its view has finished initializing.
|
2020-01-27 18:25:48 -07:00
|
|
|
v.awaitInitialized(ctx)
|
2019-12-19 12:31:39 -07:00
|
|
|
|
2019-12-17 14:53:57 -07:00
|
|
|
// This should be the only time we hold the view's snapshot lock for any period of time.
|
|
|
|
v.snapshotMu.Lock()
|
|
|
|
defer v.snapshotMu.Unlock()
|
|
|
|
|
2020-05-14 12:02:48 -06:00
|
|
|
v.snapshot = v.snapshot.clone(ctx, uris, forceReloadMetadata)
|
2019-12-17 14:53:57 -07:00
|
|
|
return v.snapshot
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) cancelBackground() {
|
2019-03-05 15:30:44 -07:00
|
|
|
v.mu.Lock()
|
|
|
|
defer v.mu.Unlock()
|
2020-04-30 13:59:37 -06:00
|
|
|
if v.cancel == nil {
|
|
|
|
// this can happen during shutdown
|
|
|
|
return
|
|
|
|
}
|
2019-03-05 15:30:44 -07:00
|
|
|
v.cancel()
|
2019-03-29 17:04:29 -06:00
|
|
|
v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
|
2019-03-05 15:30:44 -07:00
|
|
|
}
|
|
|
|
|
internal/lsp: handle unknown revision in go.mod file
This change ensures that, when the initial workspace load fails, we
re-run it if the go.mod file changes. Previously, if a user opened a
workspace with a corrupt go.mod file, we never recovered.
To reinitialize the workspace on-demand, we use the initializeOnce field
as an indicator of whether or not we should reinitialize. Every call to
awaitInitialized (which is called by all functions that need the IWL),
passes through the initialization code. If a retry isn't necessary,
this is a no-op, but if it is, we will call the initialization logic.
Only the first attempt uses a detached context; subsequent attempts can
be canceled by their contexts.
To indicate that we should reinitialize, we call maybeReinitialize.
Right now, we only call this when the go.mod file changes. In the
future, we may need it in other cases.
Fixes golang/go#38232
Change-Id: I77eefebb0ac38fbd0fe2c7da09c864eba45b075f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242159
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-07-11 23:52:22 -06:00
|
|
|
func (v *View) maybeReinitialize() {
|
|
|
|
v.initializationSema <- struct{}{}
|
|
|
|
defer func() {
|
|
|
|
<-v.initializationSema
|
|
|
|
}()
|
|
|
|
|
|
|
|
if v.initializedErr == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var once sync.Once
|
|
|
|
v.initializeOnce = &once
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
|
2020-03-24 15:19:36 -06:00
|
|
|
if err := checkPathCase(folder.Filename()); err != nil {
|
2020-05-01 08:05:39 -06:00
|
|
|
return fmt.Errorf("invalid workspace configuration: %w", err)
|
2020-03-24 15:19:36 -06:00
|
|
|
}
|
2020-01-23 23:22:47 -07:00
|
|
|
// Make sure to get the `go env` before continuing with initialization.
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
modFile, err := v.setGoEnv(ctx, env)
|
2020-01-23 23:22:47 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if modFile == os.DevNull {
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-10 23:11:52 -06:00
|
|
|
v.modURI = span.URIFromPath(modFile)
|
|
|
|
// Set the sumURI, if the go.sum exists.
|
|
|
|
sumFilename := filepath.Join(filepath.Dir(modFile), "go.sum")
|
|
|
|
if stat, _ := os.Stat(sumFilename); stat != nil {
|
|
|
|
v.sumURI = span.URIFromPath(sumFilename)
|
|
|
|
}
|
2020-01-23 23:22:47 -07:00
|
|
|
|
2020-07-20 23:34:22 -06:00
|
|
|
v.root = v.folder
|
|
|
|
if v.modURI != "" {
|
|
|
|
v.root = span.URIFromPath(filepath.Dir(v.modURI.Filename()))
|
|
|
|
}
|
|
|
|
|
2020-01-23 23:22:47 -07:00
|
|
|
// Now that we have set all required fields,
|
|
|
|
// check if the view has a valid build configuration.
|
2020-06-10 23:11:52 -06:00
|
|
|
v.setBuildConfiguration()
|
2020-01-23 23:22:47 -07:00
|
|
|
|
2020-02-12 14:42:17 -07:00
|
|
|
// The user has disabled the use of the -modfile flag or has no go.mod file.
|
2020-06-10 23:11:52 -06:00
|
|
|
if !modfileFlagEnabled || v.modURI == "" {
|
2020-01-23 23:22:47 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if modfileFlag, err := v.modfileFlagExists(ctx, v.Options().Env); err != nil {
|
|
|
|
return err
|
2020-06-10 23:11:52 -06:00
|
|
|
} else if modfileFlag {
|
|
|
|
v.tmpMod = true
|
2020-01-23 23:22:47 -07:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-24 15:19:36 -06:00
|
|
|
// OS-specific path case check, for case-insensitive filesystems.
|
|
|
|
var checkPathCase = defaultCheckPathCase
|
|
|
|
|
|
|
|
func defaultCheckPathCase(path string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-10 23:11:52 -06:00
|
|
|
func (v *View) setBuildConfiguration() (isValid bool) {
|
|
|
|
defer func() {
|
|
|
|
v.hasValidBuildConfiguration = isValid
|
|
|
|
}()
|
2020-01-23 23:22:47 -07:00
|
|
|
// Since we only really understand the `go` command, if the user is not
|
|
|
|
// using the go command, assume that their configuration is valid.
|
2020-06-10 23:11:52 -06:00
|
|
|
if !v.goCommand {
|
2020-01-23 23:22:47 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Check if the user is working within a module.
|
2020-06-10 23:11:52 -06:00
|
|
|
if v.modURI != "" {
|
2020-01-23 23:22:47 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// The user may have a multiple directories in their GOPATH.
|
|
|
|
// Check if the workspace is within any of them.
|
2020-06-10 23:11:52 -06:00
|
|
|
for _, gp := range filepath.SplitList(v.gopath) {
|
|
|
|
if isSubdirectory(filepath.Join(gp, "src"), v.folder.Filename()) {
|
2020-01-23 23:22:47 -07:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func isSubdirectory(root, leaf string) bool {
|
|
|
|
rel, err := filepath.Rel(root, leaf)
|
|
|
|
return err == nil && !strings.HasPrefix(rel, "..")
|
|
|
|
}
|
|
|
|
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
// setGoEnv sets the view's various GO* values. It also returns the view's
|
|
|
|
// GOMOD value, which need not be cached.
|
|
|
|
func (v *View) setGoEnv(ctx context.Context, configEnv []string) (string, error) {
|
|
|
|
var gomod string
|
|
|
|
vars := map[string]*string{
|
|
|
|
"GOCACHE": &v.gocache,
|
|
|
|
"GOPATH": &v.gopath,
|
|
|
|
"GOPRIVATE": &v.goprivate,
|
|
|
|
"GOMODCACHE": &v.gomodcache,
|
|
|
|
"GOMOD": &gomod,
|
2020-01-23 23:22:47 -07:00
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
// We can save ~200 ms by requesting only the variables we care about.
|
|
|
|
args := append([]string{"-json"}, imports.RequiredGoEnvVars...)
|
|
|
|
for k := range vars {
|
|
|
|
args = append(args, k)
|
2020-01-07 19:37:41 -07:00
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
|
2020-02-24 14:46:08 -07:00
|
|
|
inv := gocommand.Invocation{
|
|
|
|
Verb: "env",
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
Args: args,
|
|
|
|
Env: configEnv,
|
2020-02-24 14:46:08 -07:00
|
|
|
WorkingDir: v.Folder().Filename(),
|
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
// Don't go through runGoCommand, as we don't need a temporary -modfile to
|
|
|
|
// run `go env`.
|
2020-06-25 23:34:55 -06:00
|
|
|
stdout, err := v.session.gocmdRunner.Run(ctx, inv)
|
2020-01-11 18:29:13 -07:00
|
|
|
if err != nil {
|
2020-01-23 15:36:38 -07:00
|
|
|
return "", err
|
2020-01-11 18:29:13 -07:00
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
if err := json.Unmarshal(stdout.Bytes(), &v.goEnv); err != nil {
|
2020-01-23 15:36:38 -07:00
|
|
|
return "", err
|
2020-01-11 18:29:13 -07:00
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
|
|
|
|
for key, ptr := range vars {
|
|
|
|
*ptr = v.goEnv[key]
|
2020-01-23 23:22:47 -07:00
|
|
|
}
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
|
|
|
|
// Old versions of Go don't have GOMODCACHE, so emulate it.
|
|
|
|
if v.gomodcache == "" && v.gopath != "" {
|
|
|
|
v.gomodcache = filepath.Join(filepath.SplitList(v.gopath)[0], "pkg/mod")
|
2020-01-11 18:29:13 -07:00
|
|
|
}
|
2020-05-12 19:56:33 -06:00
|
|
|
|
all: rework goimports environment, support GOMODCACHE
This CL got away from me a little.
For a number of reasons, the existing goimports API of passing in values
for various GO* values was not working. For one, the number of necessary
variables kept growing. For another, we tried to avoid calling `go env`
in GOPATH mode by using `build.Default`, but that turns out to be buggy;
see golang/go#39838. And finally, it created massive confusion about
whether the values were intended to be read from the OS environment, or
fully evaluated by the `go` command.
There are only two users of the internal imports API, so there really
shouldn't need to be more than two modes. For the command line tool, we
have to call `go env` to deal with the `go/build` bug. So we just do it.
Tests use that same path, but can augment the enviroment to set
themselves up. In contrast, `gopls` needs to fully specify the
environment. It can simply pass in the fully evaluated GO* values.
Finally, make the change I was actually here to make: propagate
GOMODCACHE and use it where appropriate.
Fixes golang/go#39761.
Change-Id: I720c69839d91d66d98e94dfc5f065ba0279c5542
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239754
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-06-23 12:44:42 -06:00
|
|
|
// The value of GOPACKAGESDRIVER is not returned through the go command.
|
|
|
|
gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
|
|
|
|
v.goCommand = gopackagesdriver == "" || gopackagesdriver == "off"
|
|
|
|
return gomod, nil
|
2020-05-12 19:56:33 -06:00
|
|
|
}
|
|
|
|
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) IsGoPrivatePath(target string) bool {
|
2020-05-12 19:56:33 -06:00
|
|
|
return globsMatchPath(v.goprivate, target)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copied from
|
|
|
|
// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
|
|
|
|
func globsMatchPath(globs, target string) bool {
|
|
|
|
for globs != "" {
|
|
|
|
// Extract next non-empty glob in comma-separated list.
|
|
|
|
var glob string
|
|
|
|
if i := strings.Index(globs, ","); i >= 0 {
|
|
|
|
glob, globs = globs[:i], globs[i+1:]
|
|
|
|
} else {
|
|
|
|
glob, globs = globs, ""
|
|
|
|
}
|
|
|
|
if glob == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// A glob with N+1 path elements (N slashes) needs to be matched
|
|
|
|
// against the first N+1 path elements of target,
|
|
|
|
// which end just before the N+1'th slash.
|
|
|
|
n := strings.Count(glob, "/")
|
|
|
|
prefix := target
|
|
|
|
// Walk target, counting slashes, truncating at the N+1'th slash.
|
|
|
|
for i := 0; i < len(target); i++ {
|
|
|
|
if target[i] == '/' {
|
|
|
|
if n == 0 {
|
|
|
|
prefix = target[:i]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
n--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if n > 0 {
|
|
|
|
// Not enough prefix elements.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
matched, _ := path.Match(glob, prefix)
|
|
|
|
if matched {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2020-01-07 19:37:41 -07:00
|
|
|
}
|
2020-01-23 22:40:20 -07:00
|
|
|
|
2020-07-20 23:34:22 -06:00
|
|
|
// This function will return the main go.mod file for this folder if it exists
|
|
|
|
// and whether the -modfile flag exists for this version of go.
|
2020-06-02 08:57:20 -06:00
|
|
|
func (v *View) modfileFlagExists(ctx context.Context, env []string) (bool, error) {
|
2020-01-23 22:40:20 -07:00
|
|
|
// Check the go version by running "go list" with modules off.
|
|
|
|
// Borrowed from internal/imports/mod.go:620.
|
|
|
|
const format = `{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}`
|
|
|
|
folder := v.folder.Filename()
|
2020-02-24 14:46:08 -07:00
|
|
|
inv := gocommand.Invocation{
|
|
|
|
Verb: "list",
|
|
|
|
Args: []string{"-e", "-f", format},
|
|
|
|
Env: append(env, "GO111MODULE=off"),
|
2020-07-20 23:34:22 -06:00
|
|
|
WorkingDir: v.root.Filename(),
|
2020-02-24 14:46:08 -07:00
|
|
|
}
|
2020-06-25 23:34:55 -06:00
|
|
|
stdout, err := v.session.gocmdRunner.Run(ctx, inv)
|
2020-01-23 22:40:20 -07:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
// If the output is not go1.14 or an empty string, then it could be an error.
|
|
|
|
lines := strings.Split(stdout.String(), "\n")
|
|
|
|
if len(lines) < 2 && stdout.String() != "" {
|
2020-03-10 21:09:39 -06:00
|
|
|
event.Error(ctx, "unexpected stdout when checking for go1.14", errors.Errorf("%q", stdout), tag.Directory.Of(folder))
|
2020-01-23 22:40:20 -07:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return lines[0] == "go1.14", nil
|
|
|
|
}
|