this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal/lsp: add toggle surrounding struct braces code action

Add a third brace-manipulation code action, "Toggle surrounding struct
braces", alongside the existing "Add..." and "Remove..." primitives.
The toggle commits to the 0 <-> 1 brace cycle so it can be bound to a
single keystroke: if the cursor is inside removable braces it applies
the remove edit; otherwise it applies the add edit. The two primitives
remain available for users who want to deepen nesting explicitly.

Fixes #4333.

Signed-off-by: Paul Jolly <paul@myitcv.io>
Change-Id: I8f616c2a08505caa3e59503e971b4609523266bb
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1236070
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

authored by

Paul Jolly and committed by
Matthew Sackman
cb4bd6a3 99c9c5cd

+115 -9
+73
cmd/cue/cmd/integration/workspace/codeaction_test.go
··· 278 278 }) 279 279 } 280 280 } 281 + 282 + func TestCodeActionToggleStructBraces(t *testing.T) { 283 + type testCase struct { 284 + name string 285 + input string 286 + position protocol.Position 287 + expected string 288 + } 289 + testCases := []testCase{ 290 + { 291 + // Inside removable braces, toggle removes. 292 + name: "remove", 293 + input: ` 294 + foo: { 295 + bar: "baz" 296 + } 297 + `[1:], 298 + position: protocol.Position{Line: 1, Character: 1}, // bar 299 + expected: ` 300 + foo: bar: "baz" 301 + `[1:], 302 + }, 303 + { 304 + // No removable braces at the cursor: toggle adds. 305 + name: "add", 306 + input: `foo: bar: "baz"`, 307 + position: protocol.Position{Line: 0, Character: 5}, // bar 308 + expected: ` 309 + foo: { 310 + bar: "baz" 311 + } 312 + `[1:], 313 + }, 314 + } 315 + 316 + for _, tc := range testCases { 317 + t.Run(tc.name, func(t *testing.T) { 318 + WithOptions(RootURIAsDefaultFolder()).Run(t, "-- input.cue --\n"+tc.input, func(t *testing.T, env *Env) { 319 + env.OpenFile("input.cue") 320 + env.Await(env.DoneWithOpen()) 321 + rootURI := env.Sandbox.Workdir.RootURI() 322 + 323 + cursor := protocol.Location{ 324 + URI: rootURI + "/input.cue", 325 + Range: protocol.Range{ 326 + Start: tc.position, 327 + }, 328 + } 329 + 330 + actions, err := env.Editor.CodeAction(env.Ctx, cursor, nil) 331 + if err != nil { 332 + qt.Assert(t, qt.IsNil(err)) 333 + } 334 + 335 + var action protocol.CodeAction 336 + found := slices.ContainsFunc(actions, func(a protocol.CodeAction) bool { 337 + if a.Title == "Toggle surrounding struct braces" { 338 + action = a 339 + return true 340 + } 341 + return false 342 + }) 343 + if !found { 344 + t.Fatal("Failed to find ToggleStructBraces code action") 345 + } 346 + 347 + env.ApplyCodeAction(action) 348 + after := env.BufferText("input.cue") 349 + qt.Check(t, qt.Equals(after, tc.expected)) 350 + }) 351 + }) 352 + } 353 + }
+3 -2
internal/golangorgx/gopls/protocol/codeactionkind.go
··· 6 6 7 7 // Custom code actions that aren't explicitly stated in LSP 8 8 const ( 9 - RefactorRewriteConvertToStruct CodeActionKind = "refactor.rewrite.convertToStruct" 10 - RefactorRewriteConvertFromStruct CodeActionKind = "refactor.rewrite.convertFromStruct" 9 + RefactorRewriteConvertToStruct CodeActionKind = "refactor.rewrite.convertToStruct" 10 + RefactorRewriteConvertFromStruct CodeActionKind = "refactor.rewrite.convertFromStruct" 11 + RefactorRewriteConvertToFromStruct CodeActionKind = "refactor.rewrite.convertToFromStruct" 11 12 )
+3 -2
internal/golangorgx/gopls/settings/default.go
··· 38 38 ServerOptions: ServerOptions{ 39 39 SupportedCodeActions: map[file.Kind]map[protocol.CodeActionKind]bool{ 40 40 file.CUE: { 41 - protocol.RefactorRewriteConvertToStruct: true, 42 - protocol.RefactorRewriteConvertFromStruct: true, 41 + protocol.RefactorRewriteConvertToStruct: true, 42 + protocol.RefactorRewriteConvertFromStruct: true, 43 + protocol.RefactorRewriteConvertToFromStruct: true, 43 44 }, 44 45 }, 45 46 SupportedCommands: []string{ExternalValidateCommand},
+36 -5
internal/lsp/server/codeaction.go
··· 15 15 package server 16 16 17 17 import ( 18 + "cmp" 18 19 "context" 19 20 "encoding/json" 20 21 "slices" ··· 80 81 action := protocol.CodeAction{ 81 82 Title: "Remove surrounding struct braces", 82 83 Kind: protocol.RefactorRewriteConvertFromStruct, 83 - // Mark it preferred so that if both "Add..." and "Remove..." 84 - // are available, then this "Remove..." action will be 85 - // prioritised by editors. This most likely matches the 86 - // user's needs. 84 + } 85 + if delayEdit { 86 + action.Data = &raw 87 + } else { 88 + action.Edit = convertFromStructEdit 89 + } 90 + codeActions = append(codeActions, action) 91 + } 92 + 93 + // "Toggle surrounding struct braces" exists so it can be bound to a 94 + // single keystroke for the 0 <-> 1 brace cycle. If "Remove..." is 95 + // available at the cursor it is used; otherwise "Add..." is used. 96 + // The two primitives above remain available so users can still 97 + // deepen nesting explicitly. 98 + if toggleEdit := cmp.Or(convertFromStructEdit, convertToStructEdit); toggleEdit != nil { 99 + action := protocol.CodeAction{ 100 + Title: "Toggle surrounding struct braces", 101 + Kind: protocol.RefactorRewriteConvertToFromStruct, 87 102 IsPreferred: true, 88 103 } 89 104 if delayEdit { 90 105 action.Data = &raw 91 106 } else { 92 - action.Edit = convertFromStructEdit 107 + action.Edit = toggleEdit 93 108 } 94 109 codeActions = append(codeActions, action) 95 110 } ··· 139 154 return nil, err 140 155 } 141 156 action.Edit = convertFromStructEdit 157 + return action, nil 158 + 159 + case protocol.RefactorRewriteConvertToFromStruct: 160 + convertFromStructEdit, err := s.workspace.CodeActionConvertFromStruct(ctx, &params, false) 161 + if err != nil { 162 + return nil, err 163 + } 164 + if convertFromStructEdit != nil { 165 + action.Edit = convertFromStructEdit 166 + return action, nil 167 + } 168 + convertToStructEdit, err := s.workspace.CodeActionConvertToStruct(ctx, &params, false) 169 + if err != nil { 170 + return nil, err 171 + } 172 + action.Edit = convertToStructEdit 142 173 return action, nil 143 174 144 175 case protocol.SourceOrganizeImports: