internal/lsp/cache: only invalidate metadata for go.mod files on save

Throwing away all of the metadata in the package means it will be
expensive to reload, so it makes sense to keep it around until a go.mod
file has been saved, indicating that the user is more likely to be done
typing. Otherwise, we will be triggering full package metadata reloads
on every keystroke.

Fixes golang/go#42529

Change-Id: Ibc4f4bc8937f8f7176da79e9fefe166468578993
Reviewed-on: https://go-review.googlesource.com/c/tools/+/271299
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index 5e6180d..6d40ecb 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -59,6 +59,10 @@
 	err     error
 }
 
+func (h *fileHandle) Saved() bool {
+	return true
+}
+
 func (c *Cache) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
 	return c.getFile(ctx, uri)
 }
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 43516b7..e1d43de 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -106,6 +106,10 @@
 	}
 }
 
+func (c *closedFile) Saved() bool {
+	return true
+}
+
 func (c *closedFile) Session() string {
 	return ""
 }
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 3e35ca7..9cce32b 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -1415,7 +1415,10 @@
 	}
 	// If a go.mod in the workspace has been changed, invalidate metadata.
 	if kind := originalFH.Kind(); kind == source.Mod {
-		return source.InDir(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
+		if !source.InDir(filepath.Dir(s.view.rootURI.Filename()), originalFH.URI().Filename()) {
+			return false
+		}
+		return currentFH.Saved()
 	}
 	// Get the original and current parsed files in order to check package name
 	// and imports. Use the new snapshot to parse to avoid modifying the
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index cc7b207..89f4c6a 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -306,9 +306,6 @@
 // Overlay is the type for a file held in memory on a session.
 type Overlay interface {
 	VersionedFileHandle
-
-	// Saved returns whether this overlay has been saved to disk.
-	Saved() bool
 }
 
 // FileModification represents a modification to a file.
@@ -438,6 +435,8 @@
 	// Read reads the contents of a file.
 	// If the file is not available, returns a nil slice and an error.
 	Read() ([]byte, error)
+	// Saved reports whether the file has the same content on disk.
+	Saved() bool
 }
 
 // FileIdentity uniquely identifies a file at a version from a FileSystem.