@@ -16,6 +16,7 @@ pub const Builder = struct {
1616 analyser : * Analyser ,
1717 handle : * DocumentStore.Handle ,
1818 offset_encoding : offsets.Encoding ,
19+ only_kinds : ? std .EnumSet (std .meta .Tag (types .CodeActionKind )),
1920
2021 pub fn generateCodeAction (
2122 builder : * Builder ,
@@ -27,6 +28,8 @@ pub const Builder = struct {
2728
2829 var remove_capture_actions : std .AutoHashMapUnmanaged (types.Range , void ) = .{};
2930
31+ try handleUnorganizedImport (builder , actions );
32+
3033 if (error_bundle .errorMessageCount () == 0 ) return ; // `getMessages` can't be called on an empty ErrorBundle
3134 for (error_bundle .getMessages ()) | msg_index | {
3235 const err = error_bundle .getErrorMessage (msg_index );
@@ -66,11 +69,10 @@ pub const Builder = struct {
6669 }
6770 }
6871
69- pub fn generateOrganizeImportsAction (
70- builder : * Builder ,
71- actions : * std .ArrayListUnmanaged (types.CodeAction ),
72- ) error {OutOfMemory }! void {
73- try handleUnorganizedImport (builder , actions );
72+ /// Returns `false` if the client explicitly specified that they are not interested in this code action kind.
73+ fn wantKind (builder : * Builder , kind : std .meta .Tag (types.CodeActionKind )) bool {
74+ const only_kinds = builder .only_kinds orelse return true ;
75+ return only_kinds .contains (kind );
7476 }
7577
7678 pub fn createTextEditLoc (self : * Builder , loc : offsets.Loc , new_text : []const u8 ) types.TextEdit {
@@ -106,6 +108,9 @@ pub fn collectAutoDiscardDiagnostics(
106108 diagnostics : * std .ArrayListUnmanaged (types.Diagnostic ),
107109 offset_encoding : offsets.Encoding ,
108110) error {OutOfMemory }! void {
111+ const tracy_zone = tracy .trace (@src ());
112+ defer tracy_zone .end ();
113+
109114 const token_tags = tree .tokens .items (.tag );
110115 const token_starts = tree .tokens .items (.start );
111116
@@ -145,6 +150,11 @@ pub fn collectAutoDiscardDiagnostics(
145150}
146151
147152fn handleNonCamelcaseFunction (builder : * Builder , actions : * std .ArrayListUnmanaged (types.CodeAction ), loc : offsets .Loc ) ! void {
153+ const tracy_zone = tracy .trace (@src ());
154+ defer tracy_zone .end ();
155+
156+ if (! builder .wantKind (.quickfix )) return ;
157+
148158 const identifier_name = offsets .locToSlice (builder .handle .tree .source , loc );
149159
150160 if (std .mem .allEqual (u8 , identifier_name , '_' )) return ;
@@ -165,6 +175,8 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman
165175 const tracy_zone = tracy .trace (@src ());
166176 defer tracy_zone .end ();
167177
178+ if (! builder .wantKind (.@"source.fixAll" ) and ! builder .wantKind (.quickfix )) return ;
179+
168180 const identifier_name = offsets .locToSlice (builder .handle .tree .source , loc );
169181
170182 const tree = builder .handle .tree ;
@@ -213,28 +225,34 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman
213225 const add_suffix_newline = is_last_param and token_tags [insert_token + 1 ] == .r_brace and tree .tokensOnSameLine (insert_token , insert_token + 1 );
214226 const insert_index , const new_text = try createDiscardText (builder , identifier_name , insert_token , true , add_suffix_newline );
215227
216- const action1 = types.CodeAction {
217- .title = "discard function parameter" ,
218- .kind = .@"source.fixAll" ,
219- .isPreferred = true ,
220- .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditPos (insert_index , new_text )}),
221- };
228+ try actions .ensureUnusedCapacity (builder .arena , 2 );
222229
223- // TODO fix formatting
224- const action2 = types.CodeAction {
225- .title = "remove function parameter" ,
226- .kind = .quickfix ,
227- .isPreferred = false ,
228- .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditLoc (getParamRemovalRange (tree , fn_proto_param ), "" )}),
229- };
230+ if (builder .wantKind (.@"source.fixAll" )) {
231+ actions .insertAssumeCapacity (0 , .{
232+ .title = "discard function parameter" ,
233+ .kind = .@"source.fixAll" ,
234+ .isPreferred = true ,
235+ .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditPos (insert_index , new_text )}),
236+ });
237+ }
230238
231- try actions .insertSlice (builder .arena , 0 , &.{ action1 , action2 });
239+ if (builder .wantKind (.quickfix )) {
240+ // TODO fix formatting
241+ actions .appendAssumeCapacity (.{
242+ .title = "remove function parameter" ,
243+ .kind = .quickfix ,
244+ .isPreferred = false ,
245+ .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditLoc (getParamRemovalRange (tree , fn_proto_param ), "" )}),
246+ });
247+ }
232248}
233249
234250fn handleUnusedVariableOrConstant (builder : * Builder , actions : * std .ArrayListUnmanaged (types.CodeAction ), loc : offsets .Loc ) ! void {
235251 const tracy_zone = tracy .trace (@src ());
236252 defer tracy_zone .end ();
237253
254+ if (! builder .wantKind (.@"source.fixAll" )) return ;
255+
238256 const identifier_name = offsets .locToSlice (builder .handle .tree .source , loc );
239257
240258 const tree = builder .handle .tree ;
@@ -276,11 +294,40 @@ fn handleUnusedCapture(
276294 const tracy_zone = tracy .trace (@src ());
277295 defer tracy_zone .end ();
278296
297+ if (! builder .wantKind (.@"source.fixAll" ) and ! builder .wantKind (.quickfix )) return ;
298+
279299 const tree = builder .handle .tree ;
280300 const token_tags = tree .tokens .items (.tag );
281301
282302 const source = tree .source ;
283- const capture_loc = getCaptureLoc (source , loc ) orelse return ;
303+
304+ try actions .ensureUnusedCapacity (builder .arena , 3 );
305+
306+ if (builder .wantKind (.quickfix )) {
307+ const capture_loc = getCaptureLoc (source , loc ) orelse return ;
308+
309+ const remove_cap_loc = builder .createTextEditLoc (capture_loc , "" );
310+ actions .appendAssumeCapacity (.{
311+ .title = "discard capture name" ,
312+ .kind = .quickfix ,
313+ .isPreferred = false ,
314+ .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditLoc (loc , "_" )}),
315+ });
316+
317+ // prevent adding duplicate 'remove capture' action.
318+ // search for a matching action by comparing ranges.
319+ const gop = try remove_capture_actions .getOrPut (builder .arena , remove_cap_loc .range );
320+ if (! gop .found_existing ) {
321+ actions .appendAssumeCapacity (.{
322+ .title = "remove capture" ,
323+ .kind = .quickfix ,
324+ .isPreferred = false ,
325+ .edit = try builder .createWorkspaceEdit (&.{remove_cap_loc }),
326+ });
327+ }
328+ }
329+
330+ if (! builder .wantKind (.@"source.fixAll" )) return ;
284331
285332 const identifier_token = offsets .sourceIndexToTokenIndex (tree , loc .start );
286333 if (token_tags [identifier_token ] != .identifier ) return ;
@@ -328,42 +375,22 @@ fn handleUnusedCapture(
328375 // if we are on the last capture of the block, we need to add an additional newline
329376 // i.e |a, b| { ... } -> |a, b| { ... \n_ = a; \n_ = b;\n }
330377 const add_suffix_newline = is_last_capture and token_tags [insert_token + 1 ] == .r_brace and tree .tokensOnSameLine (insert_token , insert_token + 1 );
331-
332378 const insert_index , const new_text = try createDiscardText (builder , identifier_name , insert_token , true , add_suffix_newline );
333- const action1 : types.CodeAction = .{
379+
380+ actions .insertAssumeCapacity (0 , .{
334381 .title = "discard capture" ,
335382 .kind = .@"source.fixAll" ,
336383 .isPreferred = true ,
337384 .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditPos (insert_index , new_text )}),
338- };
339- const action2 : types.CodeAction = .{
340- .title = "discard capture name" ,
341- .kind = .quickfix ,
342- .isPreferred = false ,
343- .edit = try builder .createWorkspaceEdit (&.{builder .createTextEditLoc (loc , "_" )}),
344- };
345-
346- // prevent adding duplicate 'remove capture' action.
347- // search for a matching action by comparing ranges.
348- const remove_cap_loc = builder .createTextEditLoc (capture_loc , "" );
349- const gop = try remove_capture_actions .getOrPut (builder .arena , remove_cap_loc .range );
350- if (gop .found_existing )
351- try actions .insertSlice (builder .arena , 0 , &.{ action1 , action2 })
352- else {
353- const action0 = types.CodeAction {
354- .title = "remove capture" ,
355- .kind = .quickfix ,
356- .isPreferred = false ,
357- .edit = try builder .createWorkspaceEdit (&.{remove_cap_loc }),
358- };
359- try actions .insertSlice (builder .arena , 0 , &.{ action0 , action1 , action2 });
360- }
385+ });
361386}
362387
363388fn handlePointlessDiscard (builder : * Builder , actions : * std .ArrayListUnmanaged (types.CodeAction ), loc : offsets .Loc ) ! void {
364389 const tracy_zone = tracy .trace (@src ());
365390 defer tracy_zone .end ();
366391
392+ if (! builder .wantKind (.@"source.fixAll" )) return ;
393+
367394 const edit_loc = getDiscardLoc (builder .handle .tree .source , loc ) orelse return ;
368395
369396 try actions .append (builder .arena , .{
@@ -377,6 +404,11 @@ fn handlePointlessDiscard(builder: *Builder, actions: *std.ArrayListUnmanaged(ty
377404}
378405
379406fn handleVariableNeverMutated (builder : * Builder , actions : * std .ArrayListUnmanaged (types.CodeAction ), loc : offsets .Loc ) ! void {
407+ const tracy_zone = tracy .trace (@src ());
408+ defer tracy_zone .end ();
409+
410+ if (! builder .wantKind (.quickfix )) return ;
411+
380412 const source = builder .handle .tree .source ;
381413
382414 const var_keyword_end = 1 + (std .mem .lastIndexOfNone (u8 , source [0.. loc .start ], & std .ascii .whitespace ) orelse return );
@@ -399,6 +431,11 @@ fn handleVariableNeverMutated(builder: *Builder, actions: *std.ArrayListUnmanage
399431}
400432
401433fn handleUnorganizedImport (builder : * Builder , actions : * std .ArrayListUnmanaged (types.CodeAction )) ! void {
434+ const tracy_zone = tracy .trace (@src ());
435+ defer tracy_zone .end ();
436+
437+ if (! builder .wantKind (.@"source.organizeImports" )) return ;
438+
402439 const tree = builder .handle .tree ;
403440 if (tree .errors .len != 0 ) return ;
404441
0 commit comments