[tools] gopls/codeaction: Add dialog tests for editing struct tags

7 views
Skip to first unread message

Peter Weinberger (Gerrit)

unread,
Feb 24, 2026, 1:49:43 PMFeb 24
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Peter Weinberger has uploaded the change for review

Commit message

gopls/codeaction: Add dialog tests for editing struct tags

This CL makes it possible to test dialogs in code action marker
tests. The key message from clients to gopls is 'command/resolve'.
Clients with full control of their communications with gopls
can get to server.ResolveCommand() directly, but vscode-go cannot.

The new code for codeActionMarker() simulates a vscode-go-like client,
as this seems to be the most stressing case.
Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e

Change diff

diff --git a/gopls/internal/test/marker/codeaction_test.go b/gopls/internal/test/marker/codeaction_test.go
new file mode 100644
index 0000000..b9e5adf
--- /dev/null
+++ b/gopls/internal/test/marker/codeaction_test.go
@@ -0,0 +1,353 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package marker
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "log"
+ "path/filepath"
+ "regexp"
+
+ "golang.org/x/tools/gopls/internal/protocol"
+ "golang.org/x/tools/gopls/internal/protocol/command"
+ "golang.org/x/tools/gopls/internal/test/integration"
+ "golang.org/x/tools/internal/expect"
+)
+
+func codeActionMarker(mark marker, loc protocol.Location, kind string) {
+ if !exactlyOneNamedArg(mark, "action", "edit", "result", "err") {
+ return
+ }
+
+ if end := namedArgFunc(mark, "end", convertNamedArgLocation, protocol.Location{}); end.URI != "" {
+ if end.URI != loc.URI {
+ mark.errorf("end marker is in a different file (%s)", filepath.Base(loc.URI.Path()))
+ return
+ }
+ loc.Range.End = end.Range.End
+ }
+
+ var (
+ edit = namedArg(mark, "edit", expect.Identifier(""))
+ result = namedArg(mark, "result", expect.Identifier(""))
+ wantAction = namedArg(mark, "action", expect.Identifier(""))
+ wantErr = namedArgFunc(mark, "err", convertStringMatcher, stringMatcher{})
+ )
+
+ var diag *protocol.Diagnostic
+ if re := namedArg(mark, "diag", (*regexp.Regexp)(nil)); re != nil {
+ d, ok := removeDiagnostic(mark, loc, false, re)
+ if !ok {
+ mark.errorf("no diagnostic at %v matches %q", loc, re)
+ return
+ }
+ diag = &d
+ }
+
+ action, err := resolveCodeAction(mark.run.env, loc.URI, loc.Range, protocol.CodeActionKind(kind), diag)
+ if err != nil {
+ if !wantErr.empty() {
+ wantErr.checkErr(mark, err)
+ } else {
+ mark.errorf("resolveCodeAction failed: %v", err)
+ }
+ return
+ }
+
+ // If when 'action' is set, we simply compare the action, and don't apply it.
+ if wantAction != "" {
+ g := mark.getGolden(wantAction)
+ if action == nil {
+ mark.errorf("no matching codeAction")
+ return
+ }
+ data, err := json.MarshalIndent(action, "", "\t")
+ if err != nil {
+ mark.errorf("failed to marshal codeaction: %v", err)
+ return
+ }
+ data = bytes.ReplaceAll(data, []byte(mark.run.env.Sandbox.Workdir.RootURI()), []byte("$WORKDIR"))
+ compareGolden(mark, data, g)
+ return
+ }
+
+ var changes []protocol.DocumentChange
+ if namedArg(mark, "form0", "") != "" { // wants a dialog
+ // so simulate a dialog-supporting LSP client
+ // (note that the test file should have a capabilities.json file containing
+ // "experimental":{"interactiveInputTypes":["string","bool","number","enum"]}})
+ // The client sends one codeAction/resolve and then two workspace/executeCommand
+ // The first is to get the form for the dialog
+ // The second is to return the user's choices
+ // This deliberately simulates what vscode does, instead of direclty calling ResolveCommand
+ naction, err := mark.run.env.Editor.Server.ResolveCodeAction(mark.run.env.Ctx, action)
+ if err != nil {
+ mark.errorf("ResolveCodeAction failed: %v", err)
+ return
+ }
+ var modifytags command.ModifyTagsArgs
+ if err := json.Unmarshal(naction.Command.Arguments[0], &modifytags); err != nil {
+ mark.errorf("Unmarshal failed, expected command.ModyfyTagsArgs:%v", err)
+ return
+ }
+ yy := protocol.ExecuteCommandParams{
+ Command: "gopls.modify_tags",
+ Arguments: naction.Command.Arguments,
+ }
+ check1, err := json.Marshal(&yy)
+ if err != nil {
+ mark.errorf("marshal(ExecutecommandParams) failed: %v", err)
+ return
+ }
+ zz := command.LSPArgs{
+ Method: "command/resolve",
+ Param: check1, // json.RawMessage(check1) might be clearer, or not
+ }
+ check2, err := json.Marshal(&zz)
+ if err != nil {
+ mark.errorf("marshal(LSPArgs) failed: %v", err)
+ return
+ }
+ ww := &protocol.ExecuteCommandParams{
+ Command: "gopls.lsp",
+ Arguments: []json.RawMessage{check2},
+ }
+ got, err := mark.run.env.Editor.Server.ExecuteCommand(mark.run.env.Ctx, ww)
+ if err != nil {
+ mark.errorf("ExecuteCommand failed: %v", err)
+ return
+ }
+ useful, ok := got.(map[string]any)
+ if !ok {
+ mark.errorf("unexpected type %T, expected map[string]any", got)
+ return
+ }
+ // catch the changes from the forthcoming ExecuteCommand
+ restore := mark.run.env.Editor.Client().SetApplyEditHandler(func(ctx context.Context, wsedit *protocol.WorkspaceEdit) error {
+ changes = append(changes, wsedit.DocumentChanges...)
+ return nil
+ })
+ defer restore()
+ switch kind {
+ case "refactor.rewrite.removeTags":
+ modifytags.Remove = mark.note.NamedArgs["form0"].(string)
+ case "refactor.rewrite.addTags":
+ modifytags.Add = mark.note.NamedArgs["form0"].(string)
+ modifytags.Transform = mark.note.NamedArgs["form1"].(string)
+ default:
+ mark.errorf("unexpected kind %q", kind)
+ return
+ }
+ want, err := json.Marshal(modifytags)
+ if err != nil {
+ log.Fatal(err)
+ }
+ cmd := &protocol.ExecuteCommandParams{
+ Command: useful["command"].(string),
+ Arguments: []json.RawMessage{want},
+ }
+ got, err = mark.run.env.Editor.Server.ExecuteCommand(mark.run.env.Ctx, cmd)
+ if err != nil {
+ mark.errorf("ExecuteCommand failed: %v", err)
+ return
+ } // expect nil return. Side effect is to change the file
+ } else {
+ changes, err = applyCodeAction(mark.run.env, action)
+ if err != nil {
+ if !wantErr.empty() {
+ wantErr.checkErr(mark, err)
+ } else {
+ mark.errorf("codeAction failed: %v", err)
+ }
+ return
+ }
+ }
+ changed, err := changedFiles(mark.run.env, changes)
+ if err != nil {
+ mark.errorf("changedFiles failed: %v", err)
+ return
+ }
+
+ switch {
+ case edit != "":
+ g := mark.getGolden(edit)
+ checkDiffs(mark, changed, g)
+ case result != "":
+ g := mark.getGolden(result)
+ // Check the file state.
+ checkChangedFiles(mark, changed, g)
+ case !wantErr.empty():
+ wantErr.checkErr(mark, err)
+ default:
+ panic("unreachable")
+ }
+}
+
+func exactlyOneNamedArg(mark marker, names ...string) bool {
+ var found []string
+ for _, name := range names {
+ if _, ok := mark.note.NamedArgs[name]; ok {
+ found = append(found, name)
+ }
+ }
+ if len(found) != 1 {
+ mark.errorf("need exactly one of %v to be set, got %v", names, found)
+ return false
+ }
+ return true
+}
+
+// not used for @codeaction, but codeactions
+
+// codeAction executes a textDocument/codeAction request for the specified
+// location and kind. If diag is non-nil, it is used as the code action
+// context.
+//
+// The resulting map contains resulting file contents after the code action is
+// applied. Currently, this function does not support code actions that return
+// edits directly; it only supports code action commands.
+func codeAction(env *integration.Env, uri protocol.DocumentURI, rng protocol.Range, kind protocol.CodeActionKind, diag *protocol.Diagnostic) (map[string][]byte, error) {
+ action, err := resolveCodeAction(env, uri, rng, kind, diag)
+ if err != nil {
+ return nil, err
+ }
+ changes, err := applyCodeAction(env, action)
+ if err != nil {
+ return nil, err
+ }
+ return changedFiles(env, changes)
+}
+
+// resolveCodeAction resolves the code action specified by the given location,
+// kind, and diagnostic.
+func resolveCodeAction(env *integration.Env, uri protocol.DocumentURI, rng protocol.Range, kind protocol.CodeActionKind, diag *protocol.Diagnostic) (*protocol.CodeAction, error) {
+ // Request all code actions that apply to the diagnostic.
+ // A production client would set Only=[kind],
+ // but we can give a better error if we don't filter.
+ params := &protocol.CodeActionParams{
+ TextDocument: protocol.TextDocumentIdentifier{URI: uri},
+ Range: rng,
+ Context: protocol.CodeActionContext{
+ Only: []protocol.CodeActionKind{protocol.Empty}, // => all
+ //TriggerKind: protocol.CodeActionTriggerKind(1 /*Invoked*/),
+ },
+ }
+ if diag != nil {
+ params.Context.Diagnostics = []protocol.Diagnostic{*diag}
+ }
+
+ actions, err := env.Editor.Server.CodeAction(env.Ctx, params)
+ if err != nil {
+ return nil, err
+ }
+
+ // Find the sole candidate CodeAction of exactly the specified kind
+ // (e.g. refactor.inline.call).
+ var candidates []protocol.CodeAction
+ for _, act := range actions {
+ if act.Kind == kind {
+ candidates = append(candidates, act)
+ }
+ }
+ if len(candidates) != 1 {
+ var msg bytes.Buffer
+ fmt.Fprintf(&msg, "found %d CodeActions of kind %s for this diagnostic, want 1", len(candidates), kind)
+ for _, act := range actions {
+ fmt.Fprintf(&msg, "\n\tfound %q (%s)", act.Title, act.Kind)
+ }
+ return nil, errors.New(msg.String())
+ }
+ action := candidates[0]
+
+ // Resolve code action edits first if the client has resolve support
+ // and the code action has no edits.
+ if action.Edit == nil {
+ editSupport, err := env.Editor.EditResolveSupport()
+ if err != nil {
+ return nil, err
+ }
+ if editSupport {
+ resolved, err := env.Editor.Server.ResolveCodeAction(env.Ctx, &action)
+ if err != nil {
+ return nil, err
+ }
+ action = *resolved
+ }
+ }
+
+ return &action, nil
+}
+
+// applyCodeAction applies the given code action, and captures the resulting
+// document changes.
+func applyCodeAction(env *integration.Env, action *protocol.CodeAction) ([]protocol.DocumentChange, error) {
+ // PJW: this doesn't work for dialogs! Fix it!
+ // Collect any server-initiated changes created by workspace/applyEdit.
+ //
+ // We set up this handler immediately, not right before executing the code
+ // action command, so we can assert that neither the codeAction request nor
+ // codeAction resolve request cause edits as a side effect (golang/go#71405).
+ var changes []protocol.DocumentChange
+ restore := env.Editor.Client().SetApplyEditHandler(func(ctx context.Context, wsedit *protocol.WorkspaceEdit) error {
+ changes = append(changes, wsedit.DocumentChanges...)
+ return nil
+ })
+ defer restore()
+
+ if action.Edit == nil && action.Command == nil {
+ panic("bad action")
+ }
+
+ // Apply the codeAction.
+ //
+ // Spec:
+ // "If a code action provides an edit and a command, first the edit is
+ // executed and then the command."
+ // An action may specify an edit and/or a command, to be
+ // applied in that order. But since applyDocumentChanges(env,
+ // action.Edit.DocumentChanges) doesn't compose, for now we
+ // assert that actions return one or the other.
+ if action.Edit != nil {
+ if len(action.Edit.Changes) > 0 {
+ env.TB.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes", action.Kind, action.Title)
+ }
+ if action.Edit.DocumentChanges != nil {
+ if action.Command != nil {
+ env.TB.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Command", action.Kind, action.Title)
+ }
+ return action.Edit.DocumentChanges, nil
+ }
+ }
+
+ if action.Command != nil {
+ // This is a typical CodeAction command:
+ //
+ // Title: "Implement error"
+ // Command: gopls.apply_fix
+ // Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}]
+ //
+ // The client makes an ExecuteCommand RPC to the server,
+ // which dispatches it to the ApplyFix handler.
+ // ApplyFix dispatches to the "stub_methods" fixer (the meat).
+ // The server then makes an ApplyEdit RPC to the client,
+ // whose WorkspaceEditFunc hook temporarily gathers the edits
+ // instead of applying them.
+ // PJW: this is inadequate in the new world of Forms and CommandResolve)
+
+ if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
+ Command: action.Command.Command,
+ Arguments: action.Command.Arguments,
+ }); err != nil {
+ return nil, err
+ }
+ return changes, nil // populated as a side effect of ExecuteCommand
+ }
+
+ return nil, nil
+}
diff --git a/gopls/internal/test/marker/doc.go b/gopls/internal/test/marker/doc.go
index 0b7afa4..ee87234 100644
--- a/gopls/internal/test/marker/doc.go
+++ b/gopls/internal/test/marker/doc.go
@@ -144,13 +144,15 @@
completion candidate produced at the given location with provided label
results in the given golden state.

