gopls/internal/lsp/cache: move quick-fix bundling logic to the cache pkg

Change-Id: I49ee8d6a84c12be8e4e993c5f9a1fcd597c27e40
Reviewed-on: https://go-review.googlesource.com/c/tools/+/543718
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/lsp/cache/diagnostics.go b/gopls/internal/lsp/cache/diagnostics.go
index e7c2ca7..1a66cde 100644
--- a/gopls/internal/lsp/cache/diagnostics.go
+++ b/gopls/internal/lsp/cache/diagnostics.go
@@ -4,7 +4,12 @@
 
 package cache
 
-import "golang.org/x/tools/gopls/internal/lsp/protocol"
+import (
+	"encoding/json"
+
+	"golang.org/x/tools/gopls/internal/bug"
+	"golang.org/x/tools/gopls/internal/lsp/protocol"
+)
 
 // SuggestedFixFromCommand returns a suggested fix to run the given command.
 func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix {
@@ -14,3 +19,81 @@
 		ActionKind: kind,
 	}
 }
+
+// quickFixesJSON is a JSON-serializable list of quick fixes
+// to be saved in the protocol.Diagnostic.Data field.
+type quickFixesJSON struct {
+	// TODO(rfindley): pack some sort of identifier here for later
+	// lookup/validation?
+	Fixes []protocol.CodeAction
+}
+
+// BundleQuickFixes attempts to bundle sd.SuggestedFixes into the
+// sd.BundledFixes field, so that it can be round-tripped through the client.
+// It returns false if the quick-fixes cannot be bundled.
+func BundleQuickFixes(sd *Diagnostic) bool {
+	if len(sd.SuggestedFixes) == 0 {
+		return true
+	}
+	var actions []protocol.CodeAction
+	for _, fix := range sd.SuggestedFixes {
+		if fix.Edits != nil {
+			// For now, we only support bundled code actions that execute commands.
+			//
+			// In order to cleanly support bundled edits, we'd have to guarantee that
+			// the edits were generated on the current snapshot. But this naively
+			// implies that every fix would have to include a snapshot ID, which
+			// would require us to republish all diagnostics on each new snapshot.
+			//
+			// TODO(rfindley): in order to avoid this additional chatter, we'd need
+			// to build some sort of registry or other mechanism on the snapshot to
+			// check whether a diagnostic is still valid.
+			return false
+		}
+		action := protocol.CodeAction{
+			Title:   fix.Title,
+			Kind:    fix.ActionKind,
+			Command: fix.Command,
+		}
+		actions = append(actions, action)
+	}
+	fixes := quickFixesJSON{
+		Fixes: actions,
+	}
+	data, err := json.Marshal(fixes)
+	if err != nil {
+		bug.Reportf("marshalling quick fixes: %v", err)
+		return false
+	}
+	msg := json.RawMessage(data)
+	sd.BundledFixes = &msg
+	return true
+}
+
+// BundledQuickFixes extracts any bundled codeActions from the
+// diag.Data field.
+func BundledQuickFixes(diag protocol.Diagnostic) []protocol.CodeAction {
+	if diag.Data == nil {
+		return nil
+	}
+	var fix quickFixesJSON
+	if err := json.Unmarshal(*diag.Data, &fix); err != nil {
+		bug.Reportf("unmarshalling quick fix: %v", err)
+		return nil
+	}
+
+	var actions []protocol.CodeAction
+	for _, action := range fix.Fixes {
+		// See BundleQuickFixes: for now we only support bundling commands.
+		if action.Edit != nil {
+			bug.Reportf("bundled fix %q includes workspace edits", action.Title)
+			continue
+		}
+		// associate the action with the incoming diagnostic
+		// (Note that this does not mutate the fix.Fixes slice).
+		action.Diagnostics = []protocol.Diagnostic{diag}
+		actions = append(actions, action)
+	}
+
+	return actions
+}
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index 44dcfa3..70db31a 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -74,7 +74,6 @@
 	IsValidImport                  = source.IsValidImport
 	RemoveIntermediateTestVariants = source.RemoveIntermediateTestVariants
 	IsCommandLineArguments         = source.IsCommandLineArguments
-	BundleQuickFixes               = source.BundleQuickFixes
 	NewFilterer                    = source.NewFilterer
 )
 
diff --git a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.go
index d6c29cb..2a7978b 100644
--- a/gopls/internal/lsp/code_action.go
+++ b/gopls/internal/lsp/code_action.go
@@ -17,6 +17,7 @@
 	"golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct"
 	"golang.org/x/tools/gopls/internal/lsp/analysis/infertypeargs"
 	"golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods"
+	"golang.org/x/tools/gopls/internal/lsp/cache"
 	"golang.org/x/tools/gopls/internal/lsp/command"
 	"golang.org/x/tools/gopls/internal/lsp/mod"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
@@ -629,7 +630,7 @@
 	var actions []protocol.CodeAction
 	var unbundled []protocol.Diagnostic // diagnostics without bundled code actions in their Data field
 	for _, pd := range pds {
-		bundled := source.BundledQuickFixes(pd)
+		bundled := cache.BundledQuickFixes(pd)
 		if len(bundled) > 0 {
 			for _, fix := range bundled {
 				if want[fix.Kind] {
diff --git a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source/diagnostics.go
index 65946e3..8ca78c7 100644
--- a/gopls/internal/lsp/source/diagnostics.go
+++ b/gopls/internal/lsp/source/diagnostics.go
@@ -6,9 +6,7 @@
 
 import (
 	"context"
-	"encoding/json"
 
-	"golang.org/x/tools/gopls/internal/bug"
 	"golang.org/x/tools/gopls/internal/lsp/progress"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
@@ -107,81 +105,3 @@
 
 	*outT = append(*outT, tdiags...)
 }
-
-// quickFixesJSON is a JSON-serializable list of quick fixes
-// to be saved in the protocol.Diagnostic.Data field.
-type quickFixesJSON struct {
-	// TODO(rfindley): pack some sort of identifier here for later
-	// lookup/validation?
-	Fixes []protocol.CodeAction
-}
-
-// BundleQuickFixes attempts to bundle sd.SuggestedFixes into the
-// sd.BundledFixes field, so that it can be round-tripped through the client.
-// It returns false if the quick-fixes cannot be bundled.
-func BundleQuickFixes(sd *Diagnostic) bool {
-	if len(sd.SuggestedFixes) == 0 {
-		return true
-	}
-	var actions []protocol.CodeAction
-	for _, fix := range sd.SuggestedFixes {
-		if fix.Edits != nil {
-			// For now, we only support bundled code actions that execute commands.
-			//
-			// In order to cleanly support bundled edits, we'd have to guarantee that
-			// the edits were generated on the current snapshot. But this naively
-			// implies that every fix would have to include a snapshot ID, which
-			// would require us to republish all diagnostics on each new snapshot.
-			//
-			// TODO(rfindley): in order to avoid this additional chatter, we'd need
-			// to build some sort of registry or other mechanism on the snapshot to
-			// check whether a diagnostic is still valid.
-			return false
-		}
-		action := protocol.CodeAction{
-			Title:   fix.Title,
-			Kind:    fix.ActionKind,
-			Command: fix.Command,
-		}
-		actions = append(actions, action)
-	}
-	fixes := quickFixesJSON{
-		Fixes: actions,
-	}
-	data, err := json.Marshal(fixes)
-	if err != nil {
-		bug.Reportf("marshalling quick fixes: %v", err)
-		return false
-	}
-	msg := json.RawMessage(data)
-	sd.BundledFixes = &msg
-	return true
-}
-
-// BundledQuickFixes extracts any bundled codeActions from the
-// diag.Data field.
-func BundledQuickFixes(diag protocol.Diagnostic) []protocol.CodeAction {
-	if diag.Data == nil {
-		return nil
-	}
-	var fix quickFixesJSON
-	if err := json.Unmarshal(*diag.Data, &fix); err != nil {
-		bug.Reportf("unmarshalling quick fix: %v", err)
-		return nil
-	}
-
-	var actions []protocol.CodeAction
-	for _, action := range fix.Fixes {
-		// See BundleQuickFixes: for now we only support bundling commands.
-		if action.Edit != nil {
-			bug.Reportf("bundled fix %q includes workspace edits", action.Title)
-			continue
-		}
-		// associate the action with the incoming diagnostic
-		// (Note that this does not mutate the fix.Fixes slice).
-		action.Diagnostics = []protocol.Diagnostic{diag}
-		actions = append(actions, action)
-	}
-
-	return actions
-}