diff --git a/README.md b/README.md index 5d3bd62b4..43d5b0975 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ The following options are currently available. | `enable_argument_placeholders` | `bool` | `true` | Whether to enable function argument placeholder completions | | `enable_ast_check_diagnostics` | `bool` | `true` | Whether to enable ast-check diagnostics | | `enable_build_on_save` | `bool` | `false` | Whether to enable build-on-save diagnostics | +| `build_on_save_step` | `[]const u8` | `"install"` | Select which step should be executed on build-on-save | | `enable_autofix` | `bool` | `false` | Whether to automatically fix errors on save. Currently supports adding and removing discards. | | `semantic_tokens` | `enum` | `.full` | Set level of semantic tokens. Partial only includes information that requires semantic analysis. | | `enable_inlay_hints` | `bool` | `true` | Enables inlay hint support when the client also supports it | diff --git a/schema.json b/schema.json index b34cdbadd..3294174a1 100644 --- a/schema.json +++ b/schema.json @@ -7,27 +7,32 @@ "enable_snippets": { "description": "Enables snippet completions when the client also supports them", "type": "boolean", - "default": "true" + "default": true }, "enable_argument_placeholders": { "description": "Whether to enable function argument placeholder completions", "type": "boolean", - "default": "true" + "default": true }, "enable_ast_check_diagnostics": { "description": "Whether to enable ast-check diagnostics", "type": "boolean", - "default": "true" + "default": true }, "enable_build_on_save": { "description": "Whether to enable build-on-save diagnostics", "type": "boolean", - "default": "false" + "default": false + }, + "build_on_save_step": { + "description": "Select which step should be executed on build-on-save", + "type": "string", + "default": "install" }, "enable_autofix": { "description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.", "type": "boolean", - "default": "false" + "default": false }, "semantic_tokens": { "description": "Set level of semantic tokens. Partial only includes information that requires semantic analysis.", @@ -42,112 +47,112 @@ "enable_inlay_hints": { "description": "Enables inlay hint support when the client also supports it", "type": "boolean", - "default": "true" + "default": true }, "inlay_hints_show_variable_type_hints": { "description": "Enable inlay hints for variable types", "type": "boolean", - "default": "true" + "default": true }, "inlay_hints_show_parameter_name": { "description": "Enable inlay hints for parameter names", "type": "boolean", - "default": "true" + "default": true }, "inlay_hints_show_builtin": { "description": "Enable inlay hints for builtin functions", "type": "boolean", - "default": "true" + "default": true }, "inlay_hints_exclude_single_argument": { "description": "Don't show inlay hints for single argument calls", "type": "boolean", - "default": "true" + "default": true }, "inlay_hints_hide_redundant_param_names": { "description": "Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)", "type": "boolean", - "default": "false" + "default": false }, "inlay_hints_hide_redundant_param_names_last_token": { "description": "Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo)", "type": "boolean", - "default": "false" + "default": false }, "warn_style": { "description": "Enables warnings for style guideline mismatches", "type": "boolean", - "default": "false" + "default": false }, "highlight_global_var_declarations": { "description": "Whether to highlight global var declarations", "type": "boolean", - "default": "false" + "default": false }, "dangerous_comptime_experiments_do_not_enable": { "description": "Whether to use the comptime interpreter", "type": "boolean", - "default": "false" + "default": false }, "skip_std_references": { "description": "When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is", "type": "boolean", - "default": "false" + "default": false }, "prefer_ast_check_as_child_process": { "description": "Can be used in conjuction with `enable_ast_check_diagnostics` to favor using `zig ast-check` instead of ZLS's fork", "type": "boolean", - "default": "true" + "default": true }, "record_session": { "description": "When true, zls will record all request is receives and write in into `record_session_path`, so that they can replayed with `zls replay`", "type": "boolean", - "default": "false" + "default": false }, "record_session_path": { "description": "Output file path when `record_session` is set. The recommended file extension *.zlsreplay", "type": "string", - "default": "null" + "default": null }, "replay_session_path": { "description": "Used when calling `zls replay` for specifying the replay file. If no extra argument is given `record_session_path` is used as the default path.", "type": "string", - "default": "null" + "default": null }, "builtin_path": { "description": "Path to 'builtin;' useful for debugging, automatically set if let null", "type": "string", - "default": "null" + "default": null }, "zig_lib_path": { "description": "Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports", "type": "string", - "default": "null" + "default": null }, "zig_exe_path": { "description": "Zig executable path, e.g. `/path/to/zig/zig`, used to run the custom build runner. If `null`, zig is looked up in `PATH`. Will be used to infer the zig standard library path if none is provided", "type": "string", - "default": "null" + "default": null }, "build_runner_path": { "description": "Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig`", "type": "string", - "default": "null" + "default": null }, "global_cache_path": { "description": "Path to a directory that will be used as zig's cache. null is equivalent to `${KnownFolders.Cache}/zls`", "type": "string", - "default": "null" + "default": null }, "build_runner_global_cache_path": { "description": "Path to a directory that will be used as the global cache path when executing a projects build.zig. null is equivalent to the path shown by `zig env`", "type": "string", - "default": "null" + "default": null }, "completions_with_replace": { "description": "Completions confirm behavior. If 'true', replace the text after the cursor", "type": "boolean", - "default": "true" + "default": true } } } diff --git a/src/Config.zig b/src/Config.zig index 06837f0de..ea3f1b499 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -16,6 +16,9 @@ enable_ast_check_diagnostics: bool = true, /// Whether to enable build-on-save diagnostics enable_build_on_save: bool = false, +/// Select which step should be executed on build-on-save +build_on_save_step: []const u8 = "install", + /// Whether to automatically fix errors on save. Currently supports adding and removing discards. enable_autofix: bool = false, diff --git a/src/Server.zig b/src/Server.zig index e9dc28f71..c10042468 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -850,18 +850,28 @@ pub fn updateConfiguration(server: *Server, new_config: configuration.Configurat inline for (std.meta.fields(Config)) |field| { if (@field(new_cfg, field.name)) |new_config_value| { - if (@TypeOf(new_config_value) == []const u8) { - if (@field(server.config, field.name) == null or - !std.mem.eql(u8, @field(server.config, field.name).?, new_config_value)) - { - log.info("set config option '{s}' to '{s}'", .{ field.name, new_config_value }); - @field(server.config, field.name) = try config_arena.dupe(u8, new_config_value); - } - } else { - if (@field(server.config, field.name) != new_config_value) { - log.info("set config option '{s}' to '{any}'", .{ field.name, new_config_value }); - @field(server.config, field.name) = new_config_value; - } + const old_config_value = @field(server.config, field.name); + switch (@TypeOf(old_config_value)) { + ?[]const u8 => { + const override_old_value = + if (old_config_value) |old_value| !std.mem.eql(u8, old_value, new_config_value) else true; + if (override_old_value) { + log.info("set config option '{s}' to '{s}'", .{ field.name, new_config_value }); + @field(server.config, field.name) = try config_arena.dupe(u8, new_config_value); + } + }, + []const u8 => { + if (!std.mem.eql(u8, old_config_value, new_config_value)) { + log.info("set config option '{s}' to '{s}'", .{ field.name, new_config_value }); + @field(server.config, field.name) = try config_arena.dupe(u8, new_config_value); + } + }, + else => { + if (old_config_value != new_config_value) { + log.info("set config option '{s}' to '{any}'", .{ field.name, new_config_value }); + @field(server.config, field.name) = new_config_value; + } + }, } } } diff --git a/src/config_gen/config.json b/src/config_gen/config.json index 405114122..f2fdd62ba 100644 --- a/src/config_gen/config.json +++ b/src/config_gen/config.json @@ -4,31 +4,37 @@ "name": "enable_snippets", "description": "Enables snippet completions when the client also supports them", "type": "bool", - "default": "true" + "default": true }, { "name": "enable_argument_placeholders", "description": "Whether to enable function argument placeholder completions", "type": "bool", - "default": "true" + "default": true }, { "name": "enable_ast_check_diagnostics", "description": "Whether to enable ast-check diagnostics", "type": "bool", - "default": "true" + "default": true }, { "name": "enable_build_on_save", "description": "Whether to enable build-on-save diagnostics", "type": "bool", - "default": "false" + "default": false + }, + { + "name": "build_on_save_step", + "description": "Select which step should be executed on build-on-save", + "type": "[]const u8", + "default": "install" }, { "name": "enable_autofix", "description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.", "type": "bool", - "default": "false" + "default": false }, { "name": "semantic_tokens", @@ -45,133 +51,133 @@ "name": "enable_inlay_hints", "description": "Enables inlay hint support when the client also supports it", "type": "bool", - "default": "true" + "default": true }, { "name": "inlay_hints_show_variable_type_hints", "description": "Enable inlay hints for variable types", "type": "bool", - "default": "true" + "default": true }, { "name": "inlay_hints_show_parameter_name", "description": "Enable inlay hints for parameter names", "type": "bool", - "default": "true" + "default": true }, { "name": "inlay_hints_show_builtin", "description": "Enable inlay hints for builtin functions", "type": "bool", - "default": "true" + "default": true }, { "name": "inlay_hints_exclude_single_argument", "description": "Don't show inlay hints for single argument calls", "type": "bool", - "default": "true" + "default": true }, { "name": "inlay_hints_hide_redundant_param_names", "description": "Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)", "type": "bool", - "default": "false" + "default": false }, { "name": "inlay_hints_hide_redundant_param_names_last_token", "description": "Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo)", "type": "bool", - "default": "false" + "default": false }, { "name": "warn_style", "description": "Enables warnings for style guideline mismatches", "type": "bool", - "default": "false" + "default": false }, { "name": "highlight_global_var_declarations", "description": "Whether to highlight global var declarations", "type": "bool", - "default": "false" + "default": false }, { "name": "dangerous_comptime_experiments_do_not_enable", "description": "Whether to use the comptime interpreter", "type": "bool", - "default": "false" + "default": false }, { "name": "skip_std_references", "description": "When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is", "type": "bool", - "default": "false" + "default": false }, { "name": "prefer_ast_check_as_child_process", "description": "Can be used in conjuction with `enable_ast_check_diagnostics` to favor using `zig ast-check` instead of ZLS's fork", "type": "bool", - "default": "true" + "default": true }, { "name": "record_session", "description": "When true, zls will record all request is receives and write in into `record_session_path`, so that they can replayed with `zls replay`", "type": "bool", - "default": "false" + "default": false }, { "name": "record_session_path", "description": "Output file path when `record_session` is set. The recommended file extension *.zlsreplay", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "replay_session_path", "description": "Used when calling `zls replay` for specifying the replay file. If no extra argument is given `record_session_path` is used as the default path.", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "builtin_path", "description": "Path to 'builtin;' useful for debugging, automatically set if let null", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "zig_lib_path", "description": "Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "zig_exe_path", "description": "Zig executable path, e.g. `/path/to/zig/zig`, used to run the custom build runner. If `null`, zig is looked up in `PATH`. Will be used to infer the zig standard library path if none is provided", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "build_runner_path", "description": "Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig`", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "global_cache_path", "description": "Path to a directory that will be used as zig's cache. null is equivalent to `${KnownFolders.Cache}/zls`", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "build_runner_global_cache_path", "description": "Path to a directory that will be used as the global cache path when executing a projects build.zig. null is equivalent to the path shown by `zig env`", "type": "?[]const u8", - "default": "null" + "default": null }, { "name": "completions_with_replace", "description": "Completions confirm behavior. If 'true', replace the text after the cursor", "type": "bool", - "default": "true" + "default": true } ] } diff --git a/src/config_gen/config_gen.zig b/src/config_gen/config_gen.zig index 6246a7bbb..9824e7fe0 100644 --- a/src/config_gen/config_gen.zig +++ b/src/config_gen/config_gen.zig @@ -13,10 +13,12 @@ const ConfigOption = struct { /// If this is set, the value `type` should to be `enum` @"enum": ?[]const []const u8 = null, /// used in Config.zig as the default initializer - default: []const u8, + default: std.json.Value, fn getTypescriptType(self: ConfigOption) error{UnsupportedType}![]const u8 { - return if (std.mem.eql(u8, self.type, "?[]const u8")) + return if (std.mem.eql(u8, self.type, "[]const u8")) + "string" + else if (std.mem.eql(u8, self.type, "?[]const u8")) "string" else if (std.mem.eql(u8, self.type, "bool")) "boolean" @@ -61,10 +63,10 @@ const ConfigOption = struct { _ = options; if (fmt.len != 0) return std.fmt.invalidFmtError(fmt, ConfigOption); if (config.@"enum" != null) { - try writer.writeByte('.'); + try writer.print(".{s}", .{config.default.string}); + return; } - const default = std.mem.trim(u8, config.default, &std.ascii.whitespace); - try writer.writeAll(default); + try std.json.stringify(config.default, .{}, writer); } fn fmtDefaultValue(self: ConfigOption) std.fmt.Formatter(formatDefaultValue) { @@ -88,7 +90,7 @@ const SchemaEntry = struct { description: []const u8, type: []const u8, @"enum": ?[]const []const u8 = null, - default: []const u8, + default: std.json.Value, }; fn generateConfigFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void { @@ -248,14 +250,7 @@ fn generateVSCodeConfigFile(allocator: std.mem.Allocator, config: Config, path: const name = try snakeCaseToCamelCase(allocator, snake_case_name); errdefer allocator.free(name); - const default: ?std.json.Value = blk: { - if (option.@"enum" != null) break :blk .{ .string = option.default }; - - var value = try std.json.parseFromSlice(std.json.Value, allocator, option.default, .{}); - defer value.deinit(); - - break :blk if (value.value != .null) value.value else null; - }; + const default: ?std.json.Value = if (option.default != .null) option.default else null; configuration.map.putAssumeCapacityNoClobber(name, .{ .type = try option.getTypescriptType(), @@ -999,7 +994,7 @@ pub fn main() !void { \\ \\ --help Prints this message \\ --readme-path [path] Update readme file (see README.md) - \\ --generate-vscode-config [path] Output zls-vscode configurations + \\ --vscode-config-path [path] Output zls-vscode configurations \\ --generate-config-path [path] Output path to config file (see src/Config.zig) \\ --generate-schema-path [path] Output json schema file (see schema.json) \\ --generate-version-data [version] Output version data file (default: master) @@ -1073,7 +1068,7 @@ pub fn main() !void { if (vscode_config_path) |output_path| { try generateVSCodeConfigFile(gpa, config, output_path); try stderr.writeAll( - \\Changing configuration options may also require editing the `package.json` from zls-vscode at https://github.com/zigtools/zls-vscode/blob/master/package.json + \\Changing configuration options may also require editing the `package.json` from ziglang/vscode-zig at https://github.com/ziglang/vscode-zig/blob/master/package.json \\You can use `zig build gen -- --vscode-config-path /path/to/output/file.json` to generate the new configuration properties which you can then copy into `package.json` \\ ); diff --git a/src/features/diagnostics.zig b/src/features/diagnostics.zig index a6a2fd8fb..54f02f0fd 100644 --- a/src/features/diagnostics.zig +++ b/src/features/diagnostics.zig @@ -225,6 +225,7 @@ pub fn generateBuildOnSaveDiagnostics( const base_args = &[_][]const u8{ server.config.zig_exe_path orelse return, "build", + server.config.build_on_save_step, "--zig-lib-dir", server.config.zig_lib_path orelse return, "--cache-dir",