- - codeaction(start location, kind string, diag=regexp, end=location, action=golden, edit=golden, result=golden, err=stringMatcher)
+ - codeaction(start location, kind string, diag=regexp, end=location, action=golden, edit=golden, result=golden, err=stringMatcher, form0=string, form1=string)

Specifies a code action to request at the location, with given kind.
If diag is set, the code action must be associated with the given
diagnostic.
If end is set, the location is defined to be between start.Start and
end.End.
+ If form0 is set the codeaction is invoking a user dialog and the form
+ arguments are the user response.

Exactly one of action, edit, result, or err must be set:
If action is set, it is a golden reference to a JSON blob representing the
diff --git a/gopls/internal/test/marker/marker_test.go b/gopls/internal/test/marker/marker_test.go
index d99dfeb..eaad054 100644
--- a/gopls/internal/test/marker/marker_test.go
+++ b/gopls/internal/test/marker/marker_test.go
@@ -11,7 +11,6 @@
"bytes"
"context"
"encoding/json"
- "errors"
"flag"
"fmt"
"go/token"
@@ -583,20 +582,6 @@
return dflt
}

-func exactlyOneNamedArg(mark marker, names ...string) bool {
- var found []string
- for _, name := range names {
- if _, ok := mark.note.NamedArgs[name]; ok {
- found = append(found, name)
- }
- }
- if len(found) != 1 {
- mark.errorf("need exactly one of %v to be set, got %v", names, found)
- return false
- }
- return true
-}
-
// is reports whether arg is a T.
func is[T any](arg any) bool {
_, ok := arg.(T)
@@ -616,7 +601,7 @@
// See doc.go for marker documentation.
var actionMarkerFuncs = map[string]func(marker){
"acceptcompletion": actionMarkerFunc(acceptCompletionMarker),
- "codeaction": actionMarkerFunc(codeActionMarker, "end", "diag", "action", "result", "edit", "err"),
+ "codeaction": actionMarkerFunc(codeActionMarker, "end", "diag", "action", "result", "edit", "err", "form0", "form1"),
"codelenses": actionMarkerFunc(codeLensesMarker),
"complete": actionMarkerFunc(completeMarker),
"def": actionMarkerFunc(defMarker),
@@ -792,10 +777,23 @@
//
// See the documentation for RunMarkerTests for more details on the test data
// archive.
-func loadMarkerTests(dir string) ([]*markerTest, error) {
+func loadMarkerTests(dir string, only ...string) ([]*markerTest, error) {
var tests []*markerTest
+ var use *regexp.Regexp
+ if len(only) > 0 {
+ var err error
+ use, err = regexp.Compile(strings.Join(only, "|"))
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ use = regexp.MustCompile(".")
+ }
err := filepath.WalkDir(dir, func(path string, _ fs.DirEntry, err error) error {
if strings.HasSuffix(path, ".txt") {
+ if !use.MatchString(path) {
+ return nil
+ }
content, err := os.ReadFile(path)
if err != nil {
return err
@@ -1490,7 +1488,7 @@
}

// checkDiffs checks that the diff content stored in the given golden directory
-// converts the original contents into the changed contents.
+// converts the orginal contents into the changed contents.
// (This logic is strange because hundreds of existing marker tests were created
// containing a modified version of unified diffs.)
func checkDiffs(mark marker, changed map[string][]byte, golden *Golden) {
@@ -1518,7 +1516,7 @@
mark.note.Name, name, got)
return
} else {
- // restore the ToUnified header lines deleted above
+ // restore the ToUnifeed header lines deleted above
// before calling ApplyUnified
diffsFromTest := "--- \n+++ \n" + string(tdiffs)
want, err := diff.ApplyUnified(diffsFromTest, before)
@@ -2223,93 +2221,6 @@
return result, nil
}

-func codeActionMarker(mark marker, loc protocol.Location, kind string) {
- if !exactlyOneNamedArg(mark, "action", "edit", "result", "err") {
- return
- }
-
- if end := namedArgFunc(mark, "end", convertNamedArgLocation, protocol.Location{}); end.URI != "" {
- if end.URI != loc.URI {
- mark.errorf("end marker is in a different file (%s)", filepath.Base(loc.URI.Path()))
- return
- }
- loc.Range.End = end.Range.End
- }
-
- var (
- edit = namedArg(mark, "edit", expect.Identifier(""))
- result = namedArg(mark, "result", expect.Identifier(""))
- wantAction = namedArg(mark, "action", expect.Identifier(""))
- wantErr = namedArgFunc(mark, "err", convertStringMatcher, stringMatcher{})
- )
-
- var diag *protocol.Diagnostic
- if re := namedArg(mark, "diag", (*regexp.Regexp)(nil)); re != nil {
- d, ok := removeDiagnostic(mark, loc, false, re)
- if !ok {
- mark.errorf("no diagnostic at %v matches %q", loc, re)
- return
- }
- diag = &d
- }
-
- action, err := resolveCodeAction(mark.run.env, loc.URI, loc.Range, protocol.CodeActionKind(kind), diag)
- if err != nil {
- if !wantErr.empty() {
- wantErr.checkErr(mark, err)
- } else {
- mark.errorf("resolveCodeAction failed: %v", err)
- }
- return
- }
-
- // If when 'action' is set, we simply compare the action, and don't apply it.
- if wantAction != "" {
- g := mark.getGolden(wantAction)
- if action == nil {
- mark.errorf("no matching codeAction")
- return
- }
- data, err := json.MarshalIndent(action, "", "\t")
- if err != nil {
- mark.errorf("failed to marshal codeaction: %v", err)
- return
- }
- data = bytes.ReplaceAll(data, []byte(mark.run.env.Sandbox.Workdir.RootURI()), []byte("$WORKDIR"))
- compareGolden(mark, data, g)
- return
- }
-
- changes, err := applyCodeAction(mark.run.env, action)
- if err != nil {
- if !wantErr.empty() {
- wantErr.checkErr(mark, err)
- } else {
- mark.errorf("codeAction failed: %v", err)
- }
- return
- }
- changed, err := changedFiles(mark.run.env, changes)
- if err != nil {
- mark.errorf("changedFiles failed: %v", err)
- return
- }
-
- switch {
- case edit != "":
- g := mark.getGolden(edit)
- checkDiffs(mark, changed, g)
- case result != "":
- g := mark.getGolden(result)
- // Check the file state.
- checkChangedFiles(mark, changed, g)
- case !wantErr.empty():
- wantErr.checkErr(mark, err)
- default:
- panic("unreachable")
- }
-}
-
// codeLensesMarker runs the @codelenses() marker, collecting @codelens marks
// in the current file and comparing with the result of the
// textDocument/codeLens RPC.
@@ -2414,150 +2325,6 @@
wantErr.checkErr(mark, err)
}

-// codeAction executes a textDocument/codeAction request for the specified
-// location and kind. If diag is non-nil, it is used as the code action
-// context.
-//
-// The resulting map contains resulting file contents after the code action is
-// applied. Currently, this function does not support code actions that return
-// edits directly; it only supports code action commands.
-func codeAction(env *integration.Env, uri protocol.DocumentURI, rng protocol.Range, kind protocol.CodeActionKind, diag *protocol.Diagnostic) (map[string][]byte, error) {
- action, err := resolveCodeAction(env, uri, rng, kind, diag)
- if err != nil {
- return nil, err
- }
- changes, err := applyCodeAction(env, action)
- if err != nil {
- return nil, err
- }
- return changedFiles(env, changes)
-}
-
-// resolveCodeAction resolves the code action specified by the given location,
-// kind, and diagnostic.
-func resolveCodeAction(env *integration.Env, uri protocol.DocumentURI, rng protocol.Range, kind protocol.CodeActionKind, diag *protocol.Diagnostic) (*protocol.CodeAction, error) {
- // Request all code actions that apply to the diagnostic.
- // A production client would set Only=[kind],
- // but we can give a better error if we don't filter.
- params := &protocol.CodeActionParams{
- TextDocument: protocol.TextDocumentIdentifier{URI: uri},
- Range: rng,
- Context: protocol.CodeActionContext{
- Only: []protocol.CodeActionKind{protocol.Empty}, // => all
- },
- }
- if diag != nil {
- params.Context.Diagnostics = []protocol.Diagnostic{*diag}
- }
-
- actions, err := env.Editor.Server.CodeAction(env.Ctx, params)
- if err != nil {
- return nil, err
- }
-
- // Find the sole candidate CodeAction of exactly the specified kind
- // (e.g. refactor.inline.call).
- var candidates []protocol.CodeAction
- for _, act := range actions {
- if act.Kind == kind {
- candidates = append(candidates, act)
- }
- }
- if len(candidates) != 1 {
- var msg bytes.Buffer
- fmt.Fprintf(&msg, "found %d CodeActions of kind %s for this diagnostic, want 1", len(candidates), kind)
- for _, act := range actions {
- fmt.Fprintf(&msg, "\n\tfound %q (%s)", act.Title, act.Kind)
- }
- return nil, errors.New(msg.String())
- }
- action := candidates[0]
-
- // Resolve code action edits first if the client has resolve support
- // and the code action has no edits.
- if action.Edit == nil {
- editSupport, err := env.Editor.EditResolveSupport()
- if err != nil {
- return nil, err
- }
- if editSupport {
- resolved, err := env.Editor.Server.ResolveCodeAction(env.Ctx, &action)
- if err != nil {
- return nil, err
- }
- action = *resolved
- }
- }
-
- return &action, nil
-}
-
-// applyCodeAction applies the given code action, and captures the resulting
-// document changes.
-func applyCodeAction(env *integration.Env, action *protocol.CodeAction) ([]protocol.DocumentChange, error) {
- // Collect any server-initiated changes created by workspace/applyEdit.
- //
- // We set up this handler immediately, not right before executing the code
- // action command, so we can assert that neither the codeAction request nor
- // codeAction resolve request cause edits as a side effect (golang/go#71405).
- var changes []protocol.DocumentChange
- restore := env.Editor.Client().SetApplyEditHandler(func(ctx context.Context, wsedit *protocol.WorkspaceEdit) error {
- changes = append(changes, wsedit.DocumentChanges...)
- return nil
- })
- defer restore()
-
- if action.Edit == nil && action.Command == nil {
- panic("bad action")
- }
-
- // Apply the codeAction.
- //
- // Spec:
- // "If a code action provides an edit and a command, first the edit is
- // executed and then the command."
- // An action may specify an edit and/or a command, to be
- // applied in that order. But since applyDocumentChanges(env,
- // action.Edit.DocumentChanges) doesn't compose, for now we
- // assert that actions return one or the other.
- if action.Edit != nil {
- if len(action.Edit.Changes) > 0 {
- env.TB.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes", action.Kind, action.Title)
- }
- if action.Edit.DocumentChanges != nil {
- if action.Command != nil {
- env.TB.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Command", action.Kind, action.Title)
- }
- return action.Edit.DocumentChanges, nil
- }
- }
-
- if action.Command != nil {
- // This is a typical CodeAction command:
- //
- // Title: "Implement error"
- // Command: gopls.apply_fix
- // Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}]
- //
- // The client makes an ExecuteCommand RPC to the server,
- // which dispatches it to the ApplyFix handler.
- // ApplyFix dispatches to the "stub_methods" fixer (the meat).
- // The server then makes an ApplyEdit RPC to the client,
- // whose WorkspaceEditFunc hook temporarily gathers the edits
- // instead of applying them.
-
- if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
- Command: action.Command.Command,
- Arguments: action.Command.Arguments,
- }); err != nil {
- return nil, err
- }
- return changes, nil // populated as a side effect of ExecuteCommand
- }
-
- return nil, nil
-}
-
// refsMarker implements the @refs marker.
func refsMarker(mark marker, src protocol.Location, want ...protocol.Location) {
refs := func(includeDeclaration bool, want []protocol.Location) error {
diff --git a/gopls/internal/test/marker/testdata/codeaction/dialog_add_tags.txt b/gopls/internal/test/marker/testdata/codeaction/dialog_add_tags.txt
new file mode 100644
index 0000000..6c8e754
--- /dev/null
+++ b/gopls/internal/test/marker/testdata/codeaction/dialog_add_tags.txt
@@ -0,0 +1,37 @@
+Test struct tags with dialogs
+
+-- capabilities.json --
+{
+ "experimental":{"interactiveInputTypes":["string","bool","number","enum"]}
+}
+
+-- flags --
+-ignore_extra_diags
+
+
+-- addtags.go --
+package addtags
+
+type A struct {
+ x int //@codeaction("x", "refactor.rewrite.addTags", edit=singleline, form0="xml", form1="camelcase")
+ y int //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.addTags", edit=twolines, form0="xml", form1="camelcase")
+ z int //@codeaction(re`()n`, "refactor.rewrite.addTags", edit=entirestruct, form0="xml", form1="camelcase")
+}
+-- @entirestruct/addtags.go --
+@@ -4,3 +4,3 @@
+- x int //@codeaction("x", "refactor.rewrite.addTags", edit=singleline, form0="xml", form1="camelcase")
+- y int //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.addTags", edit=twolines, form0="xml", form1="camelcase")
+- z int //@codeaction(re`()n`, "refactor.rewrite.addTags", edit=entirestruct, form0="xml", form1="camelcase")
++ x int `xml:"x"` //@codeaction("x", "refactor.rewrite.addTags", edit=singleline, form0="xml", form1="camelcase")
++ y int `xml:"y"` //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.addTags", edit=twolines, form0="xml", form1="camelcase")
++ z int `xml:"z"` //@codeaction(re`()n`, "refactor.rewrite.addTags", edit=entirestruct, form0="xml", form1="camelcase")
+-- @singleline/addtags.go --
+@@ -4 +4 @@
+- x int //@codeaction("x", "refactor.rewrite.addTags", edit=singleline, form0="xml", form1="camelcase")
++ x int `xml:"x"` //@codeaction("x", "refactor.rewrite.addTags", edit=singleline, form0="xml", form1="camelcase")
+-- @twolines/addtags.go --
+@@ -5,2 +5,2 @@
+- y int //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.addTags", edit=twolines, form0="xml", form1="camelcase")
+- z int //@codeaction(re`()n`, "refactor.rewrite.addTags", edit=entirestruct, form0="xml", form1="camelcase")
++ y int `xml:"y"` //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.addTags", edit=twolines, form0="xml", form1="camelcase")
++ z int `xml:"z"` //@codeaction(re`()n`, "refactor.rewrite.addTags", edit=entirestruct, form0="xml", form1="camelcase")
diff --git a/gopls/internal/test/marker/testdata/codeaction/dialog_remove_tags.txt b/gopls/internal/test/marker/testdata/codeaction/dialog_remove_tags.txt
new file mode 100644
index 0000000..cfdc4a7
--- /dev/null
+++ b/gopls/internal/test/marker/testdata/codeaction/dialog_remove_tags.txt
@@ -0,0 +1,38 @@
+Test struct tags with dialogs
+
+(why can't we also test addtags in this file?)
+
+-- capabilities.json --
+{
+ "experimental":{"interactiveInputTypes":["string","bool","number","enum"]}
+}
+
+-- flags --
+-ignore_extra_diags
+
+-- removetags.go --
+package removetags
+
+type A struct {
+ x int `xml:"x"` //@codeaction("x", "refactor.rewrite.removeTags", edit=singleline, form0="xml")
+ y int `xml:"y"` //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.removeTags", edit=twolines, form0="xml")
+ z int `xml:"z"` //@codeaction(re`()n`, "refactor.rewrite.removeTags", edit=entirestruct, form0="xml")
+}
+-- @entirestruct/removetags.go --
+@@ -4,3 +4,3 @@
+- x int `xml:"x"` //@codeaction("x", "refactor.rewrite.removeTags", edit=singleline, form0="xml")
+- y int `xml:"y"` //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.removeTags", edit=twolines, form0="xml")
+- z int `xml:"z"` //@codeaction(re`()n`, "refactor.rewrite.removeTags", edit=entirestruct, form0="xml")
++ x int //@codeaction("x", "refactor.rewrite.removeTags", edit=singleline, form0="xml")
++ y int //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.removeTags", edit=twolines, form0="xml")
++ z int //@codeaction(re`()n`, "refactor.rewrite.removeTags", edit=entirestruct, form0="xml")
+-- @singleline/removetags.go --
+@@ -4 +4 @@
+- x int `xml:"x"` //@codeaction("x", "refactor.rewrite.removeTags", edit=singleline, form0="xml")
++ x int //@codeaction("x", "refactor.rewrite.removeTags", edit=singleline, form0="xml")
+-- @twolines/removetags.go --
+@@ -5,2 +5,2 @@
+- y int `xml:"y"` //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.removeTags", edit=twolines, form0="xml")
+- z int `xml:"z"` //@codeaction(re`()n`, "refactor.rewrite.removeTags", edit=entirestruct, form0="xml")
++ y int //@codeaction(re`(?s)y.*.z int`, "refactor.rewrite.removeTags", edit=twolines, form0="xml")
++ z int //@codeaction(re`()n`, "refactor.rewrite.removeTags", edit=entirestruct, form0="xml")

Change information

Files:
  • A gopls/internal/test/marker/codeaction_test.go
  • M gopls/internal/test/marker/doc.go
  • M gopls/internal/test/marker/marker_test.go
  • A gopls/internal/test/marker/testdata/codeaction/dialog_add_tags.txt
  • A gopls/internal/test/marker/testdata/codeaction/dialog_remove_tags.txt
Change size: L
Delta: 5 files changed, 448 insertions(+), 251 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
Gerrit-Change-Number: 748500
Gerrit-PatchSet: 1
Gerrit-Owner: Peter Weinberger <p...@google.com>
Gerrit-Reviewer: Peter Weinberger <p...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Peter Weinberger (Gerrit)

unread,
Feb 25, 2026, 9:39:29 AMFeb 25
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Peter Weinberger uploaded new patchset

Peter Weinberger uploaded patch set #2 to this change.
Following approvals got outdated and were removed:
  • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
Gerrit-Change-Number: 748500
Gerrit-PatchSet: 2
Gerrit-Owner: Peter Weinberger <p...@google.com>
Gerrit-Reviewer: Peter Weinberger <p...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Hongxiang Jiang (Gerrit)

unread,
Mar 2, 2026, 11:52:27 AMMar 2
to Peter Weinberger, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
Attention needed from Peter Weinberger

Hongxiang Jiang added 3 comments

File gopls/internal/test/marker/codeaction_test.go
Line 94, Patchset 2 (Latest): var modifytags command.ModifyTagsArgs
Hongxiang Jiang . unresolved

Hi Peter, I'm confused by the fact that we have some server awared code in the client.

I thought we could just have the client read the output of "command/resolve" and pass the returned ExecuteCommandParam to the `workspace/executeCommand` below.

Line 155, Patchset 2 (Latest): got, err = mark.run.env.Editor.Server.ExecuteCommand(mark.run.env.Ctx, cmd)
Hongxiang Jiang . unresolved

What is the "got" meant to be used here. I'm not very familiar with the marker test. I thought the "got" above at line 121 is already obtained and we just need to compare the "got" with the "want" specified from the test file `@singline/addtest.go`...

Line 341, Patchset 2 (Latest): // PJW: this is inadequate in the new world of Forms and CommandResolve)
Hongxiang Jiang . unresolved

I have not fully understand the purpose of the action=? param in the code action. I know this is not your change...

can we just pass the ExecuteCommandParams to the command/resolve and pass the form 0 and form 1 and then call command/resolve, and then execute the command?

Open in Gerrit

Related details

Attention is currently required from:
  • Peter Weinberger
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: tools
    Gerrit-Branch: master
    Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
    Gerrit-Change-Number: 748500
    Gerrit-PatchSet: 2
    Gerrit-Owner: Peter Weinberger <p...@google.com>
    Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
    Gerrit-Reviewer: Peter Weinberger <p...@google.com>
    Gerrit-Attention: Peter Weinberger <p...@google.com>
    Gerrit-Comment-Date: Mon, 02 Mar 2026 16:52:17 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Peter Weinberger (Gerrit)

    unread,
    Mar 3, 2026, 11:09:43 AMMar 3
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Peter Weinberger

    Peter Weinberger uploaded new patchset

    Peter Weinberger uploaded patch set #3 to this change.
    Following approvals got outdated and were removed:
    • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI

    Related details

    Attention is currently required from:
    • Peter Weinberger
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: tools
      Gerrit-Branch: master
      Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
      Gerrit-Change-Number: 748500
      Gerrit-PatchSet: 3
      unsatisfied_requirement
      open
      diffy

      Peter Weinberger (Gerrit)

      unread,
      Mar 3, 2026, 3:37:22 PMMar 3
      to goph...@pubsubhelper.golang.org, Go LUCI, Hongxiang Jiang, golang-co...@googlegroups.com
      Attention needed from Hongxiang Jiang

      Peter Weinberger added 3 comments

      File gopls/internal/test/marker/codeaction_test.go
      Line 94, Patchset 2: var modifytags command.ModifyTagsArgs
      Hongxiang Jiang . resolved

      Hi Peter, I'm confused by the fact that we have some server awared code in the client.

      I thought we could just have the client read the output of "command/resolve" and pass the returned ExecuteCommandParam to the `workspace/executeCommand` below.

      Peter Weinberger

      I added a comment saying that this is the gopls version of interface GoModifyTagsArgs in goModifyTags.ts in vscode-go.

      Line 155, Patchset 2: got, err = mark.run.env.Editor.Server.ExecuteCommand(mark.run.env.Ctx, cmd)
      Hongxiang Jiang . resolved

      What is the "got" meant to be used here. I'm not very familiar with the marker test. I thought the "got" above at line 121 is already obtained and we just need to compare the "got" with the "want" specified from the test file `@singline/addtest.go`...

      Peter Weinberger

      I change it to _, as we don't look at the value (which is nil)

      Line 341, Patchset 2: // PJW: this is inadequate in the new world of Forms and CommandResolve)
      Hongxiang Jiang . unresolved

      I have not fully understand the purpose of the action=? param in the code action. I know this is not your change...

      can we just pass the ExecuteCommandParams to the command/resolve and pass the form 0 and form 1 and then call command/resolve, and then execute the command?

      Peter Weinberger

      See line 157 in markertest/doc.go for the action= parameter. (I don't understand them either). I've reorganized the code with a new function applyCodeActionForm().

      And I think we could call ResolveCommand() directly, but this code simulates the interaction with vscode-go, which seemed to me to be more stressful.

      Let me know if you want me to change it.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Hongxiang Jiang
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
        Gerrit-Change-Number: 748500
        Gerrit-PatchSet: 3
        Gerrit-Owner: Peter Weinberger <p...@google.com>
        Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
        Gerrit-Reviewer: Peter Weinberger <p...@google.com>
        Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
        Gerrit-Comment-Date: Tue, 03 Mar 2026 20:37:19 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Hongxiang Jiang <hxj...@golang.org>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Hongxiang Jiang (Gerrit)

        unread,
        Mar 4, 2026, 1:29:53 PMMar 4
        to Peter Weinberger, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
        Attention needed from Peter Weinberger

        Hongxiang Jiang added 5 comments

        File gopls/internal/test/marker/codeaction_test.go
        Line 132, Patchset 3 (Latest): // wants a dialog
        Hongxiang Jiang . unresolved

        I assume this belongs to

        ```


        if namedArg(mark, "form0", "") != "" { // wants a dialog

        ```

        Line 142, Patchset 3 (Latest): naction, err := mark.run.env.Editor.Server.ResolveCodeAction(mark.run.env.Ctx, action)
        if err != nil {

        mark.errorf("ResolveCodeAction failed: %v", err)
        return changes, err
        }
        Hongxiang Jiang . unresolved

        the codeAction/resolve is already called in line 53, so I don't think there is a need call this again.

        Line 147, Patchset 3 (Latest): var modifytags command.ModifyTagsArgs // Gopls version of GoModifyTagsArgs in goModifytags.ts in vscode-go
        Hongxiang Jiang . unresolved

        Hi peter, I think `goModifyTags.ts` is not the correct place to look.

        GoModifyTags.ts is not the dialog implementation in vscode-go. `form.ts` is the right place, the function name is `ResolveCommand`.

        The logic of the fake client should looks like below

        ```
        // The client sends three workspace/executeCommand to the server.
        // The first (underneath is command/resolve) is to get the form for the dialog
        // The second (underneath is command/resolve) is to return the user's choices
        // The thid (underneath is the target command) is to run the command and
        // apply edits.
        original := protocol.ExecuteCommandParams{
        Command: action.Command.Command,
        Arguments: action.Command.Arguments,
        }
        	withQuestions := new(protocol.ExecuteCommandParams)
        {
        // Call workspace/executeCommand whose underneath is gopls.lsp
        // command/resolve.
        ....
        // The return should be a [protocol.ExecuteCommandParams] with
        // question.
        if err := json.Unmarshal(???, withQuestions); err != nil {
        log.Printf("marshaled %s", string(marshaled))
        return changes, err
        }
        }
        	// Simulate the langauge client collect input from the user and insert
        // the answer to the [protocol.ExecuteCommandParam.FormAnswers].
        //
        // Remove the questions and attach the answers.
        withQuestions.FormFields = nil
        if mark.note.NamedArgs["form0"] != nil {
        withQuestions.FormAnswers = append(withQuestions.FormAnswers, mark.note.NamedArgs["form0"])
        }
        if mark.note.NamedArgs["form1"] != nil {
        withQuestions.FormAnswers = append(withQuestions.FormAnswers, mark.note.NamedArgs["form1"])
        }
        	// Second workspace/executeCommand (whose underneath is gopls.lsp) receive
        // the execute command with answers and return execute command.
        final := new(protocol.ExecuteCommandParams)
        {
        // Call workspace/executeCommand whose underneath is gopls.lsp
        // command/resolve. The input is param with answers.
        ....
        // The return should be a [protocol.ExecuteCommandParams] without
        // any questions. Because the gopls read the answer and embed the
        // answers to the command.args.

        // Validate the return should not have any questions.
        if err := json.Unmarshal(???, final); err != nil {
        log.Printf("marshaled %s", string(marshaled))
        return changes, err
        }
        }
        	// catch the changes from the forthcoming ExecuteCommand
        	restore := mark.run.env.Editor.Client().SetApplyEditHandler(func(ctx context.Context, wsedit *protocol.WorkspaceEdit) error {
        		changes = append(changes, wsedit.DocumentChanges...)
        return nil
        })
        defer restore()
        	// Third workspace/executeCommand (whose underneath is the target command
        // e.g. gopls.modifyTags ...) runs the target command.

        // expect nil return. Side effect is to change the file
        	_, err := mark.run.env.Editor.Server.ExecuteCommand(mark.run.env.Ctx, final)
        if err != nil {

        mark.errorf("ExecuteCommand failed: %v", err)
        		return changes, err
        }
        return changes, nil
        ```
        Line 180, Patchset 3 (Latest): log.Printf("%v", got)
        Hongxiang Jiang . unresolved

        delete

        Line 193, Patchset 3 (Latest): switch kind {
        case "refactor.rewrite.removeTags":

        modifytags.Remove = mark.note.NamedArgs["form0"].(string)
        case "refactor.rewrite.addTags":

        modifytags.Add = mark.note.NamedArgs["form0"].(string)
        modifytags.Transform = mark.note.NamedArgs["form1"].(string)
        default:

        mark.errorf("unexpected kind %q", kind)
        return changes, err
        }
        Hongxiang Jiang . unresolved

        We should not have any code that aware of how the langauge server works, like the type of "ModifyTags".

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Peter Weinberger
        Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
        Gerrit-Change-Number: 748500
        Gerrit-PatchSet: 3
        Gerrit-Owner: Peter Weinberger <p...@google.com>
        Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
        Gerrit-Reviewer: Peter Weinberger <p...@google.com>
        Gerrit-Attention: Peter Weinberger <p...@google.com>
        Gerrit-Comment-Date: Wed, 04 Mar 2026 18:29:46 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Hongxiang Jiang (Gerrit)

        unread,
        Mar 8, 2026, 11:07:26 AM (12 days ago) Mar 8
        to Peter Weinberger, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
        Attention needed from Peter Weinberger

        Hongxiang Jiang added 1 comment

        File gopls/internal/test/marker/marker_test.go
        Line 108, Patchset 5 (Latest): tests, err := loadMarkerTests(dir, "dialog_")
        Hongxiang Jiang . unresolved

        I think this is not intentional, right?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Peter Weinberger
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
          Gerrit-Change-Number: 748500
          Gerrit-PatchSet: 5
          Gerrit-Owner: Peter Weinberger <p...@google.com>
          Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
          Gerrit-Reviewer: Peter Weinberger <p...@google.com>
          Gerrit-Attention: Peter Weinberger <p...@google.com>
          Gerrit-Comment-Date: Sun, 08 Mar 2026 15:07:17 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          unsatisfied_requirement
          open
          diffy

          Hongxiang Jiang (Gerrit)

          unread,
          Mar 8, 2026, 12:00:31 PM (12 days ago) Mar 8
          to Peter Weinberger, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
          Attention needed from Peter Weinberger

          Hongxiang Jiang added 1 comment

          File gopls/internal/test/marker/codeaction_test.go
          Line 214, Patchset 5 (Latest): mark.errorf("ExecuteCommand failed: %v", err)
          Hongxiang Jiang . unresolved

          the code action marker allow us to verify the error `@codeaction(... err=re'..')`

          the error can be a code action resolve error, or it can be a execute command error.

          instead of fail the test here, we need to let the caller at line 86 to verify if the error we encountered what expected or not.

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Peter Weinberger
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
          Gerrit-Change-Number: 748500
          Gerrit-PatchSet: 5
          Gerrit-Owner: Peter Weinberger <p...@google.com>
          Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
          Gerrit-Reviewer: Peter Weinberger <p...@google.com>
          Gerrit-Attention: Peter Weinberger <p...@google.com>
          Gerrit-Comment-Date: Sun, 08 Mar 2026 16:00:20 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          unsatisfied_requirement
          open
          diffy

          Peter Weinberger (Gerrit)

          unread,
          Mar 19, 2026, 3:54:40 PM (15 hours ago) Mar 19
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
          Attention needed from Peter Weinberger

          Peter Weinberger uploaded new patchset

          Peter Weinberger uploaded patch set #10 to this change.
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Peter Weinberger
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newpatchset
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
          Gerrit-Change-Number: 748500
          Gerrit-PatchSet: 10
          unsatisfied_requirement
          open
          diffy

          Peter Weinberger (Gerrit)

          unread,
          Mar 19, 2026, 4:28:56 PM (14 hours ago) Mar 19
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
          Attention needed from Peter Weinberger

          Peter Weinberger uploaded new patchset

          Peter Weinberger uploaded patch set #11 to this change.
          Following approvals got outdated and were removed:
          • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Peter Weinberger
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newpatchset
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
          Gerrit-Change-Number: 748500
          Gerrit-PatchSet: 11
          unsatisfied_requirement
          open
          diffy

          Peter Weinberger (Gerrit)

          unread,
          Mar 19, 2026, 8:43:56 PM (10 hours ago) Mar 19
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
          Attention needed from Peter Weinberger

          Peter Weinberger uploaded new patchset

          Peter Weinberger uploaded patch set #12 to this change.
          Following approvals got outdated and were removed:
          • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Peter Weinberger
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newpatchset
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I3037df9893551240c207ae34e41ea9c12013dc9e
          Gerrit-Change-Number: 748500
          Gerrit-PatchSet: 12
          unsatisfied_requirement
          open
          diffy
          Reply all
          Reply to author
          Forward
          0 new messages