remove setup wizard (#878)
* remove setup wizard * add back findZig function
This commit is contained in:
parent
b95d5095af
commit
9badc745c5
@ -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
|
git clone --recurse-submodules https://github.com/zigtools/zls
|
||||||
cd zls
|
cd zls
|
||||||
zig build -Drelease-safe
|
zig build -Drelease-safe
|
||||||
./zig-out/bin/zls --config # Configure ZLS
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Build Options
|
#### 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
|
### Configuration Options
|
||||||
|
|
||||||
You can configure zls by running `zls --config` or manually creating your own `zls.json` configuration file.
|
You can configure zls by editing your `zls.json` configuration file.
|
||||||
zls will look for a zls.json configuration file in multiple locations with the following priority:
|
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 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))
|
- 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))
|
||||||
|
|
||||||
|
@ -5,175 +5,150 @@
|
|||||||
"description": "Enables snippet completions when the client also supports them",
|
"description": "Enables snippet completions when the client also supports them",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"setup_question": "Do you want to enable snippets?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "enable_ast_check_diagnostics",
|
"name": "enable_ast_check_diagnostics",
|
||||||
"description": "Whether to enable ast-check diagnostics",
|
"description": "Whether to enable ast-check diagnostics",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true"
|
||||||
"setup_question": "Do you want to enable ast-check diagnostics?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "enable_autofix",
|
"name": "enable_autofix",
|
||||||
"description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.",
|
"description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"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",
|
"name": "enable_import_embedfile_argument_completions",
|
||||||
"description": "Whether to enable import/embedFile argument completions",
|
"description": "Whether to enable import/embedFile argument completions",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"setup_question": "Do you want to enable @import/@embedFile argument path completion?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "enable_semantic_tokens",
|
"name": "enable_semantic_tokens",
|
||||||
"description": "Enables semantic token support when the client also supports it",
|
"description": "Enables semantic token support when the client also supports it",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true"
|
||||||
"setup_question": "Do you want to enable semantic highlighting?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "enable_inlay_hints",
|
"name": "enable_inlay_hints",
|
||||||
"description": "Enables inlay hint support when the client also supports it",
|
"description": "Enables inlay hint support when the client also supports it",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"setup_question": "Do you want to enable inlay hints?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inlay_hints_show_builtin",
|
"name": "inlay_hints_show_builtin",
|
||||||
"description": "Enable inlay hints for builtin functions",
|
"description": "Enable inlay hints for builtin functions",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inlay_hints_exclude_single_argument",
|
"name": "inlay_hints_exclude_single_argument",
|
||||||
"description": "Don't show inlay hints for single argument calls",
|
"description": "Don't show inlay hints for single argument calls",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inlay_hints_hide_redundant_param_names",
|
"name": "inlay_hints_hide_redundant_param_names",
|
||||||
"description": "Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)",
|
"description": "Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inlay_hints_hide_redundant_param_names_last_token",
|
"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)",
|
"description": "Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo)",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "operator_completions",
|
"name": "operator_completions",
|
||||||
"description": "Enables `*` and `?` operators in completion lists",
|
"description": "Enables `*` and `?` operators in completion lists",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "true",
|
"default": "true"
|
||||||
"setup_question": "Do you want to enable .* and .? completions?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "warn_style",
|
"name": "warn_style",
|
||||||
"description": "Enables warnings for style guideline mismatches",
|
"description": "Enables warnings for style guideline mismatches",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": "Do you want to enable style warnings?"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "highlight_global_var_declarations",
|
"name": "highlight_global_var_declarations",
|
||||||
"description": "Whether to highlight global var declarations",
|
"description": "Whether to highlight global var declarations",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "use_comptime_interpreter",
|
"name": "use_comptime_interpreter",
|
||||||
"description": "Whether to use the comptime interpreter",
|
"description": "Whether to use the comptime interpreter",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "include_at_in_builtins",
|
"name": "include_at_in_builtins",
|
||||||
"description": "Whether the @ sign should be part of the completion of builtins",
|
"description": "Whether the @ sign should be part of the completion of builtins",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "skip_std_references",
|
"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",
|
"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",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "max_detail_length",
|
"name": "max_detail_length",
|
||||||
"description": "The detail field of completions is truncated to be no longer than this (in bytes)",
|
"description": "The detail field of completions is truncated to be no longer than this (in bytes)",
|
||||||
"type": "usize",
|
"type": "usize",
|
||||||
"default": "1048576",
|
"default": "1048576"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "record_session",
|
"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`",
|
"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",
|
"type": "bool",
|
||||||
"default": "false",
|
"default": "false"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "record_session_path",
|
"name": "record_session_path",
|
||||||
"description": "Output file path when `record_session` is set. The recommended file extension *.zlsreplay",
|
"description": "Output file path when `record_session` is set. The recommended file extension *.zlsreplay",
|
||||||
"type": "?[]const u8",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "replay_session_path",
|
"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.",
|
"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",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "builtin_path",
|
"name": "builtin_path",
|
||||||
"description": "Path to 'builtin;' useful for debugging, automatically set if let null",
|
"description": "Path to 'builtin;' useful for debugging, automatically set if let null",
|
||||||
"type": "?[]const u8",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "zig_lib_path",
|
"name": "zig_lib_path",
|
||||||
"description": "Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports",
|
"description": "Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports",
|
||||||
"type": "?[]const u8",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "zig_exe_path",
|
"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",
|
"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",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "build_runner_path",
|
"name": "build_runner_path",
|
||||||
"description": "Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig`",
|
"description": "Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig`",
|
||||||
"type": "?[]const u8",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "global_cache_path",
|
"name": "global_cache_path",
|
||||||
"description": "Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`",
|
"description": "Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`",
|
||||||
"type": "?[]const u8",
|
"type": "?[]const u8",
|
||||||
"default": "null",
|
"default": "null"
|
||||||
"setup_question": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -11,9 +11,6 @@ const ConfigOption = struct {
|
|||||||
type: []const u8,
|
type: []const u8,
|
||||||
/// used in Config.zig as the default initializer
|
/// used in Config.zig as the default initializer
|
||||||
default: []const u8,
|
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 {
|
const Config = struct {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const setup = @import("setup.zig");
|
|
||||||
const tracy = @import("tracy.zig");
|
const tracy = @import("tracy.zig");
|
||||||
const known_folders = @import("known-folders");
|
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});
|
logger.debug("zig path `{s}` is not absolute, will look in path", .{exe_path});
|
||||||
allocator.free(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: {
|
if (config.zig_exe_path) |exe_path| blk: {
|
||||||
@ -208,3 +208,36 @@ fn getConfigurationType() type {
|
|||||||
config_info.Struct.decls = &.{};
|
config_info.Struct.decls = &.{};
|
||||||
return @Type(config_info);
|
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;
|
||||||
|
}
|
39
src/main.zig
39
src/main.zig
@ -6,7 +6,6 @@ const known_folders = @import("known-folders");
|
|||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const configuration = @import("configuration.zig");
|
const configuration = @import("configuration.zig");
|
||||||
const Server = @import("Server.zig");
|
const Server = @import("Server.zig");
|
||||||
const setup = @import("setup.zig");
|
|
||||||
const Header = @import("Header.zig");
|
const Header = @import("Header.zig");
|
||||||
const debug = @import("debug.zig");
|
const debug = @import("debug.zig");
|
||||||
|
|
||||||
@ -204,7 +203,6 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult {
|
|||||||
const ArgId = enum {
|
const ArgId = enum {
|
||||||
help,
|
help,
|
||||||
version,
|
version,
|
||||||
config,
|
|
||||||
replay,
|
replay,
|
||||||
@"enable-debug-log",
|
@"enable-debug-log",
|
||||||
@"show-config-path",
|
@"show-config-path",
|
||||||
@ -229,7 +227,6 @@ fn parseArgs(allocator: std.mem.Allocator) !ParseArgsResult {
|
|||||||
var cmd_infos: InfoMap = InfoMap.init(.{
|
var cmd_infos: InfoMap = InfoMap.init(.{
|
||||||
.help = "Prints this message.",
|
.help = "Prints this message.",
|
||||||
.version = "Prints the compiler version with which the server was compiled.",
|
.version = "Prints the compiler version with which the server was compiled.",
|
||||||
.config = "Run the ZLS configuration wizard.",
|
|
||||||
.replay = "Replay a previous recorded zls session",
|
.replay = "Replay a previous recorded zls session",
|
||||||
.@"enable-debug-log" = "Enables debug logs.",
|
.@"enable-debug-log" = "Enables debug logs.",
|
||||||
.@"config-path" = "Specify the path to a configuration file specifying LSP behaviour.",
|
.@"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);
|
specified.set(id, true);
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
.help, .version, .@"enable-debug-log", .config, .@"show-config-path" => {},
|
.help,
|
||||||
|
.version,
|
||||||
|
.@"enable-debug-log",
|
||||||
|
.@"show-config-path",
|
||||||
|
=> {},
|
||||||
.@"config-path" => {
|
.@"config-path" => {
|
||||||
const path = args_it.next() orelse {
|
const path = args_it.next() orelse {
|
||||||
try stderr.print("Expected configuration file path after --config-path argument.\n", .{});
|
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");
|
try stdout.writeAll(build_options.version ++ "\n");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (specified.get(.config)) {
|
|
||||||
try setup.wizard(allocator);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (specified.get(.@"enable-debug-log")) {
|
if (specified.get(.@"enable-debug-log")) {
|
||||||
actual_log_level = .debug;
|
actual_log_level = .debug;
|
||||||
logger.info("Enabled debug logging.\n", .{});
|
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 if (new_config.config_path) |path| allocator.free(path);
|
||||||
defer std.json.parseFree(Config, new_config.config, .{ .allocator = allocator });
|
defer std.json.parseFree(Config, new_config.config, .{ .allocator = allocator });
|
||||||
|
|
||||||
if (new_config.config_path) |path| {
|
const full_path = if (new_config.config_path) |path| blk: {
|
||||||
const full_path = try std.fs.path.resolve(allocator, &.{ path, "zls.json" });
|
break :blk try std.fs.path.resolve(allocator, &.{ path, "zls.json" });
|
||||||
defer allocator.free(full_path);
|
} else blk: {
|
||||||
|
const local_config_path = try known_folders.getPath(allocator, .local_configuration) orelse {
|
||||||
try stdout.writeAll(full_path);
|
logger.err("failed to find local configuration folder", .{});
|
||||||
try stdout.writeByte('\n');
|
return result;
|
||||||
} else {
|
};
|
||||||
logger.err("Failed to find zls.json!\n", .{});
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,8 +343,8 @@ pub fn main() !void {
|
|||||||
var tracy_state = if (tracy.enable_allocation) tracy.tracyAllocator(gpa_state.allocator()) else 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();
|
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{};
|
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 allocator: std.mem.Allocator = if (build_options.enable_failing_allocator) failing_allocator_state.allocator() else inner_allocator;
|
||||||
|
|
||||||
const result = try parseArgs(allocator);
|
const result = try parseArgs(allocator);
|
||||||
defer if (result.config_path) |path| allocator.free(path);
|
defer if (result.config_path) |path| allocator.free(path);
|
||||||
|
230
src/setup.zig
230
src/setup.zig
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user