@@ -588,7 +588,7 @@ var valueMarkerFuncs = map[string]func(marker){
588588// See doc.go for marker documentation.
589589var actionMarkerFuncs = map [string ]func (marker ){
590590 "acceptcompletion" : actionMarkerFunc (acceptCompletionMarker ),
591- "codeaction" : actionMarkerFunc (codeActionMarker , "end" , "result" , "edit" , "err" ),
591+ "codeaction" : actionMarkerFunc (codeActionMarker , "end" , "diag" , "action" , " result" , "edit" , "err" ),
592592 "codelenses" : actionMarkerFunc (codeLensesMarker ),
593593 "complete" : actionMarkerFunc (completeMarker ),
594594 "def" : actionMarkerFunc (defMarker ),
@@ -2168,7 +2168,7 @@ func changedFiles(env *integration.Env, changes []protocol.DocumentChange) (map[
21682168}
21692169
21702170func codeActionMarker (mark marker , loc protocol.Location , kind string ) {
2171- if ! exactlyOneNamedArg (mark , "edit" , "result" , "err" ) {
2171+ if ! exactlyOneNamedArg (mark , "action" , " edit" , "result" , "err" ) {
21722172 return
21732173 }
21742174
@@ -2181,14 +2181,61 @@ func codeActionMarker(mark marker, loc protocol.Location, kind string) {
21812181 }
21822182
21832183 var (
2184- edit = namedArg (mark , "edit" , expect .Identifier ("" ))
2185- result = namedArg (mark , "result" , expect .Identifier ("" ))
2186- wantErr = namedArgFunc (mark , "err" , convertStringMatcher , stringMatcher {})
2184+ edit = namedArg (mark , "edit" , expect .Identifier ("" ))
2185+ result = namedArg (mark , "result" , expect .Identifier ("" ))
2186+ wantAction = namedArg (mark , "action" , expect .Identifier ("" ))
2187+ wantErr = namedArgFunc (mark , "err" , convertStringMatcher , stringMatcher {})
21872188 )
21882189
2189- changed , err := codeAction (mark .run .env , loc .URI , loc .Range , protocol .CodeActionKind (kind ), nil )
2190- if err != nil && wantErr .empty () {
2191- mark .errorf ("codeAction failed: %v" , err )
2190+ var diag * protocol.Diagnostic
2191+ if re := namedArg (mark , "diag" , (* regexp .Regexp )(nil )); re != nil {
2192+ d , ok := removeDiagnostic (mark , loc , false , re )
2193+ if ! ok {
2194+ mark .errorf ("no diagnostic at %v matches %q" , loc , re )
2195+ return
2196+ }
2197+ diag = & d
2198+ }
2199+
2200+ action , err := resolveCodeAction (mark .run .env , loc .URI , loc .Range , protocol .CodeActionKind (kind ), diag )
2201+ if err != nil {
2202+ if ! wantErr .empty () {
2203+ wantErr .checkErr (mark , err )
2204+ } else {
2205+ mark .errorf ("resolveCodeAction failed: %v" , err )
2206+ }
2207+ return
2208+ }
2209+
2210+ // If when 'action' is set, we simply compare the action, and don't apply it.
2211+ if wantAction != "" {
2212+ g := mark .getGolden (wantAction )
2213+ if action == nil {
2214+ mark .errorf ("no matching codeAction" )
2215+ return
2216+ }
2217+ data , err := json .MarshalIndent (action , "" , "\t " )
2218+ if err != nil {
2219+ mark .errorf ("failed to marshal codeaction: %v" , err )
2220+ return
2221+ }
2222+ data = bytes .ReplaceAll (data , []byte (mark .run .env .Sandbox .Workdir .RootURI ()), []byte ("$WORKDIR" ))
2223+ compareGolden (mark , data , g )
2224+ return
2225+ }
2226+
2227+ changes , err := applyCodeAction (mark .run .env , action )
2228+ if err != nil {
2229+ if ! wantErr .empty () {
2230+ wantErr .checkErr (mark , err )
2231+ } else {
2232+ mark .errorf ("codeAction failed: %v" , err )
2233+ }
2234+ return
2235+ }
2236+ changed , err := changedFiles (mark .run .env , changes )
2237+ if err != nil {
2238+ mark .errorf ("changedFiles failed: %v" , err )
21922239 return
21932240 }
21942241
@@ -2319,29 +2366,20 @@ func quickfixErrMarker(mark marker, loc protocol.Location, re *regexp.Regexp, wa
23192366// applied. Currently, this function does not support code actions that return
23202367// edits directly; it only supports code action commands.
23212368func codeAction (env * integration.Env , uri protocol.DocumentURI , rng protocol.Range , kind protocol.CodeActionKind , diag * protocol.Diagnostic ) (map [string ][]byte , error ) {
2322- changes , err := codeActionChanges (env , uri , rng , kind , diag )
2369+ action , err := resolveCodeAction (env , uri , rng , kind , diag )
2370+ if err != nil {
2371+ return nil , err
2372+ }
2373+ changes , err := applyCodeAction (env , action )
23232374 if err != nil {
23242375 return nil , err
23252376 }
23262377 return changedFiles (env , changes )
23272378}
23282379
2329- // codeActionChanges executes a textDocument/codeAction request for the
2330- // specified location and kind, and captures the resulting document changes.
2331- // If diag is non-nil, it is used as the code action context.
2332- func codeActionChanges (env * integration.Env , uri protocol.DocumentURI , rng protocol.Range , kind protocol.CodeActionKind , diag * protocol.Diagnostic ) ([]protocol.DocumentChange , error ) {
2333- // Collect any server-initiated changes created by workspace/applyEdit.
2334- //
2335- // We set up this handler immediately, not right before executing the code
2336- // action command, so we can assert that neither the codeAction request nor
2337- // codeAction resolve request cause edits as a side effect (golang/go#71405).
2338- var changes []protocol.DocumentChange
2339- restore := env .Editor .Client ().SetApplyEditHandler (func (ctx context.Context , wsedit * protocol.WorkspaceEdit ) error {
2340- changes = append (changes , wsedit .DocumentChanges ... )
2341- return nil
2342- })
2343- defer restore ()
2344-
2380+ // resolveCodeAction resolves the code action specified by the given location,
2381+ // kind, and diagnostic.
2382+ func resolveCodeAction (env * integration.Env , uri protocol.DocumentURI , rng protocol.Range , kind protocol.CodeActionKind , diag * protocol.Diagnostic ) (* protocol.CodeAction , error ) {
23452383 // Request all code actions that apply to the diagnostic.
23462384 // A production client would set Only=[kind],
23472385 // but we can give a better error if we don't filter.
@@ -2379,16 +2417,6 @@ func codeActionChanges(env *integration.Env, uri protocol.DocumentURI, rng proto
23792417 }
23802418 action := candidates [0 ]
23812419
2382- // Apply the codeAction.
2383- //
2384- // Spec:
2385- // "If a code action provides an edit and a command, first the edit is
2386- // executed and then the command."
2387- // An action may specify an edit and/or a command, to be
2388- // applied in that order. But since applyDocumentChanges(env,
2389- // action.Edit.DocumentChanges) doesn't compose, for now we
2390- // assert that actions return one or the other.
2391-
23922420 // Resolve code action edits first if the client has resolve support
23932421 // and the code action has no edits.
23942422 if action .Edit == nil {
@@ -2401,10 +2429,41 @@ func codeActionChanges(env *integration.Env, uri protocol.DocumentURI, rng proto
24012429 if err != nil {
24022430 return nil , err
24032431 }
2404- action . Edit = resolved . Edit
2432+ action = * resolved
24052433 }
24062434 }
24072435
2436+ return & action , nil
2437+ }
2438+
2439+ // applyCodeAction applies the given code action, and captures the resulting
2440+ // document changes.
2441+ func applyCodeAction (env * integration.Env , action * protocol.CodeAction ) ([]protocol.DocumentChange , error ) {
2442+ // Collect any server-initiated changes created by workspace/applyEdit.
2443+ //
2444+ // We set up this handler immediately, not right before executing the code
2445+ // action command, so we can assert that neither the codeAction request nor
2446+ // codeAction resolve request cause edits as a side effect (golang/go#71405).
2447+ var changes []protocol.DocumentChange
2448+ restore := env .Editor .Client ().SetApplyEditHandler (func (ctx context.Context , wsedit * protocol.WorkspaceEdit ) error {
2449+ changes = append (changes , wsedit .DocumentChanges ... )
2450+ return nil
2451+ })
2452+ defer restore ()
2453+
2454+ if action .Edit == nil && action .Command == nil {
2455+ panic ("bad action" )
2456+ }
2457+
2458+ // Apply the codeAction.
2459+ //
2460+ // Spec:
2461+ // "If a code action provides an edit and a command, first the edit is
2462+ // executed and then the command."
2463+ // An action may specify an edit and/or a command, to be
2464+ // applied in that order. But since applyDocumentChanges(env,
2465+ // action.Edit.DocumentChanges) doesn't compose, for now we
2466+ // assert that actions return one or the other.
24082467 if action .Edit != nil {
24092468 if len (action .Edit .Changes ) > 0 {
24102469 env .TB .Errorf ("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes" , action .Kind , action .Title )
0 commit comments