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.
5 // Package cache implements the caching layer for gopls.
23 "golang.org/x/mod/modfile"
24 "golang.org/x/mod/semver"
25 exec "golang.org/x/sys/execabs"
26 "golang.org/x/tools/go/packages"
27 "golang.org/x/tools/internal/event"
28 "golang.org/x/tools/internal/gocommand"
29 "golang.org/x/tools/internal/imports"
30 "golang.org/x/tools/internal/lsp/protocol"
31 "golang.org/x/tools/internal/lsp/source"
32 "golang.org/x/tools/internal/memoize"
33 "golang.org/x/tools/internal/span"
34 "golang.org/x/tools/internal/xcontext"
35 errors "golang.org/x/xerrors"
43 options *source.Options
45 // mu protects most mutable state of the view.
48 // baseCtx is the context handed to NewView. This is the parent of all
49 // background contexts created for this view.
50 baseCtx context.Context
52 // cancel is called when all action being performed by the current view
54 cancel context.CancelFunc
56 // name is the user visible name of this view.
59 // folder is the folder with which this view was constructed.
62 importsState *importsState
64 // moduleUpgrades tracks known upgrades for module paths.
65 moduleUpgrades map[string]string
67 // keep track of files by uri and by basename, a single file may be mapped
68 // to multiple uris, and the same basename may map to multiple files
69 filesByURI map[span.URI]*fileBase
70 filesByBase map[string][]*fileBase
72 // initCancelFirstAttempt can be used to terminate the view's first
73 // attempt at initialization.
74 initCancelFirstAttempt context.CancelFunc
79 // initialWorkspaceLoad is closed when the first workspace initialization has
80 // completed. If we failed to load, we only retry if the go.mod file changes,
81 // to avoid too many go/packages calls.
82 initialWorkspaceLoad chan struct{}
84 // initializationSema is used limit concurrent initialization of snapshots in
85 // the view. We use a channel instead of a mutex to avoid blocking when a
86 // context is canceled.
87 initializationSema chan struct{}
89 // rootURI is the rootURI directory of this view. If we are in GOPATH mode, this
90 // is just the folder. If we are in module mode, this is the module rootURI.
93 // workspaceInformation tracks various details about this view's
94 // environment variables, go version, and use of modules.
97 // tempWorkspace is a temporary directory dedicated to holding the latest
98 // version of the workspace go.mod file. (TODO: also go.sum file)
99 tempWorkspace span.URI
102 type workspaceInformation struct {
103 // The Go version in use: X in Go 1.X.
106 // hasGopackagesDriver is true if the user has a value set for the
107 // GOPACKAGESDRIVER environment variable or a gopackagesdriver binary on
109 hasGopackagesDriver bool
111 // `go env` variables that need to be tracked by gopls.
114 // userGo111Module is the user's value of GO111MODULE.
115 userGo111Module go111module
117 // The value of GO111MODULE we want to run with.
118 effectiveGo111Module string
120 // goEnv is the `go env` output collected when a view is created.
121 // It includes the values of the environment variables above.
122 goEnv map[string]string
128 off = go111module(iota)
133 type environmentVariables struct {
134 gocache, gopath, goroot, goprivate, gomodcache, go111module string
137 type workspaceMode int
140 moduleMode workspaceMode = 1 << iota
142 // tempModfile indicates whether or not the -modfile flag should be used.
145 // usesWorkspaceModule indicates support for the experimental workspace module
150 type builtinPackageHandle struct {
151 handle *memoize.Handle
154 type builtinPackageData struct {
155 parsed *source.BuiltinPackage
159 // fileBase holds the common functionality for all files.
160 // It is intended to be embedded in the file implementations
161 type fileBase struct {
168 func (f *fileBase) URI() span.URI {
172 func (f *fileBase) filename() string {
176 func (f *fileBase) addURI(uri span.URI) int {
177 f.uris = append(f.uris, uri)
181 func (v *View) ID() string { return v.id }
183 // tempModFile creates a temporary go.mod file based on the contents of the
184 // given go.mod file. It is the caller's responsibility to clean up the files
185 // when they are done using them.
186 func tempModFile(modFh source.FileHandle, gosum []byte) (tmpURI span.URI, cleanup func(), err error) {
187 filenameHash := hashContents([]byte(modFh.URI().Filename()))
188 tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash))
194 tmpURI = span.URIFromPath(tmpMod.Name())
195 tmpSumName := sumFilename(tmpURI)
197 content, err := modFh.Read()
202 if _, err := tmpMod.Write(content); err != nil {
207 _ = os.Remove(tmpSumName)
208 _ = os.Remove(tmpURI.Filename())
211 // Be careful to clean up if we return an error from this function.
219 // Create an analogous go.sum, if one exists.
221 if err := ioutil.WriteFile(tmpSumName, gosum, 0655); err != nil {
222 return "", cleanup, err
226 return tmpURI, cleanup, nil
229 // Name returns the user visible name of this view.
230 func (v *View) Name() string {
234 // Folder returns the folder at the base of this view.
235 func (v *View) Folder() span.URI {
239 func (v *View) Options() *source.Options {
241 defer v.optionsMu.Unlock()
245 func minorOptionsChange(a, b *source.Options) bool {
246 // Check if any of the settings that modify our understanding of files have been changed
247 if !reflect.DeepEqual(a.Env, b.Env) {
250 if !reflect.DeepEqual(a.DirectoryFilters, b.DirectoryFilters) {
253 aBuildFlags := make([]string, len(a.BuildFlags))
254 bBuildFlags := make([]string, len(b.BuildFlags))
255 copy(aBuildFlags, a.BuildFlags)
256 copy(bBuildFlags, b.BuildFlags)
257 sort.Strings(aBuildFlags)
258 sort.Strings(bBuildFlags)
259 // the rest of the options are benign
260 return reflect.DeepEqual(aBuildFlags, bBuildFlags)
263 func (v *View) SetOptions(ctx context.Context, options *source.Options) (source.View, error) {
264 // no need to rebuild the view if the options were not materially changed
266 if minorOptionsChange(v.options, options) {
272 newView, err := v.session.updateView(ctx, v, options)
276 func (v *View) Rebuild(ctx context.Context) (source.Snapshot, func(), error) {
277 newView, err := v.session.updateView(ctx, v, v.Options())
279 return nil, func() {}, err
281 snapshot, release := newView.Snapshot(ctx)
282 return snapshot, release, nil
285 func (s *snapshot) WriteEnv(ctx context.Context, w io.Writer) error {
286 s.view.optionsMu.Lock()
287 env := s.view.options.EnvSlice()
288 buildFlags := append([]string{}, s.view.options.BuildFlags...)
289 s.view.optionsMu.Unlock()
291 fullEnv := make(map[string]string)
292 for k, v := range s.view.goEnv {
295 for _, v := range env {
296 s := strings.SplitN(v, "=", 2)
300 if _, ok := fullEnv[s[0]]; ok {
304 goVersion, err := s.view.session.gocmdRunner.Run(ctx, gocommand.Invocation{
307 WorkingDir: s.view.rootURI.Filename(),
312 fmt.Fprintf(w, `go env for %v
315 (valid build configuration = %v)
318 s.view.folder.Filename(),
319 s.view.rootURI.Filename(),
320 strings.TrimRight(goVersion.String(), "\n"),
321 s.ValidBuildConfiguration(),
323 for k, v := range fullEnv {
324 fmt.Fprintf(w, "%s=%s\n", k, v)
329 func (s *snapshot) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
330 return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
333 func (v *View) contains(uri span.URI) bool {
334 inRoot := source.InDir(v.rootURI.Filename(), uri.Filename())
335 inFolder := source.InDir(v.folder.Filename(), uri.Filename())
336 if !inRoot && !inFolder {
339 // Filters are applied relative to the workspace folder.
341 return !pathExcludedByFilter(strings.TrimPrefix(uri.Filename(), v.folder.Filename()), v.Options())
346 func (v *View) mapFile(uri span.URI, f *fileBase) {
347 v.filesByURI[uri] = f
348 if f.addURI(uri) == 1 {
349 basename := basename(f.filename())
350 v.filesByBase[basename] = append(v.filesByBase[basename], f)
354 func basename(filename string) string {
355 return strings.ToLower(filepath.Base(filename))
358 func (v *View) relevantChange(c source.FileModification) bool {
359 // If the file is known to the view, the change is relevant.
360 if v.knownFile(c.URI) {
363 // The gopls.mod may not be "known" because we first access it through the
364 // session. As a result, treat changes to the view's gopls.mod file as
365 // always relevant, even if they are only on-disk changes.
366 // TODO(rstambler): Make sure the gopls.mod is always known to the view.
367 if c.URI == goplsModURI(v.rootURI) {
370 // If the file is not known to the view, and the change is only on-disk,
371 // we should not invalidate the snapshot. This is necessary because Emacs
372 // sends didChangeWatchedFiles events for temp files.
373 if c.OnDisk && (c.Action == source.Change || c.Action == source.Delete) {
376 return v.contains(c.URI)
379 func (v *View) knownFile(uri span.URI) bool {
383 f, err := v.findFile(uri)
384 return f != nil && err == nil
387 // getFile returns a file for the given URI.
388 func (v *View) getFile(uri span.URI) *fileBase {
392 f, _ := v.findFile(uri)
398 fname: uri.Filename(),
404 // findFile checks the cache for any file matching the given uri.
406 // An error is only returned for an irreparable failure, for example, if the
407 // filename in question does not exist.
408 func (v *View) findFile(uri span.URI) (*fileBase, error) {
409 if f := v.filesByURI[uri]; f != nil {
413 // no exact match stored, time to do some real work
414 // check for any files with the same basename
415 fname := uri.Filename()
416 basename := basename(fname)
417 if candidates := v.filesByBase[basename]; candidates != nil {
418 pathStat, err := os.Stat(fname)
419 if os.IsNotExist(err) {
423 return nil, nil // the file may exist, return without an error
425 for _, c := range candidates {
426 if cStat, err := os.Stat(c.filename()); err == nil {
427 if os.SameFile(pathStat, cStat) {
435 // no file with a matching name was found, it wasn't in our cache
439 func (v *View) Shutdown(ctx context.Context) {
440 v.session.removeView(ctx, v)
443 // TODO(rFindley): probably some of this should also be one in View.Shutdown
445 func (v *View) shutdown(ctx context.Context) {
446 // Cancel the initial workspace load if it is still running.
447 v.initCancelFirstAttempt()
456 go v.snapshot.generation.Destroy()
457 v.snapshotMu.Unlock()
458 v.importsState.destroy()
459 if v.tempWorkspace != "" {
460 if err := os.RemoveAll(v.tempWorkspace.Filename()); err != nil {
461 event.Error(ctx, "removing temp workspace", err)
466 func (v *View) Session() *Session {
470 func (s *snapshot) IgnoredFile(uri span.URI) bool {
471 filename := uri.Filename()
472 var prefixes []string
473 if len(s.workspace.getActiveModFiles()) == 0 {
474 for _, entry := range filepath.SplitList(s.view.gopath) {
475 prefixes = append(prefixes, filepath.Join(entry, "src"))
478 prefixes = append(prefixes, s.view.gomodcache)
479 for m := range s.workspace.getActiveModFiles() {
480 prefixes = append(prefixes, dirURI(m).Filename())
483 for _, prefix := range prefixes {
484 if strings.HasPrefix(filename, prefix) {
485 return checkIgnored(filename[len(prefix):])
491 // checkIgnored implements go list's exclusion rules. go help list:
492 // Directory and file names that begin with "." or "_" are ignored
493 // by the go tool, as are directories named "testdata".
494 func checkIgnored(suffix string) bool {
495 for _, component := range strings.Split(suffix, string(filepath.Separator)) {
496 if len(component) == 0 {
499 if component[0] == '.' || component[0] == '_' || component == "testdata" {
506 func (v *View) Snapshot(ctx context.Context) (source.Snapshot, func()) {
507 return v.getSnapshot(ctx)
510 func (v *View) getSnapshot(ctx context.Context) (*snapshot, func()) {
512 defer v.snapshotMu.Unlock()
513 return v.snapshot, v.snapshot.generation.Acquire(ctx)
516 func (s *snapshot) initialize(ctx context.Context, firstAttempt bool) {
520 case s.view.initializationSema <- struct{}{}:
524 <-s.view.initializationSema
527 if s.initializeOnce == nil {
530 s.initializeOnce.Do(func() {
532 s.initializeOnce = nil
534 close(s.view.initialWorkspaceLoad)
538 // If we have multiple modules, we need to load them by paths.
539 var scopes []interface{}
540 var modDiagnostics []*source.Diagnostic
541 addError := func(uri span.URI, err error) {
542 modDiagnostics = append(modDiagnostics, &source.Diagnostic{
544 Severity: protocol.SeverityError,
545 Source: source.ListError,
546 Message: err.Error(),
549 for modURI := range s.workspace.getActiveModFiles() {
550 fh, err := s.GetFile(ctx, modURI)
552 addError(modURI, err)
555 parsed, err := s.ParseMod(ctx, fh)
557 addError(modURI, err)
560 if parsed.File == nil || parsed.File.Module == nil {
561 addError(modURI, fmt.Errorf("no module path for %s", modURI))
564 path := parsed.File.Module.Mod.Path
565 scopes = append(scopes, moduleLoadScope(path))
567 if len(scopes) == 0 {
568 scopes = append(scopes, viewLoadScope("LOAD_VIEW"))
570 err := s.load(ctx, firstAttempt, append(scopes, packagePath("builtin"))...)
571 if ctx.Err() != nil {
575 event.Error(ctx, "initial workspace load failed", err)
576 extractedDiags, _ := s.extractGoCommandErrors(ctx, err.Error())
577 s.initializedErr = &source.CriticalError{
579 DiagList: append(modDiagnostics, extractedDiags...),
581 } else if len(modDiagnostics) != 0 {
582 s.initializedErr = &source.CriticalError{
583 MainError: fmt.Errorf("error loading module names"),
584 DiagList: modDiagnostics,
587 // Clear out the initialization error, in case it had been set
589 s.initializedErr = nil
594 // invalidateContent invalidates the content of a Go file,
595 // including any position and type information that depends on it.
596 func (v *View) invalidateContent(ctx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, func()) {
597 // Detach the context so that content invalidation cannot be canceled.
598 ctx = xcontext.Detach(ctx)
600 // Cancel all still-running previous requests, since they would be
601 // operating on stale data.
604 // Do not clone a snapshot until its view has finished initializing.
605 v.snapshot.AwaitInitialized(ctx)
607 // This should be the only time we hold the view's snapshot lock for any period of time.
609 defer v.snapshotMu.Unlock()
611 oldSnapshot := v.snapshot
613 var workspaceChanged bool
614 v.snapshot, workspaceChanged = oldSnapshot.clone(ctx, v.baseCtx, changes, forceReloadMetadata)
615 if workspaceChanged && v.tempWorkspace != "" {
618 wsdir, err := snap.getWorkspaceDir(ctx)
620 event.Error(ctx, "getting workspace dir", err)
622 if err := copyWorkspace(v.tempWorkspace, wsdir); err != nil {
623 event.Error(ctx, "copying workspace dir", err)
627 go oldSnapshot.generation.Destroy()
629 return v.snapshot, v.snapshot.generation.Acquire(ctx)
632 func copyWorkspace(dst span.URI, src span.URI) error {
633 for _, name := range []string{"go.mod", "go.sum"} {
634 srcname := filepath.Join(src.Filename(), name)
635 srcf, err := os.Open(srcname)
637 return errors.Errorf("opening snapshot %s: %w", name, err)
640 dstname := filepath.Join(dst.Filename(), name)
641 dstf, err := os.Create(dstname)
643 return errors.Errorf("truncating view %s: %w", name, err)
646 if _, err := io.Copy(dstf, srcf); err != nil {
647 return errors.Errorf("copying %s: %w", name, err)
653 func (s *Session) getWorkspaceInformation(ctx context.Context, folder span.URI, options *source.Options) (*workspaceInformation, error) {
654 if err := checkPathCase(folder.Filename()); err != nil {
655 return nil, errors.Errorf("invalid workspace configuration: %w", err)
658 inv := gocommand.Invocation{
659 WorkingDir: folder.Filename(),
660 Env: options.EnvSlice(),
662 goversion, err := gocommand.GoVersion(ctx, inv, s.gocmdRunner)
667 go111module := os.Getenv("GO111MODULE")
668 if v, ok := options.Env["GO111MODULE"]; ok {
671 // Make sure to get the `go env` before continuing with initialization.
672 envVars, env, err := s.getGoEnv(ctx, folder.Filename(), goversion, go111module, options.EnvSlice())
676 // If using 1.16, change the default back to auto. The primary effect of
677 // GO111MODULE=on is to break GOPATH, which we aren't too interested in.
678 if goversion >= 16 && go111module == "" {
681 // The value of GOPACKAGESDRIVER is not returned through the go command.
682 gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
683 for _, s := range env {
684 split := strings.SplitN(s, "=", 2)
685 if split[0] == "GOPACKAGESDRIVER" {
686 gopackagesdriver = split[1]
690 // A user may also have a gopackagesdriver binary on their machine, which
691 // works the same way as setting GOPACKAGESDRIVER.
692 tool, _ := exec.LookPath("gopackagesdriver")
693 hasGopackagesDriver := gopackagesdriver != "off" && (gopackagesdriver != "" || tool != "")
695 return &workspaceInformation{
696 hasGopackagesDriver: hasGopackagesDriver,
697 effectiveGo111Module: go111module,
698 userGo111Module: go111moduleForVersion(go111module, goversion),
699 goversion: goversion,
700 environmentVariables: envVars,
705 func go111moduleForVersion(go111module string, goversion int) go111module {
706 // Off by default until Go 1.12.
707 if go111module == "off" || (goversion < 12 && go111module == "") {
710 // On by default as of Go 1.16.
711 if go111module == "on" || (goversion >= 16 && go111module == "") {
717 // findWorkspaceRoot searches for the best workspace root according to the
718 // following heuristics:
719 // - First, look for a parent directory containing a gopls.mod file
720 // (experimental only).
721 // - Then, a parent directory containing a go.mod file.
722 // - Then, a child directory containing a go.mod file, if there is exactly
723 // one (non-experimental only).
724 // Otherwise, it returns folder.
725 // TODO (rFindley): move this to workspace.go
726 // TODO (rFindley): simplify this once workspace modules are enabled by default.
727 func findWorkspaceRoot(ctx context.Context, folder span.URI, fs source.FileSource, excludePath func(string) bool, experimental bool) (span.URI, error) {
728 patterns := []string{"go.mod"}
730 patterns = []string{"gopls.mod", "go.mod"}
732 for _, basename := range patterns {
733 dir, err := findRootPattern(ctx, folder, basename, fs)
735 return "", errors.Errorf("finding %s: %w", basename, err)
742 // The experimental workspace can handle nested modules at this point...
747 // ...else we should check if there's exactly one nested module.
748 all, err := findModules(ctx, folder, excludePath, 2)
749 if err == errExhausted {
750 // Fall-back behavior: if we don't find any modules after searching 10000
751 // files, assume there are none.
752 event.Log(ctx, fmt.Sprintf("stopped searching for modules after %d files", fileLimit))
759 // range to access first element.
760 for uri := range all {
761 return dirURI(uri), nil
767 func findRootPattern(ctx context.Context, folder span.URI, basename string, fs source.FileSource) (span.URI, error) {
768 dir := folder.Filename()
770 target := filepath.Join(dir, basename)
771 exists, err := fileExists(ctx, span.URIFromPath(target), fs)
776 return span.URIFromPath(dir), nil
778 next, _ := filepath.Split(dir)
787 // OS-specific path case check, for case-insensitive filesystems.
788 var checkPathCase = defaultCheckPathCase
790 func defaultCheckPathCase(path string) error {
794 func validBuildConfiguration(folder span.URI, ws *workspaceInformation, modFiles map[span.URI]struct{}) bool {
795 // Since we only really understand the `go` command, if the user has a
796 // different GOPACKAGESDRIVER, assume that their configuration is valid.
797 if ws.hasGopackagesDriver {
800 // Check if the user is working within a module or if we have found
801 // multiple modules in the workspace.
802 if len(modFiles) > 0 {
805 // The user may have a multiple directories in their GOPATH.
806 // Check if the workspace is within any of them.
807 for _, gp := range filepath.SplitList(ws.gopath) {
808 if source.InDir(filepath.Join(gp, "src"), folder.Filename()) {
815 // getGoEnv gets the view's various GO* values.
816 func (s *Session) getGoEnv(ctx context.Context, folder string, goversion int, go111module string, configEnv []string) (environmentVariables, map[string]string, error) {
817 envVars := environmentVariables{}
818 vars := map[string]*string{
819 "GOCACHE": &envVars.gocache,
820 "GOPATH": &envVars.gopath,
821 "GOROOT": &envVars.goroot,
822 "GOPRIVATE": &envVars.goprivate,
823 "GOMODCACHE": &envVars.gomodcache,
824 "GO111MODULE": &envVars.go111module,
827 // We can save ~200 ms by requesting only the variables we care about.
828 args := append([]string{"-json"}, imports.RequiredGoEnvVars...)
829 for k := range vars {
830 args = append(args, k)
833 inv := gocommand.Invocation{
839 // Don't go through runGoCommand, as we don't need a temporary -modfile to
841 stdout, err := s.gocmdRunner.Run(ctx, inv)
843 return environmentVariables{}, nil, err
845 env := make(map[string]string)
846 if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
847 return environmentVariables{}, nil, err
850 for key, ptr := range vars {
854 // Old versions of Go don't have GOMODCACHE, so emulate it.
855 if envVars.gomodcache == "" && envVars.gopath != "" {
856 envVars.gomodcache = filepath.Join(filepath.SplitList(envVars.gopath)[0], "pkg/mod")
858 // GO111MODULE does not appear in `go env` output until Go 1.13.
860 envVars.go111module = go111module
862 return envVars, env, err
865 func (v *View) IsGoPrivatePath(target string) bool {
866 return globsMatchPath(v.goprivate, target)
869 func (v *View) ModuleUpgrades() map[string]string {
873 upgrades := map[string]string{}
874 for mod, ver := range v.moduleUpgrades {
880 func (v *View) RegisterModuleUpgrades(upgrades map[string]string) {
884 for mod, ver := range upgrades {
885 v.moduleUpgrades[mod] = ver
890 // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
891 func globsMatchPath(globs, target string) bool {
893 // Extract next non-empty glob in comma-separated list.
895 if i := strings.Index(globs, ","); i >= 0 {
896 glob, globs = globs[:i], globs[i+1:]
898 glob, globs = globs, ""
904 // A glob with N+1 path elements (N slashes) needs to be matched
905 // against the first N+1 path elements of target,
906 // which end just before the N+1'th slash.
907 n := strings.Count(glob, "/")
909 // Walk target, counting slashes, truncating at the N+1'th slash.
910 for i := 0; i < len(target); i++ {
911 if target[i] == '/' {
920 // Not enough prefix elements.
923 matched, _ := path.Match(glob, prefix)
931 var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
933 // TODO(rstambler): Consolidate modURI and modContent back into a FileHandle
934 // after we have a version of the workspace go.mod file on disk. Getting a
935 // FileHandle from the cache for temporary files is problematic, since we
937 func (s *snapshot) vendorEnabled(ctx context.Context, modURI span.URI, modContent []byte) (bool, error) {
938 if s.workspaceMode()&moduleMode == 0 {
941 matches := modFlagRegexp.FindStringSubmatch(s.view.goEnv["GOFLAGS"])
943 if len(matches) != 0 {
947 // Don't override an explicit '-mod=vendor' argument.
948 // We do want to override '-mod=readonly': it would break various module code lenses,
949 // and on 1.16 we know -modfile is available, so we won't mess with go.mod anyway.
950 return modFlag == "vendor", nil
953 modFile, err := modfile.Parse(modURI.Filename(), modContent, nil)
957 if fi, err := os.Stat(filepath.Join(s.view.rootURI.Filename(), "vendor")); err != nil || !fi.IsDir() {
960 vendorEnabled := modFile.Go != nil && modFile.Go.Version != "" && semver.Compare("v"+modFile.Go.Version, "v1.14") >= 0
961 return vendorEnabled, nil
964 func (v *View) allFilesExcluded(pkg *packages.Package) bool {
966 folder := filepath.ToSlash(v.folder.Filename())
967 for _, f := range pkg.GoFiles {
968 f = filepath.ToSlash(f)
969 if !strings.HasPrefix(f, folder) {
972 if !pathExcludedByFilter(strings.TrimPrefix(f, folder), opts) {
979 func pathExcludedByFilterFunc(opts *source.Options) func(string) bool {
980 return func(path string) bool {
981 return pathExcludedByFilter(path, opts)
985 func pathExcludedByFilter(path string, opts *source.Options) bool {
986 path = strings.TrimPrefix(filepath.ToSlash(path), "/")
989 for _, filter := range opts.DirectoryFilters {
990 op, prefix := filter[0], filter[1:]
991 // Non-empty prefixes have to be precise directory matches.
993 prefix = prefix + "/"
996 if !strings.HasPrefix(path, prefix) {