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
-}