Skip to content

Commit 05ea40b

Browse files
committed
make autofix configurable through in-editor configuration
The `force_autofix` will not work on an editor that supports code actions.
1 parent a66692e commit 05ea40b

File tree

7 files changed

+43
-39
lines changed

7 files changed

+43
-39
lines changed

schema.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@
2727
},
2828
"default": []
2929
},
30-
"enable_autofix": {
31-
"description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.",
32-
"type": "boolean",
33-
"default": false
34-
},
3530
"semantic_tokens": {
3631
"description": "Set level of semantic tokens. `partial` only includes information that requires semantic analysis.",
3732
"type": "string",
@@ -77,6 +72,11 @@
7772
"type": "boolean",
7873
"default": false
7974
},
75+
"force_autofix": {
76+
"description": "Work around editors that do not support 'source.fixall' code actions on save. This option may delivered a substandard user experience. Please refer to the installation guide to see which editors natively support code actions on save.",
77+
"type": "boolean",
78+
"default": false
79+
},
8080
"warn_style": {
8181
"description": "Enables warnings for style guideline mismatches",
8282
"type": "boolean",

src/Config.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ enable_build_on_save: ?bool = null,
1818
/// If the `build.zig` has declared a 'check' step, it will be preferred over the default 'install' step.
1919
build_on_save_args: []const []const u8 = &.{},
2020

21-
/// Whether to automatically fix errors on save. Currently supports adding and removing discards.
22-
enable_autofix: bool = false,
23-
2421
/// Set level of semantic tokens. `partial` only includes information that requires semantic analysis.
2522
semantic_tokens: enum {
2623
none,
@@ -49,6 +46,9 @@ inlay_hints_hide_redundant_param_names: bool = false,
4946
/// Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo)
5047
inlay_hints_hide_redundant_param_names_last_token: bool = false,
5148

49+
/// Work around editors that do not support 'source.fixall' code actions on save. This option may delivered a substandard user experience. Please refer to the installation guide to see which editors natively support code actions on save.
50+
force_autofix: bool = false,
51+
5252
/// Enables warnings for style guideline mismatches
5353
warn_style: bool = false,
5454

src/Server.zig

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -332,15 +332,19 @@ fn initAnalyser(server: *Server, handle: ?*DocumentStore.Handle) Analyser {
332332
);
333333
}
334334

335-
fn getAutofixMode(server: *Server) enum {
336-
on_save,
335+
pub fn getAutofixMode(server: *Server) enum {
336+
/// Autofix is implemented by providing `source.fixall` code actions.
337+
@"source.fixall",
338+
/// Autofix is implemented using `textDocument/willSaveWaitUntil`.
339+
/// Requires `force_autofix` to be enabled.
337340
will_save_wait_until,
338-
fixall,
341+
/// Autofix is implemented by send a `workspace/applyEdit` request after receiving a `textDocument/didSave` notification.
342+
/// Requires `force_autofix` to be enabled.
343+
on_save,
339344
none,
340345
} {
341-
if (!server.config.enable_autofix) return .none;
342-
// TODO https://github.com/zigtools/zls/issues/1093
343-
// if (server.client_capabilities.supports_code_action_fixall) return .fixall;
346+
if (server.client_capabilities.supports_code_action_fixall) return .@"source.fixall";
347+
if (!server.config.force_autofix) return .none;
344348
if (server.client_capabilities.supports_apply_edits) {
345349
if (server.client_capabilities.supports_will_save_wait_until) return .will_save_wait_until;
346350
return .on_save;
@@ -397,12 +401,12 @@ fn initializeHandler(server: *Server, arena: std.mem.Allocator, request: types.I
397401

398402
if (std.mem.eql(u8, clientInfo.name, "Sublime Text LSP")) {
399403
server.client_capabilities.max_detail_length = 256;
400-
// TODO investigate why fixall doesn't work in sublime text
404+
} else if (std.mem.startsWith(u8, clientInfo.name, "emacs")) {
405+
// Assumes that `emacs` means `emacs-lsp/lsp-mode`. Eglot uses `Eglot`.
406+
407+
// https://github.com/emacs-lsp/lsp-mode/issues/1842
401408
server.client_capabilities.supports_code_action_fixall = false;
402409
skip_set_fixall = true;
403-
} else if (std.mem.eql(u8, clientInfo.name, "Visual Studio Code")) {
404-
server.client_capabilities.supports_code_action_fixall = true;
405-
skip_set_fixall = true;
406410
}
407411
}
408412

@@ -479,16 +483,12 @@ fn initializeHandler(server: *Server, arena: std.mem.Allocator, request: types.I
479483
server.client_capabilities.supports_will_save = synchronization.willSave orelse false;
480484
server.client_capabilities.supports_will_save_wait_until = synchronization.willSaveWaitUntil orelse false;
481485
}
482-
if (textDocument.codeAction) |codeaction| {
483-
if (codeaction.codeActionLiteralSupport) |literalSupport| {
484-
if (!skip_set_fixall) {
485-
for (literalSupport.codeActionKind.valueSet) |code_action_kind| {
486-
if (code_action_kind.eql(.@"source.fixAll")) {
487-
server.client_capabilities.supports_code_action_fixall = true;
488-
break;
489-
}
490-
}
491-
}
486+
if (textDocument.codeAction) |_| {
487+
if (!skip_set_fixall) {
488+
// Some clients do not specify `source.fixAll` in
489+
// `textDocument.codeAction.?.codeActionLiteralSupport.?.codeActionKind.valueSet`
490+
// so we assume they support it if they support code actions in general.
491+
server.client_capabilities.supports_code_action_fixall = true;
492492
}
493493
}
494494
if (textDocument.definition) |definition| {
@@ -531,6 +531,7 @@ fn initializeHandler(server: *Server, arena: std.mem.Allocator, request: types.I
531531
if (request.clientInfo) |clientInfo| {
532532
log.info("Client Info: {s}-{s}", .{ clientInfo.name, clientInfo.version orelse "<no version>" });
533533
}
534+
log.info("Autofix Mode: {s}", .{@tagName(server.getAutofixMode())});
534535
log.debug("Offset Encoding: {s}", .{@tagName(server.offset_encoding)});
535536

536537
if (request.workspaceFolders) |workspace_folders| {
@@ -879,6 +880,7 @@ pub fn updateConfiguration(
879880
const new_build_runner_path =
880881
new_config.build_runner_path != null and
881882
(server.config.build_runner_path == null or !std.mem.eql(u8, server.config.build_runner_path.?, new_config.build_runner_path.?));
883+
const new_force_autofix = new_config.force_autofix != null and server.config.force_autofix != new_config.force_autofix.?;
882884

883885
inline for (std.meta.fields(Config)) |field| {
884886
if (@field(new_cfg, field.name)) |new_value| {
@@ -1013,8 +1015,10 @@ pub fn updateConfiguration(
10131015
}
10141016
}
10151017

1016-
if (server.config.enable_autofix and server.getAutofixMode() == .none) {
1017-
log.warn("`enable_autofix` is ignored because it is not supported by {s}", .{server.client_capabilities.client_name orelse "your editor"});
1018+
if (server.config.force_autofix and server.getAutofixMode() == .none) {
1019+
log.warn("`force_autofix` is ignored because it is not supported by {s}", .{server.client_capabilities.client_name orelse "your editor"});
1020+
} else if (new_force_autofix) {
1021+
log.info("Autofix Mode: {s}", .{@tagName(server.getAutofixMode())});
10181022
}
10191023
}
10201024

src/features/diagnostics.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: *D
5353
}
5454
}
5555

56-
if (server.config.enable_autofix and tree.mode == .zig) {
56+
if (server.getAutofixMode() != .none and tree.mode == .zig) {
5757
try code_actions.collectAutoDiscardDiagnostics(tree, arena, &diagnostics, server.offset_encoding);
5858
}
5959

src/tools/config.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@
2424
"type": "[]const []const u8",
2525
"default": []
2626
},
27-
{
28-
"name": "enable_autofix",
29-
"description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.",
30-
"type": "bool",
31-
"default": false
32-
},
3327
{
3428
"name": "semantic_tokens",
3529
"description": "Set level of semantic tokens. `partial` only includes information that requires semantic analysis.",
@@ -83,6 +77,12 @@
8377
"type": "bool",
8478
"default": false
8579
},
80+
{
81+
"name": "force_autofix",
82+
"description": "Work around editors that do not support 'source.fixall' code actions on save. This option may delivered a substandard user experience. Please refer to the installation guide to see which editors natively support code actions on save.",
83+
"type": "bool",
84+
"default": false
85+
},
8686
{
8787
"name": "warn_style",
8888
"description": "Enables warnings for style guideline mismatches",

src/tools/config_gen.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ fn generateVSCodeConfigFile(allocator: std.mem.Allocator, config: Config, path:
261261
});
262262

263263
for (config.options) |option| {
264-
if (std.mem.eql(u8, option.name, "zig_exe_path")) continue;
264+
if (std.mem.eql(u8, option.name, "zig_exe_path")) continue; // vscode-zig has its own option for this
265+
if (std.mem.eql(u8, option.name, "force_autofix")) continue; // VS Code supports code actions on save without a workaround
265266

266267
const snake_case_name = try std.fmt.allocPrint(allocator, "zig.zls.{s}", .{option.name});
267268
defer allocator.free(snake_case_name);

tests/lsp_features/code_actions.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,6 @@ fn testDiagnostic(
663663
) !void {
664664
var ctx = try Context.init();
665665
defer ctx.deinit();
666-
ctx.server.config.enable_autofix = true;
667666
ctx.server.config.prefer_ast_check_as_child_process = !options.want_zir;
668667

669668
const uri = try ctx.addDocument(.{ .source = before });

0 commit comments

Comments
 (0)