func (v *View) shutdown(ctx context.Context) {
	v.initCancelFirstAttempt()
	v.mu.Lock()
	if v.cancel != nil {
		v.cancel()
		v.cancel = nil
	}
	v.mu.Unlock()
	v.snapshotMu.Lock()
	go v.snapshot.generation.Destroy()
	v.snapshotMu.Unlock()
	v.importsState.destroy()
}
TODO(rFindley): probably some of this should also be one in View.Shutdown
above?
package cache
import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"sync"
	"golang.org/x/mod/modfile"
	"golang.org/x/mod/semver"
	exec "golang.org/x/sys/execabs"
	"golang.org/x/tools/go/packages"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/event"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/gocommand"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/imports"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/protocol"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/source"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/memoize"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/span"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/xcontext"
	errors "golang.org/x/xerrors"
)
type View struct {
	session *Session
	id      string
	optionsMu sync.Mutex
	options   *source.Options
	mu sync.Mutex
	baseCtx context.Context
	cancel context.CancelFunc
	name string
	folder span.URI
	importsState *importsState
	moduleUpgrades map[string]string
	filesByURI  map[span.URI]*fileBase
	filesByBase map[string][]*fileBase
	initCancelFirstAttempt context.CancelFunc
	snapshotMu sync.Mutex
	snapshot   *snapshot
	initialWorkspaceLoad chan struct{}
	initializationSema chan struct{}
	rootURI span.URI
	workspaceInformation
	tempWorkspace span.URI
}
type workspaceInformation struct {
	goversion int
	hasGopackagesDriver bool
	environmentVariables
	userGo111Module go111module
	effectiveGo111Module string
	goEnv map[string]string
}
type go111module int
const (
	off = go111module(iota)
	auto
	on
)
type environmentVariables struct {
	gocache, gopath, goroot, goprivate, gomodcache, go111module string
}
type workspaceMode int
const (
	moduleMode workspaceMode = 1 << iota
	tempModfile
	usesWorkspaceModule
)
type builtinPackageHandle struct {
	handle *memoize.Handle
}
type fileBase struct {
	uris  []span.URI
	fname string
	view *View
}
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)
}
func (v *View) ID() string { return v.id }
func tempModFile(modFh source.FileHandle, gosum []byte) (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())
	tmpSumName := sumFilename(tmpURI)
	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())
	}
	defer func() {
		if err != nil {
			cleanup()
			cleanup = nil
		}
	}()
	if gosum != nil {
		if err := ioutil.WriteFile(tmpSumName, gosum, 0655); err != nil {
			return "", cleanup, err
		}
	}
	return tmpURI, cleanup, nil
}
func (v *View) Name() string {
	return v.name
}
func (v *View) Folder() span.URI {
	return v.folder
}
func (v *View) TempWorkspace() span.URI {
	return v.tempWorkspace
}
func (v *View) Options() *source.Options {
	v.optionsMu.Lock()
	defer v.optionsMu.Unlock()
	return v.options
}
func minorOptionsChange(a, b *source.Options) bool {
	if !reflect.DeepEqual(a.Env, b.Env) {
		return false
	}
	if !reflect.DeepEqual(a.DirectoryFilters, b.DirectoryFilters) {
		return false
	}
	if a.MemoryMode != b.MemoryMode {
		return false
	}
	aBuildFlags := make([]string, len(a.BuildFlags))
	bBuildFlags := make([]string, len(b.BuildFlags))
	copy(aBuildFlags, a.BuildFlags)
	copy(bBuildFlags, b.BuildFlags)
	sort.Strings(aBuildFlags)
	sort.Strings(bBuildFlags)
	return reflect.DeepEqual(aBuildFlags, bBuildFlags)
}
func (v *View) SetOptions(ctx context.Context, options *source.Options) (source.View, error) {
	v.optionsMu.Lock()
	if minorOptionsChange(v.options, options) {
		v.options = options
		v.optionsMu.Unlock()
		return v, nil
	}
	v.optionsMu.Unlock()
	newView, err := v.session.updateView(ctx, v, options)
	return newView, err
}
func (v *View) Rebuild(ctx context.Context) (source.Snapshot, func(), error) {
	newView, err := v.session.updateView(ctx, v, v.Options())
	if err != nil {
		return nil, func() {}, err
	}
	snapshot, release := newView.Snapshot(ctx)
	return snapshot, release, nil
}
func (s *snapshot) WriteEnv(ctx context.Context, w io.Writer) error {
	s.view.optionsMu.Lock()
	env := s.view.options.EnvSlice()
	buildFlags := append([]string{}, s.view.options.BuildFlags...)
	s.view.optionsMu.Unlock()
	fullEnv := make(map[string]string)
	for k, v := range s.view.goEnv {
		fullEnv[k] = v
	}
	for _, v := range env {
		s := strings.SplitN(v, "=", 2)
		if len(s) != 2 {
			continue
		}
		if _, ok := fullEnv[s[0]]; ok {
			fullEnv[s[0]] = s[1]
		}
	}
	goVersion, err := s.view.session.gocmdRunner.Run(ctx, gocommand.Invocation{
		Verb:       "version",
		Env:        env,
		WorkingDir: s.view.rootURI.Filename(),
	})
	if err != nil {
		return err
	}
	fmt.Fprintf(w, `go env for %v
(root %s)
(go version %s)
(valid build configuration = %v)
(build flags: %v)
`,
		s.view.folder.Filename(),
		s.view.rootURI.Filename(),
		strings.TrimRight(goVersion.String(), "\n"),
		s.ValidBuildConfiguration(),
		buildFlags)
	for k, v := range fullEnv {
		fmt.Fprintf(w, "%s=%s\n", k, v)
	}
	return nil
}
func (s *snapshot) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
	return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
}
func (s *snapshot) locateTemplateFiles(ctx context.Context) {
	if !s.view.Options().ExperimentalTemplateSupport {
		return
	}
	dir := s.workspace.root.Filename()
	searched := 0
	err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if strings.HasSuffix(filepath.Ext(path), "tmpl") && !pathExcludedByFilter(path, dir, s.view.gomodcache, s.view.options) &&
			!fi.IsDir() {
			k := span.URIFromPath(path)
			fh, err := s.GetVersionedFile(ctx, k)
			if err != nil {
				return nil
			}
			s.files[k] = fh
		}
		searched++
		if fileLimit > 0 && searched > fileLimit {
			return errExhausted
		}
		return nil
	})
	if err != nil {
		event.Error(ctx, "searching for template files failed", err)
	}
}
func (v *View) contains(uri span.URI) bool {
	inRoot := source.InDir(v.rootURI.Filename(), uri.Filename())
	inFolder := source.InDir(v.folder.Filename(), uri.Filename())
	if !inRoot && !inFolder {
		return false
	}
	if inFolder {
		return !pathExcludedByFilter(strings.TrimPrefix(uri.Filename(), v.folder.Filename()), v.rootURI.Filename(), v.gomodcache, v.Options())
	}
	return true
}
func (v *View) mapFile(uri span.URI, f *fileBase) {
	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))
}
func (v *View) relevantChange(c source.FileModification) bool {
	if v.knownFile(c.URI) {
		return true
	}
	for _, src := range []workspaceSource{goWorkWorkspace, goplsModWorkspace} {
		if c.URI == uriForSource(v.rootURI, src) {
			return true
		}
	}
	if c.OnDisk && (c.Action == source.Change || c.Action == source.Delete) {
		return false
	}
	return v.contains(c.URI)
}
func (v *View) knownFile(uri span.URI) bool {
	v.mu.Lock()
	defer v.mu.Unlock()
	f, err := v.findFile(uri)
	return f != nil && err == nil
}
func (v *View) getFile(uri span.URI) *fileBase {
	v.mu.Lock()
	defer v.mu.Unlock()
	f, _ := v.findFile(uri)
	if f != nil {
		return f
	}
	f = &fileBase{
		view:  v,
		fname: uri.Filename(),
	}
	v.mapFile(uri, f)
	return f
}
func (v *View) findFile(uri span.URI) (*fileBase, error) {
	if f := v.filesByURI[uri]; f != nil {
		return f, nil
	}
	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  
		}
		for _, c := range candidates {
			if cStat, err := os.Stat(c.filename()); err == nil {
				if os.SameFile(pathStat, cStat) {
					v.mapFile(uri, c)
					return c, nil
				}
			}
		}
	}
	return nil, nil
}
func (v *View) Shutdown(ctx context.Context) {
	v.session.removeView(ctx, v)
}