From 9badc745c5ea395ab85abde71b408d908990ba0d Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:54:13 +0000 Subject: [PATCH] remove setup wizard (#878) * remove setup wizard * add back findZig function --- README.md | 7 +- src/config_gen/config.json | 67 ++++------ src/config_gen/config_gen.zig | 3 - src/configuration.zig | 37 +++++- src/main.zig | 41 +++--- src/setup.zig | 230 ---------------------------------- 6 files changed, 81 insertions(+), 304 deletions(-) delete mode 100644 src/setup.zig diff --git a/README.md b/README.md index e1f9e4e..88e1f06 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Building `zls` is very easy. You will need [a build of Zig master](https://zigla git clone --recurse-submodules https://github.com/zigtools/zls cd zls zig build -Drelease-safe -./zig-out/bin/zls --config # Configure ZLS ``` #### Build Options @@ -59,8 +58,10 @@ There is also a `generate-data.js` in the `src/data` folder, you'll need to run ### Configuration Options -You can configure zls by running `zls --config` or manually creating your own `zls.json` configuration file. -zls will look for a zls.json configuration file in multiple locations with the following priority: +You can configure zls by editing your `zls.json` configuration file. +Running `zls --show-config-path` will a path to an already existing `zls.json` or a path to the local configuration folder instead. + +zls will look for a `zls.json` configuration file in multiple locations with the following priority: - In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders/blob/master/RESOURCES.md#folder-list)) - In the global configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders/blob/master/RESOURCES.md#folder-list)) diff --git a/src/config_gen/config.json b/src/config_gen/config.json index ed21934..f032d33 100644 --- a/src/config_gen/config.json +++ b/src/config_gen/config.json @@ -5,175 +5,150 @@ "description": "Enables snippet completions when the client also supports them", "type": "bool", "default": "true", - "setup_question": "Do you want to enable snippets?" }, { "name": "enable_ast_check_diagnostics", "description": "Whether to enable ast-check diagnostics", "type": "bool", - "default": "true", - "setup_question": "Do you want to enable ast-check diagnostics?" + "default": "true" }, { "name": "enable_autofix", "description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.", "type": "bool", "default": "true", - "setup_question": "Do you want to zls to automatically try to fix errors on save? (supports adding & removing discards)" }, { "name": "enable_import_embedfile_argument_completions", "description": "Whether to enable import/embedFile argument completions", "type": "bool", "default": "true", - "setup_question": "Do you want to enable @import/@embedFile argument path completion?" }, { "name": "enable_semantic_tokens", "description": "Enables semantic token support when the client also supports it", "type": "bool", - "default": "true", - "setup_question": "Do you want to enable semantic highlighting?" + "default": "true" }, { "name": "enable_inlay_hints", "description": "Enables inlay hint support when the client also supports it", "type": "bool", "default": "true", - "setup_question": "Do you want to enable inlay hints?" }, { "name": "inlay_hints_show_builtin", "description": "Enable inlay hints for builtin functions", "type": "bool", - "default": "true", - "setup_question": null + "default": "true" }, { "name": "inlay_hints_exclude_single_argument", "description": "Don't show inlay hints for single argument calls", "type": "bool", - "default": "true", - "setup_question": null + "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", - "setup_question": null + "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", - "setup_question": null + "default": "false" }, { "name": "operator_completions", "description": "Enables `*` and `?` operators in completion lists", "type": "bool", - "default": "true", - "setup_question": "Do you want to enable .* and .? completions?" + "default": "true" }, { "name": "warn_style", "description": "Enables warnings for style guideline mismatches", "type": "bool", - "default": "false", - "setup_question": "Do you want to enable style warnings?" + "default": "false" }, { "name": "highlight_global_var_declarations", "description": "Whether to highlight global var declarations", "type": "bool", - "default": "false", - "setup_question": null + "default": "false" }, { "name": "use_comptime_interpreter", "description": "Whether to use the comptime interpreter", "type": "bool", - "default": "false", - "setup_question": null + "default": "false" }, { "name": "include_at_in_builtins", "description": "Whether the @ sign should be part of the completion of builtins", "type": "bool", - "default": "false", - "setup_question": null + "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", - "setup_question": null + "default": "false" }, { "name": "max_detail_length", "description": "The detail field of completions is truncated to be no longer than this (in bytes)", "type": "usize", - "default": "1048576", - "setup_question": null + "default": "1048576" }, { "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", - "setup_question": null + "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", - "setup_question": 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", - "setup_question": null + "default": "null" }, { "name": "builtin_path", "description": "Path to 'builtin;' useful for debugging, automatically set if let null", "type": "?[]const u8", - "default": "null", - "setup_question": 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", - "setup_question": 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", - "setup_question": 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", - "setup_question": null + "default": "null" }, { "name": "global_cache_path", "description": "Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`", "type": "?[]const u8", - "default": "null", - "setup_question": null + "default": "null" } ] } \ No newline at end of file diff --git a/src/config_gen/config_gen.zig b/src/config_gen/config_gen.zig index 73f9d4d..bb3249d 100644 --- a/src/config_gen/config_gen.zig +++ b/src/config_gen/config_gen.zig @@ -11,9 +11,6 @@ const ConfigOption = struct { type: []const u8, /// used in Config.zig as the default initializer default: []const u8, - /// If set, this option can be configured through `zls --config` - /// currently unused but could later be used to automatically generate queries for setup.zig - setup_question: ?[]const u8, }; const Config = struct { diff --git a/src/configuration.zig b/src/configuration.zig index 8006941..ebab2e8 100644 --- a/src/configuration.zig +++ b/src/configuration.zig @@ -1,6 +1,6 @@ const std = @import("std"); +const builtin = @import("builtin"); -const setup = @import("setup.zig"); const tracy = @import("tracy.zig"); const known_folders = @import("known-folders"); @@ -65,7 +65,7 @@ pub fn configChanged(config: *Config, allocator: std.mem.Allocator, builtin_crea logger.debug("zig path `{s}` is not absolute, will look in path", .{exe_path}); allocator.free(exe_path); } - config.zig_exe_path = try setup.findZig(allocator); + config.zig_exe_path = try findZig(allocator); } if (config.zig_exe_path) |exe_path| blk: { @@ -208,3 +208,36 @@ fn getConfigurationType() type { config_info.Struct.decls = &.{}; return @Type(config_info); } + +pub fn findZig(allocator: std.mem.Allocator) !?[]const u8 { + const env_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) { + error.EnvironmentVariableNotFound => { + return null; + }, + else => return err, + }; + defer allocator.free(env_path); + + const exe_extension = builtin.target.exeFileExt(); + const zig_exe = try std.fmt.allocPrint(allocator, "zig{s}", .{exe_extension}); + defer allocator.free(zig_exe); + + var it = std.mem.tokenize(u8, env_path, &[_]u8{std.fs.path.delimiter}); + while (it.next()) |path| { + if (builtin.os.tag == .windows) { + if (std.mem.indexOfScalar(u8, path, '/') != null) continue; + } + const full_path = try std.fs.path.join(allocator, &[_][]const u8{ path, zig_exe }); + defer allocator.free(full_path); + + if (!std.fs.path.isAbsolute(full_path)) continue; + + const file = std.fs.openFileAbsolute(full_path, .{}) catch continue; + defer file.close(); + const stat = file.stat() catch continue; + if (stat.kind == .Directory) continue; + + return try allocator.dupe(u8, full_path); + } + return null; +} \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 4bb1075..e4f3407 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,6 @@ const known_folders = @import("known-folders"); const Config = @import("Config.zig"); const configuration = @import("configuration.zig"); const Server = @import("Server.zig"); -const setup = @import("setup.zig"); const Header = @import("Header.zig"); const debug = @import("debug.zig"); @@ -204,7 +203,6 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult { const ArgId = enum { help, version, - config, replay, @"enable-debug-log", @"show-config-path", @@ -229,7 +227,6 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult { var cmd_infos: InfoMap = InfoMap.init(.{ .help = "Prints this message.", .version = "Prints the compiler version with which the server was compiled.", - .config = "Run the ZLS configuration wizard.", .replay = "Replay a previous recorded zls session", .@"enable-debug-log" = "Enables debug logs.", .@"config-path" = "Specify the path to a configuration file specifying LSP behaviour.", @@ -274,7 +271,11 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult { specified.set(id, true); switch (id) { - .help, .version, .@"enable-debug-log", .config, .@"show-config-path" => {}, + .help, + .version, + .@"enable-debug-log", + .@"show-config-path", + => {}, .@"config-path" => { const path = args_it.next() orelse { try stderr.print("Expected configuration file path after --config-path argument.\n", .{}); @@ -298,10 +299,6 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult { try stdout.writeAll(build_options.version ++ "\n"); return result; } - if (specified.get(.config)) { - try setup.wizard(allocator); - return result; - } if (specified.get(.@"enable-debug-log")) { actual_log_level = .debug; logger.info("Enabled debug logging.\n", .{}); @@ -314,15 +311,19 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult { defer if (new_config.config_path) |path| allocator.free(path); defer std.json.parseFree(Config, new_config.config, .{ .allocator = allocator }); - if (new_config.config_path) |path| { - const full_path = try std.fs.path.resolve(allocator, &.{ path, "zls.json" }); - defer allocator.free(full_path); - - try stdout.writeAll(full_path); - try stdout.writeByte('\n'); - } else { - logger.err("Failed to find zls.json!\n", .{}); - } + const full_path = if (new_config.config_path) |path| blk: { + break :blk try std.fs.path.resolve(allocator, &.{ path, "zls.json" }); + } else blk: { + const local_config_path = try known_folders.getPath(allocator, .local_configuration) orelse { + logger.err("failed to find local configuration folder", .{}); + return result; + }; + defer allocator.free(local_config_path); + break :blk try std.fs.path.resolve(allocator, &.{ local_config_path, "zls.json" }); + }; + defer allocator.free(full_path); + try stdout.writeAll(full_path); + try stdout.writeByte('\n'); return result; } @@ -341,9 +342,9 @@ pub fn main() !void { var tracy_state = if (tracy.enable_allocation) tracy.tracyAllocator(gpa_state.allocator()) else void{}; const inner_allocator: std.mem.Allocator = if (tracy.enable_allocation) tracy_state.allocator() else gpa_state.allocator(); - - var failing_allocator_state = if(build_options.enable_failing_allocator) debug.FailingAllocator.init(inner_allocator, build_options.enable_failing_allocator_likelihood) else void{}; - const allocator: std.mem.Allocator = if(build_options.enable_failing_allocator) failing_allocator_state.allocator() else inner_allocator; + + var failing_allocator_state = if (build_options.enable_failing_allocator) debug.FailingAllocator.init(inner_allocator, build_options.enable_failing_allocator_likelihood) else void{}; + const allocator: std.mem.Allocator = if (build_options.enable_failing_allocator) failing_allocator_state.allocator() else inner_allocator; const result = try parseArgs(allocator); defer if (result.config_path) |path| allocator.free(path); diff --git a/src/setup.zig b/src/setup.zig deleted file mode 100644 index 6085a03..0000000 --- a/src/setup.zig +++ /dev/null @@ -1,230 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const known_folders = @import("known-folders"); - -/// Caller must free memory. -pub fn askString(allocator: std.mem.Allocator, prompt: []const u8, max_size: usize) ![]u8 { - const in = std.io.getStdIn().reader(); - const out = std.io.getStdOut().writer(); - - try out.print("? {s}", .{prompt}); - - const result = try in.readUntilDelimiterAlloc(allocator, '\n', max_size); - return if (std.mem.endsWith(u8, result, "\r")) result[0..(result.len - 1)] else result; -} - -/// Caller must free memory. Max size is recommended to be a high value, like 512. -pub fn askDirPath(allocator: std.mem.Allocator, prompt: []const u8, max_size: usize) ![]u8 { - const out = std.io.getStdOut().writer(); - - while (true) { - const path = try askString(allocator, prompt, max_size); - if (!std.fs.path.isAbsolute(path)) { - try out.writeAll("Error: Invalid directory, please try again.\n\n"); - allocator.free(path); - continue; - } - - var dir = std.fs.cwd().openDir(path, std.fs.Dir.OpenDirOptions{}) catch { - try out.writeAll("Error: Invalid directory, please try again.\n\n"); - allocator.free(path); - continue; - }; - - dir.close(); - return path; - } -} - -pub fn askBool(prompt: []const u8) !bool { - const in = std.io.getStdIn().reader(); - const out = std.io.getStdOut().writer(); - - var buffer: [1]u8 = undefined; - - while (true) { - try out.print("? {s} (y/n) > ", .{prompt}); - - const read = in.read(&buffer) catch continue; - try in.skipUntilDelimiterOrEof('\n'); - - if (read == 0) return error.EndOfStream; - - switch (buffer[0]) { - 'y' => return true, - 'n' => return false, - else => continue, - } - } -} - -pub fn askSelectOne(prompt: []const u8, comptime Options: type) !Options { - const in = std.io.getStdIn().reader(); - const out = std.io.getStdOut().writer(); - - try out.print("? {s} (select one)\n\n", .{prompt}); - - comptime var max_size: usize = 0; - inline for (@typeInfo(Options).Enum.fields) |option| { - try out.print(" - {s}\n", .{option.name}); - if (option.name.len > max_size) max_size = option.name.len; - } - - while (true) { - var buffer: [max_size + 1]u8 = undefined; - - try out.writeAll("\n> "); - - var result = (in.readUntilDelimiterOrEof(&buffer, '\n') catch { - try in.skipUntilDelimiterOrEof('\n'); - try out.writeAll("Error: Invalid option, please try again.\n"); - continue; - }) orelse return error.EndOfStream; - result = if (std.mem.endsWith(u8, result, "\r")) result[0..(result.len - 1)] else result; - - inline for (@typeInfo(Options).Enum.fields) |option| - if (std.ascii.eqlIgnoreCase(option.name, result)) - return @intToEnum(Options, option.value); - - try out.writeAll("Error: Invalid option, please try again.\n"); - } -} - -pub fn wizard(allocator: std.mem.Allocator) !void { - @setEvalBranchQuota(2500); - const stdout = std.io.getStdOut().writer(); - - try stdout.writeAll( - \\Welcome to the ZLS configuration wizard! - \\ * - \\ |\ - \\ /* \ - \\ | *\ - \\ _/_*___|_ x - \\ | @ @ / - \\ @ \ / - \\ \__-/ / - \\ - \\ - ); - - var local_path = known_folders.getPath(allocator, .local_configuration) catch null; - var global_path = known_folders.getPath(allocator, .global_configuration) catch null; - defer if (local_path) |d| allocator.free(d); - defer if (global_path) |d| allocator.free(d); - - const can_access_global = blk: { - std.fs.accessAbsolute(global_path orelse break :blk false, .{}) catch break :blk false; - break :blk true; - }; - - if (global_path == null and local_path == null) { - try stdout.writeAll("Could not open a global or local config directory.\n"); - return; - } - var config_path: []const u8 = undefined; - if (can_access_global and try askBool("Should this configuration be system-wide?")) { - config_path = global_path.?; - } else { - if (local_path) |p| { - config_path = p; - } else { - try stdout.writeAll("Could not find a local config directory.\n"); - return; - } - } - var dir = std.fs.cwd().openDir(config_path, .{}) catch |err| { - try stdout.print("Could not open {s}: {}.\n", .{ config_path, err }); - return; - }; - defer dir.close(); - var file = dir.createFile("zls.json", .{}) catch |err| { - try stdout.print("Could not create {s}/zls.json: {}.\n", .{ config_path, err }); - return; - }; - defer file.close(); - const out = file.writer(); - - var zig_exe_path = try findZig(allocator); - defer if (zig_exe_path) |p| allocator.free(p); - - if (zig_exe_path) |path| { - try stdout.print("Found zig executable '{s}' in PATH.\n", .{path}); - } else { - try stdout.writeAll("Could not find 'zig' in PATH\n"); - zig_exe_path = try askString(allocator, if (builtin.os.tag == .windows) - \\What is the path to the 'zig' executable you would like to use? - \\Note that due to a bug in zig (https://github.com/ziglang/zig/issues/6044), - \\your zig directory cannot contain the '/' character. - else - "What is the path to the 'zig' executable you would like to use?", std.fs.MAX_PATH_BYTES); - } - - const snippets = try askBool("Do you want to enable snippets?"); - const ast_check = try askBool("Do you want to enable ast-check diagnostics?"); - const autofix = try askBool("Do you want to zls to automatically try to fix errors on save? (supports adding & removing discards)"); - const ief_apc = try askBool("Do you want to enable @import/@embedFile argument path completion?"); - const style = try askBool("Do you want to enable style warnings?"); - const semantic_tokens = try askBool("Do you want to enable semantic highlighting?"); - const inlay_hints = try askBool("Do you want to enable inlay hints?"); - const operator_completions = try askBool("Do you want to enable .* and .? completions?"); - - std.debug.print("Writing config to {s}/zls.json ... ", .{config_path}); - - try std.json.stringify(.{ - .@"$schema" = "https://raw.githubusercontent.com/zigtools/zls/master/schema.json", - .zig_exe_path = zig_exe_path, - .enable_snippets = snippets, - .enable_ast_check_diagnostics = ast_check, - .enable_autofix = autofix, - .enable_import_embedfile_argument_completions = ief_apc, - .warn_style = style, - .enable_semantic_tokens = semantic_tokens, - .enable_inlay_hints = inlay_hints, - .operator_completions = operator_completions, - }, .{ - .whitespace = .{}, - }, out); - - try stdout.writeAll( - \\successful. - \\ - \\You can find information on how to setup zls for your editor on zigtools.github.io/install-zls/ - \\ - \\Thank you for choosing ZLS! - \\ - ); -} - -pub fn findZig(allocator: std.mem.Allocator) !?[]const u8 { - const env_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) { - error.EnvironmentVariableNotFound => { - return null; - }, - else => return err, - }; - defer allocator.free(env_path); - - const exe_extension = builtin.target.exeFileExt(); - const zig_exe = try std.fmt.allocPrint(allocator, "zig{s}", .{exe_extension}); - defer allocator.free(zig_exe); - - var it = std.mem.tokenize(u8, env_path, &[_]u8{std.fs.path.delimiter}); - while (it.next()) |path| { - if (builtin.os.tag == .windows) { - if (std.mem.indexOfScalar(u8, path, '/') != null) continue; - } - const full_path = try std.fs.path.join(allocator, &[_][]const u8{ path, zig_exe }); - defer allocator.free(full_path); - - if (!std.fs.path.isAbsolute(full_path)) continue; - - const file = std.fs.openFileAbsolute(full_path, .{}) catch continue; - defer file.close(); - const stat = file.stat() catch continue; - if (stat.kind == .Directory) continue; - - return try allocator.dupe(u8, full_path); - } - return null; -}