cuelang.org/go@v0.10.1/internal/golangorgx/gopls/cache/view.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package cache is the core of gopls: it is concerned with state
     6  // management, dependency analysis, and invalidation; and it holds the
     7  // machinery of type checking and modular static analysis. Its
     8  // principal types are [Session], [Folder], [View], [Snapshot],
     9  // [Cache], and [Package].
    10  package cache
    11  
    12  import (
    13  	"bytes"
    14  	"context"
    15  	"encoding/json"
    16  	"errors"
    17  	"fmt"
    18  	"os"
    19  	"os/exec"
    20  	"path"
    21  	"path/filepath"
    22  	"regexp"
    23  	"sort"
    24  	"strings"
    25  	"sync"
    26  
    27  	"cuelang.org/go/internal/golangorgx/gopls/cache/metadata"
    28  	"cuelang.org/go/internal/golangorgx/gopls/file"
    29  	"cuelang.org/go/internal/golangorgx/gopls/protocol"
    30  	"cuelang.org/go/internal/golangorgx/gopls/settings"
    31  	"cuelang.org/go/internal/golangorgx/gopls/util/maps"
    32  	"cuelang.org/go/internal/golangorgx/gopls/util/pathutil"
    33  	"cuelang.org/go/internal/golangorgx/gopls/util/slices"
    34  	"cuelang.org/go/internal/golangorgx/tools/event"
    35  	"cuelang.org/go/internal/golangorgx/tools/gocommand"
    36  	"cuelang.org/go/internal/golangorgx/tools/imports"
    37  	"cuelang.org/go/internal/golangorgx/tools/xcontext"
    38  )
    39  
    40  // A Folder represents an LSP workspace folder, together with its per-folder
    41  // options and environment variables that affect build configuration.
    42  //
    43  // Folders (Name and Dir) are specified by the 'initialize' and subsequent
    44  // 'didChangeWorkspaceFolders' requests; their options come from
    45  // didChangeConfiguration.
    46  //
    47  // Folders must not be mutated, as they may be shared across multiple views.
    48  type Folder struct {
    49  	Dir     protocol.DocumentURI
    50  	Name    string // decorative name for UI; not necessarily unique
    51  	Options *settings.Options
    52  	Env     *GoEnv
    53  }
    54  
    55  // GoEnv holds the environment variables and data from the Go command that is
    56  // required for operating on a workspace folder.
    57  type GoEnv struct {
    58  	// Go environment variables. These correspond directly with the Go env var of
    59  	// the same name.
    60  	GOOS        string
    61  	GOARCH      string
    62  	GOCACHE     string
    63  	GOMODCACHE  string
    64  	GOPATH      string
    65  	GOPRIVATE   string
    66  	GOFLAGS     string
    67  	GO111MODULE string
    68  
    69  	// Go version output.
    70  	GoVersion       int    // The X in Go 1.X
    71  	GoVersionOutput string // complete go version output
    72  
    73  	// OS environment variables (notably not go env).
    74  	GOWORK           string
    75  	GOPACKAGESDRIVER string
    76  }
    77  
    78  // View represents a single build for a workspace.
    79  //
    80  // A View is a logical build (the viewDefinition) along with a state of that
    81  // build (the Snapshot).
    82  type View struct {
    83  	id string // a unique string to identify this View in (e.g.) serialized Commands
    84  
    85  	*viewDefinition // build configuration
    86  
    87  	gocmdRunner *gocommand.Runner // limits go command concurrency
    88  
    89  	// baseCtx is the context handed to NewView. This is the parent of all
    90  	// background contexts created for this view.
    91  	baseCtx context.Context
    92  
    93  	importsState *importsState
    94  
    95  	// parseCache holds an LRU cache of recently parsed files.
    96  	parseCache *parseCache
    97  
    98  	// fs is the file source used to populate this view.
    99  	fs *overlayFS
   100  
   101  	// ignoreFilter is used for fast checking of ignored files.
   102  	ignoreFilter *ignoreFilter
   103  
   104  	// cancelInitialWorkspaceLoad can be used to terminate the view's first
   105  	// attempt at initialization.
   106  	cancelInitialWorkspaceLoad context.CancelFunc
   107  
   108  	snapshotMu sync.Mutex
   109  	snapshot   *Snapshot // latest snapshot; nil after shutdown has been called
   110  
   111  	// initialWorkspaceLoad is closed when the first workspace initialization has
   112  	// completed. If we failed to load, we only retry if the go.mod file changes,
   113  	// to avoid too many go/packages calls.
   114  	initialWorkspaceLoad chan struct{}
   115  
   116  	// initializationSema is used limit concurrent initialization of snapshots in
   117  	// the view. We use a channel instead of a mutex to avoid blocking when a
   118  	// context is canceled.
   119  	//
   120  	// This field (along with snapshot.initialized) guards against duplicate
   121  	// initialization of snapshots. Do not change it without adjusting snapshot
   122  	// accordingly.
   123  	initializationSema chan struct{}
   124  
   125  	// Document filters are constructed once, in View.filterFunc.
   126  	filterFuncOnce sync.Once
   127  	_filterFunc    func(protocol.DocumentURI) bool // only accessed by View.filterFunc
   128  }
   129  
   130  // definition implements the viewDefiner interface.
   131  func (v *View) definition() *viewDefinition { return v.viewDefinition }
   132  
   133  // A viewDefinition is a logical build, i.e. configuration (Folder) along with
   134  // a build directory and possibly an environment overlay (e.g. GOWORK=off or
   135  // GOOS, GOARCH=...) to affect the build.
   136  //
   137  // This type is immutable, and compared to see if the View needs to be
   138  // reconstructed.
   139  //
   140  // Note: whenever modifying this type, also modify the equivalence relation
   141  // implemented by viewDefinitionsEqual.
   142  //
   143  // TODO(golang/go#57979): viewDefinition should be sufficient for running
   144  // go/packages. Enforce this in the API.
   145  type viewDefinition struct {
   146  	folder *Folder // pointer comparison is OK, as any new Folder creates a new def
   147  
   148  	typ    ViewType
   149  	root   protocol.DocumentURI // root directory; where to run the Go command
   150  	gomod  protocol.DocumentURI // the nearest go.mod file, or ""
   151  	gowork protocol.DocumentURI // the nearest go.work file, or ""
   152  
   153  	// workspaceModFiles holds the set of mod files active in this snapshot.
   154  	//
   155  	// For a go.work workspace, this is the set of workspace modfiles. For a
   156  	// go.mod workspace, this contains the go.mod file defining the workspace
   157  	// root, as well as any locally replaced modules (if
   158  	// "includeReplaceInWorkspace" is set).
   159  	//
   160  	// TODO(rfindley): should we just run `go list -m` to compute this set?
   161  	workspaceModFiles    map[protocol.DocumentURI]struct{}
   162  	workspaceModFilesErr error // error encountered computing workspaceModFiles
   163  
   164  	// envOverlay holds additional environment to apply to this viewDefinition.
   165  	envOverlay map[string]string
   166  }
   167  
   168  // definition implements the viewDefiner interface.
   169  func (d *viewDefinition) definition() *viewDefinition { return d }
   170  
   171  // Type returns the ViewType type, which determines how go/packages are loaded
   172  // for this View.
   173  func (d *viewDefinition) Type() ViewType { return d.typ }
   174  
   175  // Root returns the view root, which determines where packages are loaded from.
   176  func (d *viewDefinition) Root() protocol.DocumentURI { return d.root }
   177  
   178  // GoMod returns the nearest go.mod file for this view's root, or "".
   179  func (d *viewDefinition) GoMod() protocol.DocumentURI { return d.gomod }
   180  
   181  // GoWork returns the nearest go.work file for this view's root, or "".
   182  func (d *viewDefinition) GoWork() protocol.DocumentURI { return d.gowork }
   183  
   184  // EnvOverlay returns a new sorted slice of environment variables (in the form
   185  // "k=v") for this view definition's env overlay.
   186  func (d *viewDefinition) EnvOverlay() []string {
   187  	var env []string
   188  	for k, v := range d.envOverlay {
   189  		env = append(env, fmt.Sprintf("%s=%s", k, v))
   190  	}
   191  	sort.Strings(env)
   192  	return env
   193  }
   194  
   195  // GOOS returns the effective GOOS value for this view definition, accounting
   196  // for its env overlay.
   197  func (d *viewDefinition) GOOS() string {
   198  	if goos, ok := d.envOverlay["GOOS"]; ok {
   199  		return goos
   200  	}
   201  	return d.folder.Env.GOOS
   202  }
   203  
   204  // GOOS returns the effective GOARCH value for this view definition, accounting
   205  // for its env overlay.
   206  func (d *viewDefinition) GOARCH() string {
   207  	if goarch, ok := d.envOverlay["GOARCH"]; ok {
   208  		return goarch
   209  	}
   210  	return d.folder.Env.GOARCH
   211  }
   212  
   213  // adjustedGO111MODULE is the value of GO111MODULE to use for loading packages.
   214  // It is adjusted to default to "auto" rather than "on", since if we are in
   215  // GOPATH and have no module, we may as well allow a GOPATH view to work.
   216  func (d viewDefinition) adjustedGO111MODULE() string {
   217  	if d.folder.Env.GO111MODULE != "" {
   218  		return d.folder.Env.GO111MODULE
   219  	}
   220  	return "auto"
   221  }
   222  
   223  // ModFiles are the go.mod files enclosed in the snapshot's view and known
   224  // to the snapshot.
   225  func (d viewDefinition) ModFiles() []protocol.DocumentURI {
   226  	var uris []protocol.DocumentURI
   227  	for modURI := range d.workspaceModFiles {
   228  		uris = append(uris, modURI)
   229  	}
   230  	return uris
   231  }
   232  
   233  // viewDefinitionsEqual reports whether x and y are equivalent.
   234  func viewDefinitionsEqual(x, y *viewDefinition) bool {
   235  	if (x.workspaceModFilesErr == nil) != (y.workspaceModFilesErr == nil) {
   236  		return false
   237  	}
   238  	if x.workspaceModFilesErr != nil {
   239  		if x.workspaceModFilesErr.Error() != y.workspaceModFilesErr.Error() {
   240  			return false
   241  		}
   242  	} else if !maps.SameKeys(x.workspaceModFiles, y.workspaceModFiles) {
   243  		return false
   244  	}
   245  	if len(x.envOverlay) != len(y.envOverlay) {
   246  		return false
   247  	}
   248  	for i, xv := range x.envOverlay {
   249  		if xv != y.envOverlay[i] {
   250  			return false
   251  		}
   252  	}
   253  	return x.folder == y.folder &&
   254  		x.typ == y.typ &&
   255  		x.root == y.root &&
   256  		x.gomod == y.gomod &&
   257  		x.gowork == y.gowork
   258  }
   259  
   260  // A ViewType describes how we load package information for a view.
   261  //
   262  // This is used for constructing the go/packages.Load query, and for
   263  // interpreting missing packages, imports, or errors.
   264  //
   265  // See the documentation for individual ViewType values for details.
   266  type ViewType int
   267  
   268  const (
   269  	// GoPackagesDriverView is a view with a non-empty GOPACKAGESDRIVER
   270  	// environment variable.
   271  	//
   272  	// Load: ./... from the workspace folder.
   273  	GoPackagesDriverView ViewType = iota
   274  
   275  	// GOPATHView is a view in GOPATH mode.
   276  	//
   277  	// I.e. in GOPATH, with GO111MODULE=off, or GO111MODULE=auto with no
   278  	// go.mod file.
   279  	//
   280  	// Load: ./... from the workspace folder.
   281  	GOPATHView
   282  
   283  	// GoModView is a view in module mode with a single Go module.
   284  	//
   285  	// Load: <modulePath>/... from the module root.
   286  	GoModView
   287  
   288  	// GoWorkView is a view in module mode with a go.work file.
   289  	//
   290  	// Load: <modulePath>/... from the workspace folder, for each module.
   291  	GoWorkView
   292  
   293  	// An AdHocView is a collection of files in a given directory, not in GOPATH
   294  	// or a module.
   295  	//
   296  	// Load: . from the workspace folder.
   297  	AdHocView
   298  )
   299  
   300  func (t ViewType) String() string {
   301  	switch t {
   302  	case GoPackagesDriverView:
   303  		return "GoPackagesDriverView"
   304  	case GOPATHView:
   305  		return "GOPATHView"
   306  	case GoModView:
   307  		return "GoModView"
   308  	case GoWorkView:
   309  		return "GoWorkView"
   310  	case AdHocView:
   311  		return "AdHocView"
   312  	default:
   313  		return "Unknown"
   314  	}
   315  }
   316  
   317  // moduleMode reports whether the view uses Go modules.
   318  func (w viewDefinition) moduleMode() bool {
   319  	switch w.typ {
   320  	case GoModView, GoWorkView:
   321  		return true
   322  	default:
   323  		return false
   324  	}
   325  }
   326  
   327  func (v *View) ID() string { return v.id }
   328  
   329  // tempModFile creates a temporary go.mod file based on the contents
   330  // of the given go.mod file. On success, it is the caller's
   331  // responsibility to call the cleanup function when the file is no
   332  // longer needed.
   333  func tempModFile(modURI protocol.DocumentURI, gomod, gosum []byte) (tmpURI protocol.DocumentURI, cleanup func(), err error) {
   334  	filenameHash := file.HashOf([]byte(modURI.Path()))
   335  	tmpMod, err := os.CreateTemp("", fmt.Sprintf("go.%s.*.mod", filenameHash))
   336  	if err != nil {
   337  		return "", nil, err
   338  	}
   339  	defer tmpMod.Close()
   340  
   341  	tmpURI = protocol.URIFromPath(tmpMod.Name())
   342  	tmpSumName := sumFilename(tmpURI)
   343  
   344  	if _, err := tmpMod.Write(gomod); err != nil {
   345  		return "", nil, err
   346  	}
   347  
   348  	// We use a distinct name here to avoid subtlety around the fact
   349  	// that both 'return' and 'defer' update the "cleanup" variable.
   350  	doCleanup := func() {
   351  		_ = os.Remove(tmpSumName)
   352  		_ = os.Remove(tmpURI.Path())
   353  	}
   354  
   355  	// Be careful to clean up if we return an error from this function.
   356  	defer func() {
   357  		if err != nil {
   358  			doCleanup()
   359  			cleanup = nil
   360  		}
   361  	}()
   362  
   363  	// Create an analogous go.sum, if one exists.
   364  	if gosum != nil {
   365  		if err := os.WriteFile(tmpSumName, gosum, 0655); err != nil {
   366  			return "", nil, err
   367  		}
   368  	}
   369  
   370  	return tmpURI, doCleanup, nil
   371  }
   372  
   373  // Folder returns the folder at the base of this view.
   374  func (v *View) Folder() *Folder {
   375  	return v.folder
   376  }
   377  
   378  // UpdateFolders updates the set of views for the new folders.
   379  //
   380  // Calling this causes each view to be reinitialized.
   381  func (s *Session) UpdateFolders(ctx context.Context, newFolders []*Folder) error {
   382  	s.viewMu.Lock()
   383  	defer s.viewMu.Unlock()
   384  
   385  	overlays := s.Overlays()
   386  	var openFiles []protocol.DocumentURI
   387  	for _, o := range overlays {
   388  		openFiles = append(openFiles, o.URI())
   389  	}
   390  
   391  	defs, err := selectViewDefs(ctx, s, newFolders, openFiles)
   392  	if err != nil {
   393  		return err
   394  	}
   395  	var newViews []*View
   396  	for _, def := range defs {
   397  		v, _, release := s.createView(ctx, def)
   398  		release()
   399  		newViews = append(newViews, v)
   400  	}
   401  	for _, v := range s.views {
   402  		v.shutdown()
   403  	}
   404  	s.views = newViews
   405  	return nil
   406  }
   407  
   408  // viewEnv returns a string describing the environment of a newly created view.
   409  //
   410  // It must not be called concurrently with any other view methods.
   411  // TODO(rfindley): rethink this function, or inline sole call.
   412  func viewEnv(v *View) string {
   413  	var buf bytes.Buffer
   414  	fmt.Fprintf(&buf, `go info for %v
   415  (view type %v)
   416  (root dir %s)
   417  (go version %s)
   418  (build flags: %v)
   419  (go env: %+v)
   420  (env overlay: %v)
   421  `,
   422  		v.folder.Dir.Path(),
   423  		v.typ,
   424  		v.root.Path(),
   425  		strings.TrimRight(v.folder.Env.GoVersionOutput, "\n"),
   426  		v.folder.Options.BuildFlags,
   427  		*v.folder.Env,
   428  		v.envOverlay,
   429  	)
   430  
   431  	return buf.String()
   432  }
   433  
   434  // RunProcessEnvFunc runs fn with the process env for this snapshot's view.
   435  // Note: the process env contains cached module and filesystem state.
   436  func (s *Snapshot) RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error {
   437  	return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
   438  }
   439  
   440  // separated out from its sole use in locateTemplateFiles for testability
   441  func fileHasExtension(path string, suffixes []string) bool {
   442  	ext := filepath.Ext(path)
   443  	if ext != "" && ext[0] == '.' {
   444  		ext = ext[1:]
   445  	}
   446  	for _, s := range suffixes {
   447  		if s != "" && ext == s {
   448  			return true
   449  		}
   450  	}
   451  	return false
   452  }
   453  
   454  // locateTemplateFiles ensures that the snapshot has mapped template files
   455  // within the workspace folder.
   456  func (s *Snapshot) locateTemplateFiles(ctx context.Context) {
   457  	suffixes := s.Options().TemplateExtensions
   458  	if len(suffixes) == 0 {
   459  		return
   460  	}
   461  
   462  	searched := 0
   463  	filterFunc := s.view.filterFunc()
   464  	err := filepath.WalkDir(s.view.folder.Dir.Path(), func(path string, entry os.DirEntry, err error) error {
   465  		if err != nil {
   466  			return err
   467  		}
   468  		if entry.IsDir() {
   469  			return nil
   470  		}
   471  		if fileLimit > 0 && searched > fileLimit {
   472  			return errExhausted
   473  		}
   474  		searched++
   475  		if !fileHasExtension(path, suffixes) {
   476  			return nil
   477  		}
   478  		uri := protocol.URIFromPath(path)
   479  		if filterFunc(uri) {
   480  			return nil
   481  		}
   482  		// Get the file in order to include it in the snapshot.
   483  		// TODO(golang/go#57558): it is fundamentally broken to track files in this
   484  		// way; we may lose them if configuration or layout changes cause a view to
   485  		// be recreated.
   486  		//
   487  		// Furthermore, this operation must ignore errors, including context
   488  		// cancellation, or risk leaving the snapshot in an undefined state.
   489  		s.ReadFile(ctx, uri)
   490  		return nil
   491  	})
   492  	if err != nil {
   493  		event.Error(ctx, "searching for template files failed", err)
   494  	}
   495  }
   496  
   497  // filterFunc returns a func that reports whether uri is filtered by the currently configured
   498  // directoryFilters.
   499  func (v *View) filterFunc() func(protocol.DocumentURI) bool {
   500  	v.filterFuncOnce.Do(func() {
   501  		folderDir := v.folder.Dir.Path()
   502  		gomodcache := v.folder.Env.GOMODCACHE
   503  		var filters []string
   504  		filters = append(filters, v.folder.Options.DirectoryFilters...)
   505  		if pref := strings.TrimPrefix(gomodcache, folderDir); pref != gomodcache {
   506  			modcacheFilter := "-" + strings.TrimPrefix(filepath.ToSlash(pref), "/")
   507  			filters = append(filters, modcacheFilter)
   508  		}
   509  		filterer := NewFilterer(filters)
   510  		v._filterFunc = func(uri protocol.DocumentURI) bool {
   511  			// Only filter relative to the configured root directory.
   512  			if pathutil.InDir(folderDir, uri.Path()) {
   513  				return relPathExcludedByFilter(strings.TrimPrefix(uri.Path(), folderDir), filterer)
   514  			}
   515  			return false
   516  		}
   517  	})
   518  	return v._filterFunc
   519  }
   520  
   521  // shutdown releases resources associated with the view.
   522  func (v *View) shutdown() {
   523  	// Cancel the initial workspace load if it is still running.
   524  	v.cancelInitialWorkspaceLoad()
   525  
   526  	v.snapshotMu.Lock()
   527  	if v.snapshot != nil {
   528  		v.snapshot.cancel()
   529  		v.snapshot.decref()
   530  		v.snapshot = nil
   531  	}
   532  	v.snapshotMu.Unlock()
   533  }
   534  
   535  // IgnoredFile reports if a file would be ignored by a `go list` of the whole
   536  // workspace.
   537  //
   538  // While go list ./... skips directories starting with '.', '_', or 'testdata',
   539  // gopls may still load them via file queries. Explicitly filter them out.
   540  func (s *Snapshot) IgnoredFile(uri protocol.DocumentURI) bool {
   541  	// Fast path: if uri doesn't contain '.', '_', or 'testdata', it is not
   542  	// possible that it is ignored.
   543  	{
   544  		uriStr := string(uri)
   545  		if !strings.Contains(uriStr, ".") && !strings.Contains(uriStr, "_") && !strings.Contains(uriStr, "testdata") {
   546  			return false
   547  		}
   548  	}
   549  
   550  	return s.view.ignoreFilter.ignored(uri.Path())
   551  }
   552  
   553  // An ignoreFilter implements go list's exclusion rules via its 'ignored' method.
   554  type ignoreFilter struct {
   555  	prefixes []string // root dirs, ending in filepath.Separator
   556  }
   557  
   558  // newIgnoreFilter returns a new ignoreFilter implementing exclusion rules
   559  // relative to the provided directories.
   560  func newIgnoreFilter(dirs []string) *ignoreFilter {
   561  	f := new(ignoreFilter)
   562  	for _, d := range dirs {
   563  		f.prefixes = append(f.prefixes, filepath.Clean(d)+string(filepath.Separator))
   564  	}
   565  	return f
   566  }
   567  
   568  func (f *ignoreFilter) ignored(filename string) bool {
   569  	for _, prefix := range f.prefixes {
   570  		if suffix := strings.TrimPrefix(filename, prefix); suffix != filename {
   571  			if checkIgnored(suffix) {
   572  				return true
   573  			}
   574  		}
   575  	}
   576  	return false
   577  }
   578  
   579  // checkIgnored implements go list's exclusion rules.
   580  // Quoting “go help list”:
   581  //
   582  //	Directory and file names that begin with "." or "_" are ignored
   583  //	by the go tool, as are directories named "testdata".
   584  func checkIgnored(suffix string) bool {
   585  	// Note: this could be further optimized by writing a HasSegment helper, a
   586  	// segment-boundary respecting variant of strings.Contains.
   587  	for _, component := range strings.Split(suffix, string(filepath.Separator)) {
   588  		if len(component) == 0 {
   589  			continue
   590  		}
   591  		if component[0] == '.' || component[0] == '_' || component == "testdata" {
   592  			return true
   593  		}
   594  	}
   595  	return false
   596  }
   597  
   598  // Snapshot returns the current snapshot for the view, and a
   599  // release function that must be called when the Snapshot is
   600  // no longer needed.
   601  //
   602  // The resulting error is non-nil if and only if the view is shut down, in
   603  // which case the resulting release function will also be nil.
   604  func (v *View) Snapshot() (*Snapshot, func(), error) {
   605  	v.snapshotMu.Lock()
   606  	defer v.snapshotMu.Unlock()
   607  	if v.snapshot == nil {
   608  		return nil, nil, errors.New("view is shutdown")
   609  	}
   610  	return v.snapshot, v.snapshot.Acquire(), nil
   611  }
   612  
   613  // initialize loads the metadata (and currently, file contents, due to
   614  // golang/go#57558) for the main package query of the View, which depends on
   615  // the view type (see ViewType). If s.initialized is already true, initialize
   616  // is a no op.
   617  //
   618  // The first attempt--which populates the first snapshot for a new view--must
   619  // be allowed to run to completion without being cancelled.
   620  //
   621  // Subsequent attempts are triggered by conditions where gopls can't enumerate
   622  // specific packages that require reloading, such as a change to a go.mod file.
   623  // These attempts may be cancelled, and then retried by a later call.
   624  //
   625  // Postcondition: if ctx was not cancelled, s.initialized is true, s.initialErr
   626  // holds the error resulting from initialization, if any, and s.metadata holds
   627  // the resulting metadata graph.
   628  func (s *Snapshot) initialize(ctx context.Context, firstAttempt bool) {
   629  	// Acquire initializationSema, which is
   630  	// (in effect) a mutex with a timeout.
   631  	select {
   632  	case <-ctx.Done():
   633  		return
   634  	case s.view.initializationSema <- struct{}{}:
   635  	}
   636  
   637  	defer func() {
   638  		<-s.view.initializationSema
   639  	}()
   640  
   641  	s.mu.Lock()
   642  	initialized := s.initialized
   643  	s.mu.Unlock()
   644  
   645  	if initialized {
   646  		return
   647  	}
   648  
   649  	defer func() {
   650  		if firstAttempt {
   651  			close(s.view.initialWorkspaceLoad)
   652  		}
   653  	}()
   654  
   655  	// TODO(rFindley): we should only locate template files on the first attempt,
   656  	// or guard it via a different mechanism.
   657  	s.locateTemplateFiles(ctx)
   658  
   659  	// Collect module paths to load by parsing go.mod files. If a module fails to
   660  	// parse, capture the parsing failure as a critical diagnostic.
   661  	var scopes []loadScope           // scopes to load
   662  	var modDiagnostics []*Diagnostic // diagnostics for broken go.mod files
   663  	addError := func(uri protocol.DocumentURI, err error) {
   664  		modDiagnostics = append(modDiagnostics, &Diagnostic{
   665  			URI:      uri,
   666  			Severity: protocol.SeverityError,
   667  			Source:   ListError,
   668  			Message:  err.Error(),
   669  		})
   670  	}
   671  
   672  	if len(s.view.workspaceModFiles) > 0 {
   673  		for modURI := range s.view.workspaceModFiles {
   674  			// Verify that the modfile is valid before trying to load it.
   675  			//
   676  			// TODO(rfindley): now that we no longer need to parse the modfile in
   677  			// order to load scope, we could move these diagnostics to a more general
   678  			// location where we diagnose problems with modfiles or the workspace.
   679  			//
   680  			// Be careful not to add context cancellation errors as critical module
   681  			// errors.
   682  			fh, err := s.ReadFile(ctx, modURI)
   683  			if err != nil {
   684  				if ctx.Err() != nil {
   685  					return
   686  				}
   687  				addError(modURI, err)
   688  				continue
   689  			}
   690  			parsed, err := s.ParseMod(ctx, fh)
   691  			if err != nil {
   692  				if ctx.Err() != nil {
   693  					return
   694  				}
   695  				addError(modURI, err)
   696  				continue
   697  			}
   698  			if parsed.File == nil || parsed.File.Module == nil {
   699  				addError(modURI, fmt.Errorf("no module path for %s", modURI))
   700  				continue
   701  			}
   702  			moduleDir := filepath.Dir(modURI.Path())
   703  			// Previously, we loaded <modulepath>/... for each module path, but that
   704  			// is actually incorrect when the pattern may match packages in more than
   705  			// one module. See golang/go#59458 for more details.
   706  			scopes = append(scopes, moduleLoadScope{dir: moduleDir, modulePath: parsed.File.Module.Mod.Path})
   707  		}
   708  	} else {
   709  		scopes = append(scopes, viewLoadScope{})
   710  	}
   711  
   712  	// If we're loading anything, ensure we also load builtin,
   713  	// since it provides fake definitions (and documentation)
   714  	// for types like int that are used everywhere.
   715  	if len(scopes) > 0 {
   716  		scopes = append(scopes, packageLoadScope("builtin"))
   717  	}
   718  	loadErr := s.load(ctx, true, scopes...)
   719  
   720  	// A failure is retryable if it may have been due to context cancellation,
   721  	// and this is not the initial workspace load (firstAttempt==true).
   722  	//
   723  	// The IWL runs on a detached context with a long (~10m) timeout, so
   724  	// if the context was canceled we consider loading to have failed
   725  	// permanently.
   726  	if loadErr != nil && ctx.Err() != nil && !firstAttempt {
   727  		return
   728  	}
   729  
   730  	var initialErr *InitializationError
   731  	switch {
   732  	case loadErr != nil && ctx.Err() != nil:
   733  		event.Error(ctx, fmt.Sprintf("initial workspace load: %v", loadErr), loadErr)
   734  		initialErr = &InitializationError{
   735  			MainError: loadErr,
   736  		}
   737  	case loadErr != nil:
   738  		event.Error(ctx, "initial workspace load failed", loadErr)
   739  		extractedDiags := s.extractGoCommandErrors(ctx, loadErr)
   740  		initialErr = &InitializationError{
   741  			MainError:   loadErr,
   742  			Diagnostics: maps.Group(extractedDiags, byURI),
   743  		}
   744  	case s.view.workspaceModFilesErr != nil:
   745  		initialErr = &InitializationError{
   746  			MainError: s.view.workspaceModFilesErr,
   747  		}
   748  	case len(modDiagnostics) > 0:
   749  		initialErr = &InitializationError{
   750  			MainError: fmt.Errorf(modDiagnostics[0].Message),
   751  		}
   752  	}
   753  
   754  	s.mu.Lock()
   755  	defer s.mu.Unlock()
   756  
   757  	s.initialized = true
   758  	s.initialErr = initialErr
   759  }
   760  
   761  // A StateChange describes external state changes that may affect a snapshot.
   762  //
   763  // By far the most common of these is a change to file state, but a query of
   764  // module upgrade information or vulnerabilities also affects gopls' behavior.
   765  type StateChange struct {
   766  	Modifications  []file.Modification // if set, the raw modifications originating this change
   767  	Files          map[protocol.DocumentURI]file.Handle
   768  	ModuleUpgrades map[protocol.DocumentURI]map[string]string
   769  	GCDetails      map[metadata.PackageID]bool // package -> whether or not we want details
   770  }
   771  
   772  // InvalidateView processes the provided state change, invalidating any derived
   773  // results that depend on the changed state.
   774  //
   775  // The resulting snapshot is non-nil, representing the outcome of the state
   776  // change. The second result is a function that must be called to release the
   777  // snapshot when the snapshot is no longer needed.
   778  //
   779  // An error is returned if the given view is no longer active in the session.
   780  func (s *Session) InvalidateView(ctx context.Context, view *View, changed StateChange) (*Snapshot, func(), error) {
   781  	s.viewMu.Lock()
   782  	defer s.viewMu.Unlock()
   783  
   784  	if !slices.Contains(s.views, view) {
   785  		return nil, nil, fmt.Errorf("view is no longer active")
   786  	}
   787  	snapshot, release, _ := s.invalidateViewLocked(ctx, view, changed)
   788  	return snapshot, release, nil
   789  }
   790  
   791  // invalidateViewLocked invalidates the content of the given view.
   792  // (See [Session.InvalidateView]).
   793  //
   794  // The resulting bool reports whether the View needs to be re-diagnosed.
   795  // (See [Snapshot.clone]).
   796  //
   797  // s.viewMu must be held while calling this method.
   798  func (s *Session) invalidateViewLocked(ctx context.Context, v *View, changed StateChange) (*Snapshot, func(), bool) {
   799  	// Detach the context so that content invalidation cannot be canceled.
   800  	ctx = xcontext.Detach(ctx)
   801  
   802  	// This should be the only time we hold the view's snapshot lock for any period of time.
   803  	v.snapshotMu.Lock()
   804  	defer v.snapshotMu.Unlock()
   805  
   806  	prevSnapshot := v.snapshot
   807  
   808  	if prevSnapshot == nil {
   809  		panic("invalidateContent called after shutdown")
   810  	}
   811  
   812  	// Cancel all still-running previous requests, since they would be
   813  	// operating on stale data.
   814  	prevSnapshot.cancel()
   815  
   816  	// Do not clone a snapshot until its view has finished initializing.
   817  	//
   818  	// TODO(rfindley): shouldn't we do this before canceling?
   819  	prevSnapshot.AwaitInitialized(ctx)
   820  
   821  	var needsDiagnosis bool
   822  	s.snapshotWG.Add(1)
   823  	v.snapshot, needsDiagnosis = prevSnapshot.clone(ctx, v.baseCtx, changed, s.snapshotWG.Done)
   824  
   825  	// Remove the initial reference created when prevSnapshot was created.
   826  	prevSnapshot.decref()
   827  
   828  	// Return a second lease to the caller.
   829  	return v.snapshot, v.snapshot.Acquire(), needsDiagnosis
   830  }
   831  
   832  // defineView computes the view definition for the provided workspace folder
   833  // and URI.
   834  //
   835  // If forURI is non-empty, this view should be the best view including forURI.
   836  // Otherwise, it is the default view for the folder.
   837  //
   838  // defineView only returns an error in the event of context cancellation.
   839  //
   840  // Note: keep this function in sync with bestView.
   841  //
   842  // TODO(rfindley): we should be able to remove the error return, as
   843  // findModules is going away, and all other I/O is memoized.
   844  //
   845  // TODO(rfindley): pass in a narrower interface for the file.Source
   846  // (e.g. fileExists func(DocumentURI) bool) to make clear that this
   847  // process depends only on directory information, not file contents.
   848  func defineView(ctx context.Context, fs file.Source, folder *Folder, forFile file.Handle) (*viewDefinition, error) {
   849  	if err := checkPathValid(folder.Dir.Path()); err != nil {
   850  		return nil, fmt.Errorf("invalid workspace folder path: %w; check that the spelling of the configured workspace folder path agrees with the spelling reported by the operating system", err)
   851  	}
   852  	dir := folder.Dir.Path()
   853  	if forFile != nil {
   854  		dir = filepath.Dir(forFile.URI().Path())
   855  	}
   856  
   857  	def := new(viewDefinition)
   858  	def.folder = folder
   859  
   860  	if forFile != nil && fileKind(forFile) == file.Go {
   861  		// If the file has GOOS/GOARCH build constraints that
   862  		// don't match the folder's environment (which comes from
   863  		// 'go env' in the folder, plus user options),
   864  		// add those constraints to the viewDefinition's environment.
   865  
   866  		// Content trimming is nontrivial, so do this outside of the loop below.
   867  		// Keep this in sync with bestView.
   868  		path := forFile.URI().Path()
   869  		if content, err := forFile.Content(); err == nil {
   870  			// Note the err == nil condition above: by convention a non-existent file
   871  			// does not have any constraints. See the related note in bestView: this
   872  			// choice of behavior shouldn't actually matter. In this case, we should
   873  			// only call defineView with Overlays, which always have content.
   874  			content = trimContentForPortMatch(content)
   875  			viewPort := port{def.folder.Env.GOOS, def.folder.Env.GOARCH}
   876  			if !viewPort.matches(path, content) {
   877  				for _, p := range preferredPorts {
   878  					if p.matches(path, content) {
   879  						if def.envOverlay == nil {
   880  							def.envOverlay = make(map[string]string)
   881  						}
   882  						def.envOverlay["GOOS"] = p.GOOS
   883  						def.envOverlay["GOARCH"] = p.GOARCH
   884  						break
   885  					}
   886  				}
   887  			}
   888  		}
   889  	}
   890  
   891  	var err error
   892  	dirURI := protocol.URIFromPath(dir)
   893  	goworkFromEnv := false
   894  	if folder.Env.GOWORK != "off" && folder.Env.GOWORK != "" {
   895  		goworkFromEnv = true
   896  		def.gowork = protocol.URIFromPath(folder.Env.GOWORK)
   897  	} else {
   898  		def.gowork, err = findRootPattern(ctx, dirURI, "go.work", fs)
   899  		if err != nil {
   900  			return nil, err
   901  		}
   902  	}
   903  
   904  	// When deriving the best view for a given file, we only want to search
   905  	// up the directory hierarchy for modfiles.
   906  	def.gomod, err = findRootPattern(ctx, dirURI, "go.mod", fs)
   907  	if err != nil {
   908  		return nil, err
   909  	}
   910  
   911  	// Determine how we load and where to load package information for this view
   912  	//
   913  	// Specifically, set
   914  	//  - def.typ
   915  	//  - def.root
   916  	//  - def.workspaceModFiles, and
   917  	//  - def.envOverlay.
   918  
   919  	// If GOPACKAGESDRIVER is set it takes precedence.
   920  	{
   921  		// The value of GOPACKAGESDRIVER is not returned through the go command.
   922  		gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
   923  		// A user may also have a gopackagesdriver binary on their machine, which
   924  		// works the same way as setting GOPACKAGESDRIVER.
   925  		//
   926  		// TODO(rfindley): remove this call to LookPath. We should not support this
   927  		// undocumented method of setting GOPACKAGESDRIVER.
   928  		tool, err := exec.LookPath("gopackagesdriver")
   929  		if gopackagesdriver != "off" && (gopackagesdriver != "" || (err == nil && tool != "")) {
   930  			def.typ = GoPackagesDriverView
   931  			def.root = dirURI
   932  			return def, nil
   933  		}
   934  	}
   935  
   936  	// From go.dev/ref/mod, module mode is active if GO111MODULE=on, or
   937  	// GO111MODULE=auto or "" and we are inside a module or have a GOWORK value.
   938  	// But gopls is less strict, allowing GOPATH mode if GO111MODULE="", and
   939  	// AdHoc views if no module is found.
   940  
   941  	// gomodWorkspace is a helper to compute the correct set of workspace
   942  	// modfiles for a go.mod file, based on folder options.
   943  	gomodWorkspace := func() map[protocol.DocumentURI]unit {
   944  		modFiles := map[protocol.DocumentURI]struct{}{def.gomod: {}}
   945  		if folder.Options.IncludeReplaceInWorkspace {
   946  			includingReplace, err := goModModules(ctx, def.gomod, fs)
   947  			if err == nil {
   948  				modFiles = includingReplace
   949  			} else {
   950  				// If the go.mod file fails to parse, we don't know anything about
   951  				// replace directives, so fall back to a view of just the root module.
   952  			}
   953  		}
   954  		return modFiles
   955  	}
   956  
   957  	// Prefer a go.work file if it is available and contains the module relevant
   958  	// to forURI.
   959  	if def.adjustedGO111MODULE() != "off" && folder.Env.GOWORK != "off" && def.gowork != "" {
   960  		def.typ = GoWorkView
   961  		if goworkFromEnv {
   962  			// The go.work file could be anywhere, which can lead to confusing error
   963  			// messages.
   964  			def.root = dirURI
   965  		} else {
   966  			// The go.work file could be anywhere, which can lead to confusing error
   967  			def.root = def.gowork.Dir()
   968  		}
   969  		def.workspaceModFiles, def.workspaceModFilesErr = goWorkModules(ctx, def.gowork, fs)
   970  
   971  		// If forURI is in a module but that module is not
   972  		// included in the go.work file, use a go.mod view with GOWORK=off.
   973  		if forFile != nil && def.workspaceModFilesErr == nil && def.gomod != "" {
   974  			if _, ok := def.workspaceModFiles[def.gomod]; !ok {
   975  				def.typ = GoModView
   976  				def.root = def.gomod.Dir()
   977  				def.workspaceModFiles = gomodWorkspace()
   978  				if def.envOverlay == nil {
   979  					def.envOverlay = make(map[string]string)
   980  				}
   981  				def.envOverlay["GOWORK"] = "off"
   982  			}
   983  		}
   984  		return def, nil
   985  	}
   986  
   987  	// Otherwise, use the active module, if in module mode.
   988  	//
   989  	// Note, we could override GO111MODULE here via envOverlay if we wanted to
   990  	// support the case where someone opens a module with GO111MODULE=off. But
   991  	// that is probably not worth worrying about (at this point, folks probably
   992  	// shouldn't be setting GO111MODULE).
   993  	if def.adjustedGO111MODULE() != "off" && def.gomod != "" {
   994  		def.typ = GoModView
   995  		def.root = def.gomod.Dir()
   996  		def.workspaceModFiles = gomodWorkspace()
   997  		return def, nil
   998  	}
   999  
  1000  	// Check if the workspace is within any GOPATH directory.
  1001  	inGOPATH := false
  1002  	for _, gp := range filepath.SplitList(folder.Env.GOPATH) {
  1003  		if pathutil.InDir(filepath.Join(gp, "src"), dir) {
  1004  			inGOPATH = true
  1005  			break
  1006  		}
  1007  	}
  1008  	if def.adjustedGO111MODULE() != "on" && inGOPATH {
  1009  		def.typ = GOPATHView
  1010  		def.root = dirURI
  1011  		return def, nil
  1012  	}
  1013  
  1014  	// We're not in a workspace, module, or GOPATH, so have no better choice than
  1015  	// an ad-hoc view.
  1016  	def.typ = AdHocView
  1017  	def.root = dirURI
  1018  	return def, nil
  1019  }
  1020  
  1021  // FetchGoEnv queries the environment and Go command to collect environment
  1022  // variables necessary for the workspace folder.
  1023  func FetchGoEnv(ctx context.Context, folder protocol.DocumentURI, opts *settings.Options) (*GoEnv, error) {
  1024  	dir := folder.Path()
  1025  	// All of the go commands invoked here should be fast. No need to share a
  1026  	// runner with other operations.
  1027  	runner := new(gocommand.Runner)
  1028  	inv := gocommand.Invocation{
  1029  		WorkingDir: dir,
  1030  		Env:        opts.EnvSlice(),
  1031  	}
  1032  
  1033  	var (
  1034  		env = new(GoEnv)
  1035  		err error
  1036  	)
  1037  	envvars := map[string]*string{
  1038  		"GOOS":        &env.GOOS,
  1039  		"GOARCH":      &env.GOARCH,
  1040  		"GOCACHE":     &env.GOCACHE,
  1041  		"GOPATH":      &env.GOPATH,
  1042  		"GOPRIVATE":   &env.GOPRIVATE,
  1043  		"GOMODCACHE":  &env.GOMODCACHE,
  1044  		"GOFLAGS":     &env.GOFLAGS,
  1045  		"GO111MODULE": &env.GO111MODULE,
  1046  	}
  1047  	if err := loadGoEnv(ctx, dir, opts.EnvSlice(), runner, envvars); err != nil {
  1048  		return nil, err
  1049  	}
  1050  
  1051  	env.GoVersion, err = gocommand.GoVersion(ctx, inv, runner)
  1052  	if err != nil {
  1053  		return nil, err
  1054  	}
  1055  	env.GoVersionOutput, err = gocommand.GoVersionOutput(ctx, inv, runner)
  1056  	if err != nil {
  1057  		return nil, err
  1058  	}
  1059  
  1060  	// The value of GOPACKAGESDRIVER is not returned through the go command.
  1061  	if driver, ok := opts.Env["GOPACKAGESDRIVER"]; ok {
  1062  		env.GOPACKAGESDRIVER = driver
  1063  	} else {
  1064  		env.GOPACKAGESDRIVER = os.Getenv("GOPACKAGESDRIVER")
  1065  		// A user may also have a gopackagesdriver binary on their machine, which
  1066  		// works the same way as setting GOPACKAGESDRIVER.
  1067  		//
  1068  		// TODO(rfindley): remove this call to LookPath. We should not support this
  1069  		// undocumented method of setting GOPACKAGESDRIVER.
  1070  		if env.GOPACKAGESDRIVER == "" {
  1071  			tool, err := exec.LookPath("gopackagesdriver")
  1072  			if err == nil && tool != "" {
  1073  				env.GOPACKAGESDRIVER = tool
  1074  			}
  1075  		}
  1076  	}
  1077  
  1078  	// While GOWORK is available through the Go command, we want to differentiate
  1079  	// between an explicit GOWORK value and one which is implicit from the file
  1080  	// system. The former doesn't change unless the environment changes.
  1081  	if gowork, ok := opts.Env["GOWORK"]; ok {
  1082  		env.GOWORK = gowork
  1083  	} else {
  1084  		env.GOWORK = os.Getenv("GOWORK")
  1085  	}
  1086  	return env, nil
  1087  }
  1088  
  1089  // loadGoEnv loads `go env` values into the provided map, keyed by Go variable
  1090  // name.
  1091  func loadGoEnv(ctx context.Context, dir string, configEnv []string, runner *gocommand.Runner, vars map[string]*string) error {
  1092  	// We can save ~200 ms by requesting only the variables we care about.
  1093  	args := []string{"-json"}
  1094  	for k := range vars {
  1095  		args = append(args, k)
  1096  	}
  1097  
  1098  	inv := gocommand.Invocation{
  1099  		Verb:       "env",
  1100  		Args:       args,
  1101  		Env:        configEnv,
  1102  		WorkingDir: dir,
  1103  	}
  1104  	stdout, err := runner.Run(ctx, inv)
  1105  	if err != nil {
  1106  		return err
  1107  	}
  1108  	envMap := make(map[string]string)
  1109  	if err := json.Unmarshal(stdout.Bytes(), &envMap); err != nil {
  1110  		return fmt.Errorf("internal error unmarshaling JSON from 'go env': %w", err)
  1111  	}
  1112  	for key, ptr := range vars {
  1113  		*ptr = envMap[key]
  1114  	}
  1115  
  1116  	return nil
  1117  }
  1118  
  1119  // findRootPattern looks for files with the given basename in dir or any parent
  1120  // directory of dir, using the provided FileSource. It returns the first match,
  1121  // starting from dir and search parents.
  1122  //
  1123  // The resulting string is either the file path of a matching file with the
  1124  // given basename, or "" if none was found.
  1125  //
  1126  // findRootPattern only returns an error in the case of context cancellation.
  1127  func findRootPattern(ctx context.Context, dirURI protocol.DocumentURI, basename string, fs file.Source) (protocol.DocumentURI, error) {
  1128  	dir := dirURI.Path()
  1129  	for dir != "" {
  1130  		target := filepath.Join(dir, basename)
  1131  		uri := protocol.URIFromPath(target)
  1132  		fh, err := fs.ReadFile(ctx, uri)
  1133  		if err != nil {
  1134  			return "", err // context cancelled
  1135  		}
  1136  		if fileExists(fh) {
  1137  			return uri, nil
  1138  		}
  1139  		// Trailing separators must be trimmed, otherwise filepath.Split is a noop.
  1140  		next, _ := filepath.Split(strings.TrimRight(dir, string(filepath.Separator)))
  1141  		if next == dir {
  1142  			break
  1143  		}
  1144  		dir = next
  1145  	}
  1146  	return "", nil
  1147  }
  1148  
  1149  // checkPathValid performs an OS-specific path validity check. The
  1150  // implementation varies for filesystems that are case-insensitive
  1151  // (e.g. macOS, Windows), and for those that disallow certain file
  1152  // names (e.g. path segments ending with a period on Windows, or
  1153  // reserved names such as "com"; see
  1154  // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file).
  1155  var checkPathValid = defaultCheckPathValid
  1156  
  1157  // CheckPathValid checks whether a directory is suitable as a workspace folder.
  1158  func CheckPathValid(dir string) error { return checkPathValid(dir) }
  1159  
  1160  func defaultCheckPathValid(path string) error {
  1161  	return nil
  1162  }
  1163  
  1164  // IsGoPrivatePath reports whether target is a private import path, as identified
  1165  // by the GOPRIVATE environment variable.
  1166  func (s *Snapshot) IsGoPrivatePath(target string) bool {
  1167  	return globsMatchPath(s.view.folder.Env.GOPRIVATE, target)
  1168  }
  1169  
  1170  // ModuleUpgrades returns known module upgrades for the dependencies of
  1171  // modfile.
  1172  func (s *Snapshot) ModuleUpgrades(modfile protocol.DocumentURI) map[string]string {
  1173  	s.mu.Lock()
  1174  	defer s.mu.Unlock()
  1175  	upgrades := map[string]string{}
  1176  	orig, _ := s.moduleUpgrades.Get(modfile)
  1177  	for mod, ver := range orig {
  1178  		upgrades[mod] = ver
  1179  	}
  1180  	return upgrades
  1181  }
  1182  
  1183  // GoVersion returns the effective release Go version (the X in go1.X) for this
  1184  // view.
  1185  func (v *View) GoVersion() int {
  1186  	return v.folder.Env.GoVersion
  1187  }
  1188  
  1189  // GoVersionString returns the effective Go version string for this view.
  1190  //
  1191  // Unlike [GoVersion], this encodes the minor version and commit hash information.
  1192  func (v *View) GoVersionString() string {
  1193  	return gocommand.ParseGoVersionOutput(v.folder.Env.GoVersionOutput)
  1194  }
  1195  
  1196  // GoVersionString is temporarily available from the snapshot.
  1197  //
  1198  // TODO(rfindley): refactor so that this method is not necessary.
  1199  func (s *Snapshot) GoVersionString() string {
  1200  	return s.view.GoVersionString()
  1201  }
  1202  
  1203  // Copied from
  1204  // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
  1205  func globsMatchPath(globs, target string) bool {
  1206  	for globs != "" {
  1207  		// Extract next non-empty glob in comma-separated list.
  1208  		var glob string
  1209  		if i := strings.Index(globs, ","); i >= 0 {
  1210  			glob, globs = globs[:i], globs[i+1:]
  1211  		} else {
  1212  			glob, globs = globs, ""
  1213  		}
  1214  		if glob == "" {
  1215  			continue
  1216  		}
  1217  
  1218  		// A glob with N+1 path elements (N slashes) needs to be matched
  1219  		// against the first N+1 path elements of target,
  1220  		// which end just before the N+1'th slash.
  1221  		n := strings.Count(glob, "/")
  1222  		prefix := target
  1223  		// Walk target, counting slashes, truncating at the N+1'th slash.
  1224  		for i := 0; i < len(target); i++ {
  1225  			if target[i] == '/' {
  1226  				if n == 0 {
  1227  					prefix = target[:i]
  1228  					break
  1229  				}
  1230  				n--
  1231  			}
  1232  		}
  1233  		if n > 0 {
  1234  			// Not enough prefix elements.
  1235  			continue
  1236  		}
  1237  		matched, _ := path.Match(glob, prefix)
  1238  		if matched {
  1239  			return true
  1240  		}
  1241  	}
  1242  	return false
  1243  }
  1244  
  1245  var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
  1246  
  1247  // TODO(rfindley): clean up the redundancy of allFilesExcluded,
  1248  // pathExcludedByFilterFunc, pathExcludedByFilter, view.filterFunc...
  1249  func allFilesExcluded(files []string, filterFunc func(protocol.DocumentURI) bool) bool {
  1250  	for _, f := range files {
  1251  		uri := protocol.URIFromPath(f)
  1252  		if !filterFunc(uri) {
  1253  			return false
  1254  		}
  1255  	}
  1256  	return true
  1257  }
  1258  
  1259  func relPathExcludedByFilter(path string, filterer *Filterer) bool {
  1260  	path = strings.TrimPrefix(filepath.ToSlash(path), "/")
  1261  	return filterer.Disallow(path)
  1262  }