automatically generate config associated files (#813)
This commit is contained in:
parent
9e658cdbb6
commit
1ae341850e
41
README.md
41
README.md
@ -59,29 +59,32 @@ zls will look for a zls.json configuration file in multiple locations with the f
|
|||||||
|
|
||||||
The following options are currently available.
|
The following options are currently available.
|
||||||
|
|
||||||
|
<!-- DO NOT EDIT | THIS SECTION IS AUTO-GENERATED | DO NOT EDIT -->
|
||||||
| Option | Type | Default value | What it Does |
|
| Option | Type | Default value | What it Does |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `enable_snippets` | `bool` | `false` | Enables snippet completions when the client also supports them. |
|
| `enable_snippets` | `bool` | `false` | Enables snippet completions when the client also supports them |
|
||||||
| `enable_ast_check_diagnostics` | `bool` | `true`| Whether to enable ast-check diagnostics |
|
| `enable_ast_check_diagnostics` | `bool` | `true` | Whether to enable ast-check diagnostics |
|
||||||
| `enable_autofix` | `bool` | `false`| Whether to automatically fix errors on save. Currently supports adding and removing discards. |
|
| `enable_autofix` | `bool` | `false` | Whether to automatically fix errors on save. Currently supports adding and removing discards. |
|
||||||
| `enable_import_embedfile_argument_completions` | `bool` | `false` | Whether to enable import/embedFile argument completions |
|
| `enable_import_embedfile_argument_completions` | `bool` | `false` | Whether to enable import/embedFile argument completions |
|
||||||
| `zig_lib_path` | `?[]const u8` | `null` | zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports. |
|
| `enable_semantic_tokens` | `bool` | `true` | Enables semantic token support when the client also supports it |
|
||||||
| `zig_exe_path` | `?[]const u8` | `null` | 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. |
|
| `enable_inlay_hints` | `bool` | `false` | Enables inlay hint support when the client also supports it |
|
||||||
| `warn_style` | `bool` | `false` | Enables warnings for style *guideline* mismatches |
|
|
||||||
| `build_runner_path` | `?[]const u8` | `null` | Path to the build_runner.zig file provided by zls. `null` is equivalent to `${executable_directory}/build_runner.zig` |
|
|
||||||
| `global_cache_path` | `?[]const u8` | `null` | Path to a directroy that will be used as zig's cache. `null` is equivalent to `${KnownFloders.Cache}/zls` |
|
|
||||||
| `enable_semantic_tokens` | `bool` | `true` | Enables semantic token support when the client also supports it. |
|
|
||||||
| `enable_inlay_hints` | `bool` | `false` | Enables inlay hint support when the client also supports it. |
|
|
||||||
| `inlay_hints_show_builtin` | `bool` | `true` | Enable inlay hints for builtin functions |
|
| `inlay_hints_show_builtin` | `bool` | `true` | Enable inlay hints for builtin functions |
|
||||||
| `inlay_hints_exclude_single_argument` | `bool` | `true`| Don't show inlay hints for single argument calls |
|
| `inlay_hints_exclude_single_argument` | `bool` | `true` | Don't show inlay hints for single argument calls |
|
||||||
| `inlay_hints_hide_redundant_param_names` | `bool` | `false`| Hides inlay hints when parameter name matches the identifier (e.g. foo: foo) |
|
| `inlay_hints_hide_redundant_param_names` | `bool` | `false` | Hides inlay hints when parameter name matches the identifier (e.g. foo: foo) |
|
||||||
| `inlay_hints_hide_redundant_param_names_last_token` | `bool` | `false`| Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo) |
|
| `inlay_hints_hide_redundant_param_names_last_token` | `bool` | `false` | Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo) |
|
||||||
| `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists. |
|
| `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists |
|
||||||
|`include_at_in_builtins`|`bool`|`false`| Whether the @ sign should be part of the completion of builtins.
|
| `warn_style` | `bool` | `false` | Enables warnings for style guideline mismatches |
|
||||||
|`max_detail_length`|`usize`|`1024 * 1024`| The detail field of completions is truncated to be no longer than this (in bytes).
|
| `highlight_global_var_declarations` | `bool` | `false` | Whether to highlight global var declarations |
|
||||||
| `skip_std_references` | `bool` | `false` | 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.
|
| `use_comptime_interpreter` | `bool` | `false` | Whether to use the comptime interpreter |
|
||||||
| `highlight_global_var_declarations` | `bool` | `false` | Whether to highlight global var declarations.
|
| `include_at_in_builtins` | `bool` | `false` | Whether the @ sign should be part of the completion of builtins |
|
||||||
| `use_comptime_interpreter` | `bool` | `false` | Whether to use the comptime interpreter.
|
| `skip_std_references` | `bool` | `false` | 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 |
|
||||||
|
| `max_detail_length` | `usize` | `1048576` | The detail field of completions is truncated to be no longer than this (in bytes) |
|
||||||
|
| `builtin_path` | `?[]const u8` | `null` | Path to 'builtin;' useful for debugging, automatically set if let null |
|
||||||
|
| `zig_lib_path` | `?[]const u8` | `null` | Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports |
|
||||||
|
| `zig_exe_path` | `?[]const u8` | `null` | 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 |
|
||||||
|
| `build_runner_path` | `?[]const u8` | `null` | Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig` |
|
||||||
|
| `global_cache_path` | `?[]const u8` | `null` | Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls` |
|
||||||
|
<!-- DO NOT EDIT -->
|
||||||
|
|
||||||
### Per-build Configuration Options
|
### Per-build Configuration Options
|
||||||
|
|
||||||
|
12
build.zig
12
build.zig
@ -116,6 +116,18 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
exe.setBuildMode(mode);
|
exe.setBuildMode(mode);
|
||||||
exe.install();
|
exe.install();
|
||||||
|
|
||||||
|
const gen_exe = b.addExecutable("zls_gen", "src/config_gen/config_gen.zig");
|
||||||
|
|
||||||
|
const gen_cmd = gen_exe.run();
|
||||||
|
gen_cmd.addArgs(&.{
|
||||||
|
b.fmt("{s}/src/Config.zig", .{b.build_root}),
|
||||||
|
b.fmt("{s}/schema.json", .{b.build_root}),
|
||||||
|
b.fmt("{s}/README.md", .{b.build_root}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const gen_step = b.step("gen", "Regenerate config files");
|
||||||
|
gen_step.dependOn(&gen_cmd.step);
|
||||||
|
|
||||||
const test_step = b.step("test", "Run all the tests");
|
const test_step = b.step("test", "Run all the tests");
|
||||||
test_step.dependOn(b.getInstallStep());
|
test_step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
75
schema.json
75
schema.json
@ -24,27 +24,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": "false"
|
"default": "false"
|
||||||
},
|
},
|
||||||
"zig_lib_path": {
|
|
||||||
"description": "Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"warn_style": {
|
|
||||||
"description": "Enables warnings for style guideline mismatches",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": "false"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"global_cache_path": {
|
|
||||||
"description": "Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"enable_semantic_tokens": {
|
"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": "boolean",
|
"type": "boolean",
|
||||||
@ -80,18 +59,8 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": "true"
|
"default": "true"
|
||||||
},
|
},
|
||||||
"include_at_in_builtins": {
|
"warn_style": {
|
||||||
"description": "Whether the @ sign should be part of the completion of builtins",
|
"description": "Enables warnings for style guideline mismatches",
|
||||||
"type": "boolean",
|
|
||||||
"default": "false"
|
|
||||||
},
|
|
||||||
"max_detail_length": {
|
|
||||||
"description": "The detail field of completions is truncated to be no longer than this (in bytes)",
|
|
||||||
"type": "integer",
|
|
||||||
"default": "1048576"
|
|
||||||
},
|
|
||||||
"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",
|
"type": "boolean",
|
||||||
"default": "false"
|
"default": "false"
|
||||||
},
|
},
|
||||||
@ -104,6 +73,46 @@
|
|||||||
"description": "Whether to use the comptime interpreter",
|
"description": "Whether to use the comptime interpreter",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": "false"
|
"default": "false"
|
||||||
|
},
|
||||||
|
"include_at_in_builtins": {
|
||||||
|
"description": "Whether the @ sign should be part of the completion of builtins",
|
||||||
|
"type": "boolean",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"max_detail_length": {
|
||||||
|
"description": "The detail field of completions is truncated to be no longer than this (in bytes)",
|
||||||
|
"type": "integer",
|
||||||
|
"default": "1048576"
|
||||||
|
},
|
||||||
|
"builtin_path": {
|
||||||
|
"description": "Path to 'builtin;' useful for debugging, automatically set if let null",
|
||||||
|
"type": "string",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"global_cache_path": {
|
||||||
|
"description": "Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`",
|
||||||
|
"type": "string",
|
||||||
|
"default": "null"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
279
src/Config.zig
279
src/Config.zig
@ -1,284 +1,73 @@
|
|||||||
|
//! DO NOT EDIT
|
||||||
//! Configuration options for zls.
|
//! Configuration options for zls.
|
||||||
//! Keep in sync with schema.json and zls-vscode's package.json!
|
//! If you want to add a config option edit
|
||||||
|
//! src/config_gen/config.zig and run `zig build gen`
|
||||||
|
//! GENERATED BY src/config_gen/config_gen.zig
|
||||||
|
|
||||||
const Config = @This();
|
/// Enables snippet completions when the client also supports them
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const setup = @import("setup.zig");
|
|
||||||
const tracy = @import("tracy.zig");
|
|
||||||
const known_folders = @import("known-folders");
|
|
||||||
|
|
||||||
const logger = std.log.scoped(.config);
|
|
||||||
|
|
||||||
/// Whether to enable snippet completions
|
|
||||||
enable_snippets: bool = false,
|
enable_snippets: bool = false,
|
||||||
|
|
||||||
/// Whether to enable ast-check diagnostics
|
/// Whether to enable ast-check diagnostics
|
||||||
enable_ast_check_diagnostics: bool = true,
|
enable_ast_check_diagnostics: bool = true,
|
||||||
|
|
||||||
/// Whether to automatically fix errors on save.
|
/// Whether to automatically fix errors on save. Currently supports adding and removing discards.
|
||||||
/// Currently supports adding and removing discards.
|
|
||||||
enable_autofix: bool = false,
|
enable_autofix: bool = false,
|
||||||
|
|
||||||
/// Whether to enable import/embedFile argument completions (NOTE: these are triggered manually as updating the autotrigger characters may cause issues)
|
/// Whether to enable import/embedFile argument completions
|
||||||
enable_import_embedfile_argument_completions: bool = false,
|
enable_import_embedfile_argument_completions: bool = false,
|
||||||
|
|
||||||
/// Zig library path
|
/// Enables semantic token support when the client also supports it
|
||||||
zig_lib_path: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Zig executable path used to run the custom build runner.
|
|
||||||
/// May be used to find a lib path if none is provided.
|
|
||||||
zig_exe_path: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Whether to pay attention to style issues. This is opt-in since the style
|
|
||||||
/// guide explicitly states that the style info provided is a guideline only.
|
|
||||||
warn_style: bool = false,
|
|
||||||
|
|
||||||
/// Path to the build_runner.zig file.
|
|
||||||
build_runner_path: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Path to the global cache directory
|
|
||||||
global_cache_path: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Semantic token support
|
|
||||||
enable_semantic_tokens: bool = true,
|
enable_semantic_tokens: bool = true,
|
||||||
|
|
||||||
/// Inlay hint support
|
/// Enables inlay hint support when the client also supports it
|
||||||
enable_inlay_hints: bool = false,
|
enable_inlay_hints: bool = false,
|
||||||
|
|
||||||
/// enable inlay hints for builtin functions
|
/// Enable inlay hints for builtin functions
|
||||||
inlay_hints_show_builtin: bool = true,
|
inlay_hints_show_builtin: bool = true,
|
||||||
|
|
||||||
/// don't show inlay hints for single argument calls
|
/// Don't show inlay hints for single argument calls
|
||||||
inlay_hints_exclude_single_argument: bool = true,
|
inlay_hints_exclude_single_argument: bool = true,
|
||||||
|
|
||||||
/// don't show inlay hints when parameter name matches the identifier
|
/// Hides inlay hints when parameter name matches the identifier (e.g. foo: foo)
|
||||||
/// for example: `foo: foo`
|
|
||||||
inlay_hints_hide_redundant_param_names: bool = false,
|
inlay_hints_hide_redundant_param_names: bool = false,
|
||||||
|
|
||||||
/// don't show inlay hints when parameter names matches the last
|
/// Hides inlay hints when parameter name matches the last token of a parameter node (e.g. foo: bar.foo, foo: &foo)
|
||||||
/// for example: `foo: bar.foo`, `foo: &foo`
|
|
||||||
inlay_hints_hide_redundant_param_names_last_token: bool = false,
|
inlay_hints_hide_redundant_param_names_last_token: bool = false,
|
||||||
|
|
||||||
/// Whether to enable `*` and `?` operators in completion lists
|
/// Enables `*` and `?` operators in completion lists
|
||||||
operator_completions: bool = true,
|
operator_completions: bool = true,
|
||||||
|
|
||||||
/// Whether the @ sign should be part of the completion of builtins
|
/// Enables warnings for style guideline mismatches
|
||||||
include_at_in_builtins: bool = false,
|
warn_style: bool = false,
|
||||||
|
|
||||||
/// The detail field of completions is truncated to be no longer than this (in bytes).
|
/// Whether to highlight global var declarations
|
||||||
max_detail_length: usize = 1048576,
|
|
||||||
|
|
||||||
/// Skips references to std. This will improve lookup speeds.
|
|
||||||
/// Going to definition however will continue to work
|
|
||||||
skip_std_references: bool = false,
|
|
||||||
|
|
||||||
/// Path to "builtin;" useful for debugging, automatically set if let null
|
|
||||||
builtin_path: ?[]const u8 = null,
|
|
||||||
|
|
||||||
/// Whether to highlight global var declarations.
|
|
||||||
highlight_global_var_declarations: bool = false,
|
highlight_global_var_declarations: bool = false,
|
||||||
|
|
||||||
/// Whether to use the comptime interpreter
|
/// Whether to use the comptime interpreter
|
||||||
use_comptime_interpreter: bool = false,
|
use_comptime_interpreter: bool = false,
|
||||||
|
|
||||||
pub fn loadFromFile(allocator: std.mem.Allocator, file_path: []const u8) ?Config {
|
/// Whether the @ sign should be part of the completion of builtins
|
||||||
const tracy_zone = tracy.trace(@src());
|
include_at_in_builtins: bool = false,
|
||||||
defer tracy_zone.end();
|
|
||||||
|
|
||||||
var file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
|
/// 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
|
||||||
if (err != error.FileNotFound)
|
skip_std_references: bool = false,
|
||||||
logger.warn("Error while reading configuration file: {}", .{err});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
defer file.close();
|
/// The detail field of completions is truncated to be no longer than this (in bytes)
|
||||||
|
max_detail_length: usize = 1048576,
|
||||||
|
|
||||||
const file_buf = file.readToEndAlloc(allocator, 0x1000000) catch return null;
|
/// Path to 'builtin;' useful for debugging, automatically set if let null
|
||||||
defer allocator.free(file_buf);
|
builtin_path: ?[]const u8 = null,
|
||||||
@setEvalBranchQuota(10000);
|
|
||||||
|
|
||||||
var token_stream = std.json.TokenStream.init(file_buf);
|
/// Zig library path, e.g. `/path/to/zig/lib/zig`, used to analyze std library imports
|
||||||
const parse_options = std.json.ParseOptions{ .allocator = allocator, .ignore_unknown_fields = true };
|
zig_lib_path: ?[]const u8 = null,
|
||||||
|
|
||||||
// TODO: Better errors? Doesn't seem like std.json can provide us positions or context.
|
/// 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
|
||||||
var config = std.json.parse(Config, &token_stream, parse_options) catch |err| {
|
zig_exe_path: ?[]const u8 = null,
|
||||||
logger.warn("Error while parsing configuration file: {}", .{err});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.zig_lib_path) |zig_lib_path| {
|
/// Path to the `build_runner.zig` file provided by zls. null is equivalent to `${executable_directory}/build_runner.zig`
|
||||||
if (!std.fs.path.isAbsolute(zig_lib_path)) {
|
build_runner_path: ?[]const u8 = null,
|
||||||
logger.warn("zig library path is not absolute, defaulting to null.", .{});
|
|
||||||
allocator.free(zig_lib_path);
|
|
||||||
config.zig_lib_path = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
/// Path to a directroy that will be used as zig's cache. null is equivalent to `${KnownFloders.Cache}/zls`
|
||||||
}
|
global_cache_path: ?[]const u8 = null,
|
||||||
|
|
||||||
pub fn loadFromFolder(allocator: std.mem.Allocator, folder_path: []const u8) ?Config {
|
// DO NOT EDIT
|
||||||
const tracy_zone = tracy.trace(@src());
|
|
||||||
defer tracy_zone.end();
|
|
||||||
|
|
||||||
const full_path = std.fs.path.resolve(allocator, &.{ folder_path, "zls.json" }) catch return null;
|
|
||||||
defer allocator.free(full_path);
|
|
||||||
return loadFromFile(allocator, full_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invoke this once all config values have been changed.
|
|
||||||
pub fn configChanged(config: *Config, allocator: std.mem.Allocator, builtin_creation_dir: ?[]const u8) !void {
|
|
||||||
// Find the zig executable in PATH
|
|
||||||
find_zig: {
|
|
||||||
if (config.zig_exe_path) |exe_path| {
|
|
||||||
if (std.fs.path.isAbsolute(exe_path)) not_valid: {
|
|
||||||
std.fs.cwd().access(exe_path, .{}) catch break :not_valid;
|
|
||||||
break :find_zig;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.zig_exe_path) |exe_path| blk: {
|
|
||||||
logger.info("Using zig executable {s}", .{exe_path});
|
|
||||||
|
|
||||||
if (config.zig_lib_path != null) break :blk;
|
|
||||||
|
|
||||||
var env = getZigEnv(allocator, exe_path) orelse break :blk;
|
|
||||||
defer std.json.parseFree(Env, env, .{ .allocator = allocator });
|
|
||||||
|
|
||||||
// Make sure the path is absolute
|
|
||||||
config.zig_lib_path = try std.fs.realpathAlloc(allocator, env.lib_dir.?);
|
|
||||||
logger.info("Using zig lib path '{s}'", .{config.zig_lib_path.?});
|
|
||||||
} else {
|
|
||||||
logger.warn("Zig executable path not specified in zls.json and could not be found in PATH", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.zig_lib_path == null) {
|
|
||||||
logger.warn("Zig standard library path not specified in zls.json and could not be resolved from the zig executable", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.builtin_path == null and config.zig_exe_path != null and builtin_creation_dir != null) blk: {
|
|
||||||
const result = try std.ChildProcess.exec(.{
|
|
||||||
.allocator = allocator,
|
|
||||||
.argv = &.{
|
|
||||||
config.zig_exe_path.?,
|
|
||||||
"build-exe",
|
|
||||||
"--show-builtin",
|
|
||||||
},
|
|
||||||
.max_output_bytes = 1024 * 1024 * 50,
|
|
||||||
});
|
|
||||||
defer allocator.free(result.stdout);
|
|
||||||
defer allocator.free(result.stderr);
|
|
||||||
|
|
||||||
var d = try std.fs.cwd().openDir(builtin_creation_dir.?, .{});
|
|
||||||
defer d.close();
|
|
||||||
|
|
||||||
const f = d.createFile("builtin.zig", .{}) catch |err| switch (err) {
|
|
||||||
error.AccessDenied => break :blk,
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
defer f.close();
|
|
||||||
|
|
||||||
try f.writer().writeAll(result.stdout);
|
|
||||||
|
|
||||||
config.builtin_path = try std.fs.path.join(allocator, &.{ builtin_creation_dir.?, "builtin.zig" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null == config.global_cache_path) {
|
|
||||||
const cache_dir_path = (try known_folders.getPath(allocator, .cache)) orelse {
|
|
||||||
logger.warn("Known-folders could not fetch the cache path", .{});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer allocator.free(cache_dir_path);
|
|
||||||
|
|
||||||
config.global_cache_path = try std.fs.path.resolve(allocator, &[_][]const u8{ cache_dir_path, "zls" });
|
|
||||||
|
|
||||||
std.fs.cwd().makePath(config.global_cache_path.?) catch |err| switch (err) {
|
|
||||||
error.PathAlreadyExists => {},
|
|
||||||
else => return err,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null == config.build_runner_path) {
|
|
||||||
config.build_runner_path = try std.fs.path.resolve(allocator, &[_][]const u8{ config.global_cache_path.?, "build_runner.zig" });
|
|
||||||
|
|
||||||
const file = try std.fs.createFileAbsolute(config.build_runner_path.?, .{});
|
|
||||||
defer file.close();
|
|
||||||
|
|
||||||
try file.writeAll(@embedFile("special/build_runner.zig"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Env = struct {
|
|
||||||
zig_exe: []const u8,
|
|
||||||
lib_dir: ?[]const u8,
|
|
||||||
std_dir: []const u8,
|
|
||||||
global_cache_dir: []const u8,
|
|
||||||
version: []const u8,
|
|
||||||
target: ?[]const u8 = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// result has to be freed with `std.json.parseFree`
|
|
||||||
pub fn getZigEnv(allocator: std.mem.Allocator, zig_exe_path: []const u8) ?Env {
|
|
||||||
const zig_env_result = std.ChildProcess.exec(.{
|
|
||||||
.allocator = allocator,
|
|
||||||
.argv = &[_][]const u8{ zig_exe_path, "env" },
|
|
||||||
}) catch {
|
|
||||||
logger.err("Failed to execute zig env", .{});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
defer {
|
|
||||||
allocator.free(zig_env_result.stdout);
|
|
||||||
allocator.free(zig_env_result.stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (zig_env_result.term) {
|
|
||||||
.Exited => |code| {
|
|
||||||
if (code != 0) {
|
|
||||||
logger.err("zig env failed with error_code: {}", .{code});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => logger.err("zig env invocation failed", .{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var token_stream = std.json.TokenStream.init(zig_env_result.stdout);
|
|
||||||
return std.json.parse(
|
|
||||||
Env,
|
|
||||||
&token_stream,
|
|
||||||
.{
|
|
||||||
.allocator = allocator,
|
|
||||||
.ignore_unknown_fields = true,
|
|
||||||
},
|
|
||||||
) catch {
|
|
||||||
logger.err("Failed to parse zig env JSON result", .{});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Configuration = Config.getConfigurationType();
|
|
||||||
pub const DidChangeConfigurationParams = struct {
|
|
||||||
settings: ?Configuration,
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns a Struct which is the same as `Config` except that every field is optional.
|
|
||||||
fn getConfigurationType() type {
|
|
||||||
var config_info: std.builtin.Type = @typeInfo(Config);
|
|
||||||
var fields: [config_info.Struct.fields.len]std.builtin.Type.StructField = undefined;
|
|
||||||
for (config_info.Struct.fields) |field, i| {
|
|
||||||
fields[i] = field;
|
|
||||||
if (@typeInfo(field.field_type) != .Optional) {
|
|
||||||
fields[i].field_type = @Type(std.builtin.Type{
|
|
||||||
.Optional = .{ .child = field.field_type },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config_info.Struct.fields = fields[0..];
|
|
||||||
config_info.Struct.decls = &.{};
|
|
||||||
return @Type(config_info);
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,7 @@ const std = @import("std");
|
|||||||
const zig_builtin = @import("builtin");
|
const zig_builtin = @import("builtin");
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
|
const configuration = @import("configuration.zig");
|
||||||
const DocumentStore = @import("DocumentStore.zig");
|
const DocumentStore = @import("DocumentStore.zig");
|
||||||
const requests = @import("requests.zig");
|
const requests = @import("requests.zig");
|
||||||
const types = @import("types.zig");
|
const types = @import("types.zig");
|
||||||
@ -1570,6 +1571,22 @@ fn initializeHandler(server: *Server, writer: anytype, id: types.RequestId, req:
|
|||||||
const tracy_zone = tracy.trace(@src());
|
const tracy_zone = tracy.trace(@src());
|
||||||
defer tracy_zone.end();
|
defer tracy_zone.end();
|
||||||
|
|
||||||
|
if (req.params.clientInfo) |clientInfo| {
|
||||||
|
std.log.info("client is '{s}-{s}'", .{ clientInfo.name, clientInfo.version orelse "<no version>" });
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, clientInfo.name, "Sublime Text LSP")) blk: {
|
||||||
|
server.config.max_detail_length = 256;
|
||||||
|
|
||||||
|
const version_str = clientInfo.version orelse break :blk;
|
||||||
|
const version = std.SemanticVersion.parse(version_str) catch break :blk;
|
||||||
|
// this indicates a LSP version for sublime text 3
|
||||||
|
// this check can be made more precise if the version that fixed this issue is known
|
||||||
|
if (version.major == 0) {
|
||||||
|
server.config.include_at_in_builtins = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (req.params.capabilities.general) |general| {
|
if (req.params.capabilities.general) |general| {
|
||||||
var supports_utf8 = false;
|
var supports_utf8 = false;
|
||||||
var supports_utf16 = false;
|
var supports_utf16 = false;
|
||||||
@ -1717,8 +1734,8 @@ fn initializeHandler(server: *Server, writer: anytype, id: types.RequestId, req:
|
|||||||
|
|
||||||
// TODO avoid having to call getZigEnv twice
|
// TODO avoid having to call getZigEnv twice
|
||||||
// once in init and here
|
// once in init and here
|
||||||
const env = Config.getZigEnv(server.allocator, server.config.zig_exe_path.?) orelse return;
|
const env = configuration.getZigEnv(server.allocator, server.config.zig_exe_path.?) orelse return;
|
||||||
defer std.json.parseFree(Config.Env, env, .{ .allocator = server.allocator });
|
defer std.json.parseFree(configuration.Env, env, .{ .allocator = server.allocator });
|
||||||
|
|
||||||
const zig_exe_version = std.SemanticVersion.parse(env.version) catch return;
|
const zig_exe_version = std.SemanticVersion.parse(env.version) catch return;
|
||||||
|
|
||||||
@ -2197,16 +2214,16 @@ fn formattingHandler(server: *Server, writer: anytype, id: types.RequestId, req:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn didChangeConfigurationHandler(server: *Server, writer: anytype, id: types.RequestId, req: Config.DidChangeConfigurationParams) !void {
|
fn didChangeConfigurationHandler(server: *Server, writer: anytype, id: types.RequestId, req: configuration.DidChangeConfigurationParams) !void {
|
||||||
const tracy_zone = tracy.trace(@src());
|
const tracy_zone = tracy.trace(@src());
|
||||||
defer tracy_zone.end();
|
defer tracy_zone.end();
|
||||||
|
|
||||||
_ = id;
|
_ = id;
|
||||||
|
|
||||||
// NOTE: VS Code seems to always respond with null
|
// NOTE: VS Code seems to always respond with null
|
||||||
if (req.settings) |configuration| {
|
if (req.settings) |cfg| {
|
||||||
inline for (std.meta.fields(Config.Configuration)) |field| {
|
inline for (std.meta.fields(configuration.Configuration)) |field| {
|
||||||
if (@field(configuration, field.name)) |value| {
|
if (@field(cfg, field.name)) |value| {
|
||||||
blk: {
|
blk: {
|
||||||
if (@TypeOf(value) == []const u8) {
|
if (@TypeOf(value) == []const u8) {
|
||||||
if (value.len == 0) {
|
if (value.len == 0) {
|
||||||
@ -2219,7 +2236,7 @@ fn didChangeConfigurationHandler(server: *Server, writer: anytype, id: types.Req
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try server.config.configChanged(server.allocator, null);
|
try configuration.configChanged(server.config, server.allocator, null);
|
||||||
} else if (server.client_capabilities.supports_configuration) {
|
} else if (server.client_capabilities.supports_configuration) {
|
||||||
try server.requestConfiguration(writer);
|
try server.requestConfiguration(writer);
|
||||||
}
|
}
|
||||||
@ -2838,7 +2855,7 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try server.config.configChanged(server.allocator, null);
|
try configuration.configChanged(server.config, server.allocator, null);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2901,7 +2918,7 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
|
|||||||
.{ "textDocument/references", requests.References, referencesHandler },
|
.{ "textDocument/references", requests.References, referencesHandler },
|
||||||
.{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler },
|
.{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler },
|
||||||
.{ "textDocument/codeAction", requests.CodeAction, codeActionHandler },
|
.{ "textDocument/codeAction", requests.CodeAction, codeActionHandler },
|
||||||
.{ "workspace/didChangeConfiguration", Config.DidChangeConfigurationParams, didChangeConfigurationHandler },
|
.{ "workspace/didChangeConfiguration", configuration.DidChangeConfigurationParams, didChangeConfigurationHandler },
|
||||||
.{ "textDocument/foldingRange", requests.FoldingRange, foldingRangeHandler },
|
.{ "textDocument/foldingRange", requests.FoldingRange, foldingRangeHandler },
|
||||||
.{ "textDocument/selectionRange", requests.SelectionRange, selectionRangeHandler },
|
.{ "textDocument/selectionRange", requests.SelectionRange, selectionRangeHandler },
|
||||||
};
|
};
|
||||||
@ -2998,7 +3015,7 @@ pub fn init(
|
|||||||
// see: https://github.com/zigtools/zls/issues/536
|
// see: https://github.com/zigtools/zls/issues/536
|
||||||
analysis.init(allocator);
|
analysis.init(allocator);
|
||||||
|
|
||||||
try config.configChanged(allocator, config_path);
|
try configuration.configChanged(config, allocator, config_path);
|
||||||
|
|
||||||
var document_store = DocumentStore{
|
var document_store = DocumentStore{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
158
src/config_gen/config.json
Normal file
158
src/config_gen/config.json
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
{
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "enable_snippets",
|
||||||
|
"description": "Enables snippet completions when the client also supports them",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"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?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enable_autofix",
|
||||||
|
"description": "Whether to automatically fix errors on save. Currently supports adding and removing discards.",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"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": "false",
|
||||||
|
"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?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "enable_inlay_hints",
|
||||||
|
"description": "Enables inlay hint support when the client also supports it",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inlay_hints_exclude_single_argument",
|
||||||
|
"description": "Don't show inlay hints for single argument calls",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "true",
|
||||||
|
"setup_question": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "operator_completions",
|
||||||
|
"description": "Enables `*` and `?` operators in completion lists",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "true",
|
||||||
|
"setup_question": "Do you want to enable .* and .? completions?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "warn_style",
|
||||||
|
"description": "Enables warnings for style guideline mismatches",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"setup_question": "Do you want to enable style warnings?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "highlight_global_var_declarations",
|
||||||
|
"description": "Whether to highlight global var declarations",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"setup_question": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "use_comptime_interpreter",
|
||||||
|
"description": "Whether to use the comptime interpreter",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"setup_question": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "include_at_in_builtins",
|
||||||
|
"description": "Whether the @ sign should be part of the completion of builtins",
|
||||||
|
"type": "bool",
|
||||||
|
"default": "false",
|
||||||
|
"setup_question": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "builtin_path",
|
||||||
|
"description": "Path to 'builtin;' useful for debugging, automatically set if let null",
|
||||||
|
"type": "?[]const u8",
|
||||||
|
"default": "null",
|
||||||
|
"setup_question": 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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
239
src/config_gen/config_gen.zig
Normal file
239
src/config_gen/config_gen.zig
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ConfigOption = struct {
|
||||||
|
/// Name of config option
|
||||||
|
name: []const u8,
|
||||||
|
/// (used in doc comments & schema.json)
|
||||||
|
description: []const u8,
|
||||||
|
/// zig type in string form. e.g "u32", "[]const u8", "?usize"
|
||||||
|
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 laer be used to automatically generate queries for setup.zig
|
||||||
|
setup_question: ?[]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Config = struct {
|
||||||
|
options: []ConfigOption,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Schema = struct {
|
||||||
|
@"$schema": []const u8 = "http://json-schema.org/schema",
|
||||||
|
title: []const u8 = "ZLS Config",
|
||||||
|
description: []const u8 = "Configuration file for the zig language server (ZLS)",
|
||||||
|
type: []const u8 = "object",
|
||||||
|
properties: std.StringArrayHashMap(SchemaEntry),
|
||||||
|
};
|
||||||
|
|
||||||
|
const SchemaEntry = struct {
|
||||||
|
description: []const u8,
|
||||||
|
type: []const u8,
|
||||||
|
default: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn zigTypeToTypescript(ty: []const u8) ![]const u8 {
|
||||||
|
return if (std.mem.eql(u8, ty, "?[]const u8"))
|
||||||
|
"string"
|
||||||
|
else if (std.mem.eql(u8, ty, "bool"))
|
||||||
|
"boolean"
|
||||||
|
else if (std.mem.eql(u8, ty, "usize"))
|
||||||
|
"integer"
|
||||||
|
else
|
||||||
|
error.UnsupportedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generateConfigFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
|
_ = allocator;
|
||||||
|
|
||||||
|
const config_file = try std.fs.openFileAbsolute(path, .{
|
||||||
|
.mode = .write_only,
|
||||||
|
});
|
||||||
|
defer config_file.close();
|
||||||
|
|
||||||
|
var buff_out = std.io.bufferedWriter(config_file.writer());
|
||||||
|
|
||||||
|
_ = try buff_out.write(
|
||||||
|
\\//! DO NOT EDIT
|
||||||
|
\\//! Configuration options for zls.
|
||||||
|
\\//! If you want to add a config option edit
|
||||||
|
\\//! src/config_gen/config.zig and run `zig build gen`
|
||||||
|
\\//! GENERATED BY src/config_gen/config_gen.zig
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
for (config.options) |option| {
|
||||||
|
try buff_out.writer().print(
|
||||||
|
\\
|
||||||
|
\\/// {s}
|
||||||
|
\\{s}: {s} = {s},
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
std.mem.trim(u8, option.description, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.name, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.type, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.default, " \t\n\r"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try buff_out.write(
|
||||||
|
\\
|
||||||
|
\\// DO NOT EDIT
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
try buff_out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generateSchemaFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
|
const schema_file = try std.fs.openFileAbsolute(path, .{
|
||||||
|
.mode = .write_only,
|
||||||
|
});
|
||||||
|
defer schema_file.close();
|
||||||
|
|
||||||
|
var buff_out = std.io.bufferedWriter(schema_file.writer());
|
||||||
|
|
||||||
|
var properties = std.StringArrayHashMapUnmanaged(SchemaEntry){};
|
||||||
|
defer properties.deinit(allocator);
|
||||||
|
try properties.ensureTotalCapacity(allocator, config.options.len);
|
||||||
|
|
||||||
|
for (config.options) |option| {
|
||||||
|
properties.putAssumeCapacityNoClobber(option.name, .{
|
||||||
|
.description = option.description,
|
||||||
|
.type = try zigTypeToTypescript(option.type),
|
||||||
|
.default = option.default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try buff_out.write(
|
||||||
|
\\{
|
||||||
|
\\ "$schema": "http://json-schema.org/schema",
|
||||||
|
\\ "title": "ZLS Config",
|
||||||
|
\\ "description": "Configuration file for the zig language server (ZLS)",
|
||||||
|
\\ "type": "object",
|
||||||
|
\\ "properties":
|
||||||
|
);
|
||||||
|
|
||||||
|
try serializeObjectMap(properties, .{
|
||||||
|
.whitespace = .{
|
||||||
|
.indent_level = 1,
|
||||||
|
},
|
||||||
|
}, buff_out.writer());
|
||||||
|
|
||||||
|
_ = try buff_out.write("\n}\n");
|
||||||
|
try buff_out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updateREADMEFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
|
var readme_file = try std.fs.openFileAbsolute(path, .{ .mode = .read_write });
|
||||||
|
defer readme_file.close();
|
||||||
|
|
||||||
|
var readme = std.ArrayListUnmanaged(u8){
|
||||||
|
.items = try readme_file.readToEndAlloc(allocator, std.math.maxInt(usize)),
|
||||||
|
};
|
||||||
|
defer readme.deinit(allocator);
|
||||||
|
|
||||||
|
const start_indicator = "<!-- DO NOT EDIT | THIS SECTION IS AUTO-GENERATED | DO NOT EDIT -->";
|
||||||
|
const end_indicator = "<!-- DO NOT EDIT -->";
|
||||||
|
|
||||||
|
const start = start_indicator.len + (std.mem.indexOf(u8, readme.items, start_indicator) orelse return error.SectionNotFound);
|
||||||
|
const end = std.mem.indexOfPos(u8, readme.items, start, end_indicator) orelse return error.SectionNotFound;
|
||||||
|
|
||||||
|
var new_readme = std.ArrayListUnmanaged(u8){};
|
||||||
|
defer new_readme.deinit(allocator);
|
||||||
|
var writer = new_readme.writer(allocator);
|
||||||
|
|
||||||
|
try writer.writeAll(
|
||||||
|
\\
|
||||||
|
\\| Option | Type | Default value | What it Does |
|
||||||
|
\\| --- | --- | --- | --- |
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
for (config.options) |option| {
|
||||||
|
try writer.print(
|
||||||
|
\\| `{s}` | `{s}` | `{s}` | {s} |
|
||||||
|
\\
|
||||||
|
, .{
|
||||||
|
std.mem.trim(u8, option.name, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.type, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.default, " \t\n\r"),
|
||||||
|
std.mem.trim(u8, option.description, " \t\n\r"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try readme.replaceRange(allocator, start, end - start, new_readme.items);
|
||||||
|
|
||||||
|
try readme_file.seekTo(0);
|
||||||
|
try readme_file.writeAll(readme.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var arg_it = std.process.args();
|
||||||
|
|
||||||
|
_ = arg_it.next() orelse @panic("");
|
||||||
|
const config_path = arg_it.next() orelse @panic("first argument must be path to Config.zig");
|
||||||
|
const schema_path = arg_it.next() orelse @panic("second argument must be path to schema.json");
|
||||||
|
const readme_path = arg_it.next() orelse @panic("third argument must be path to README.md");
|
||||||
|
|
||||||
|
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
var gpa = general_purpose_allocator.allocator();
|
||||||
|
|
||||||
|
const parse_options = std.json.ParseOptions{
|
||||||
|
.allocator = gpa,
|
||||||
|
};
|
||||||
|
var token_stream = std.json.TokenStream.init(@embedFile("config.json"));
|
||||||
|
const config = try std.json.parse(Config, &token_stream, parse_options);
|
||||||
|
defer std.json.parseFree(Config, config, parse_options);
|
||||||
|
|
||||||
|
try generateConfigFile(gpa, config, config_path);
|
||||||
|
try generateSchemaFile(gpa, config, schema_path);
|
||||||
|
try updateREADMEFile(gpa, config, readme_path);
|
||||||
|
|
||||||
|
std.log.warn(
|
||||||
|
\\ If you have added a new configuration option and it should be configuration through the config wizard, then edit src/setup.zig
|
||||||
|
, .{});
|
||||||
|
|
||||||
|
std.log.info(
|
||||||
|
\\ Changing configuration options may also require editing the `package.json` from zls-vscode at https://github.com/zigtools/zls-vscode/blob/master/package.json
|
||||||
|
, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serializeObjectMap(
|
||||||
|
value: anytype,
|
||||||
|
options: std.json.StringifyOptions,
|
||||||
|
out_stream: anytype,
|
||||||
|
) @TypeOf(out_stream).Error!void {
|
||||||
|
try out_stream.writeByte('{');
|
||||||
|
var field_output = false;
|
||||||
|
var child_options = options;
|
||||||
|
if (child_options.whitespace) |*child_whitespace| {
|
||||||
|
child_whitespace.indent_level += 1;
|
||||||
|
}
|
||||||
|
var it = value.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (!field_output) {
|
||||||
|
field_output = true;
|
||||||
|
} else {
|
||||||
|
try out_stream.writeByte(',');
|
||||||
|
}
|
||||||
|
if (child_options.whitespace) |child_whitespace| {
|
||||||
|
try child_whitespace.outputIndent(out_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.json.stringify(entry.key_ptr.*, options, out_stream);
|
||||||
|
try out_stream.writeByte(':');
|
||||||
|
if (child_options.whitespace) |child_whitespace| {
|
||||||
|
if (child_whitespace.separator) {
|
||||||
|
try out_stream.writeByte(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try std.json.stringify(entry.value_ptr.*, child_options, out_stream);
|
||||||
|
}
|
||||||
|
if (field_output) {
|
||||||
|
if (options.whitespace) |whitespace| {
|
||||||
|
try whitespace.outputIndent(out_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try out_stream.writeByte('}');
|
||||||
|
}
|
210
src/configuration.zig
Normal file
210
src/configuration.zig
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const setup = @import("setup.zig");
|
||||||
|
const tracy = @import("tracy.zig");
|
||||||
|
const known_folders = @import("known-folders");
|
||||||
|
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
|
||||||
|
const logger = std.log.scoped(.config);
|
||||||
|
|
||||||
|
pub fn loadFromFile(allocator: std.mem.Allocator, file_path: []const u8) ?Config {
|
||||||
|
const tracy_zone = tracy.trace(@src());
|
||||||
|
defer tracy_zone.end();
|
||||||
|
|
||||||
|
var file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
|
||||||
|
if (err != error.FileNotFound)
|
||||||
|
logger.warn("Error while reading configuration file: {}", .{err});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const file_buf = file.readToEndAlloc(allocator, 0x1000000) catch return null;
|
||||||
|
defer allocator.free(file_buf);
|
||||||
|
@setEvalBranchQuota(10000);
|
||||||
|
|
||||||
|
var token_stream = std.json.TokenStream.init(file_buf);
|
||||||
|
const parse_options = std.json.ParseOptions{ .allocator = allocator, .ignore_unknown_fields = true };
|
||||||
|
|
||||||
|
// TODO: Better errors? Doesn't seem like std.json can provide us positions or context.
|
||||||
|
var config = std.json.parse(Config, &token_stream, parse_options) catch |err| {
|
||||||
|
logger.warn("Error while parsing configuration file: {}", .{err});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.zig_lib_path) |zig_lib_path| {
|
||||||
|
if (!std.fs.path.isAbsolute(zig_lib_path)) {
|
||||||
|
logger.warn("zig library path is not absolute, defaulting to null.", .{});
|
||||||
|
allocator.free(zig_lib_path);
|
||||||
|
config.zig_lib_path = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loadFromFolder(allocator: std.mem.Allocator, folder_path: []const u8) ?Config {
|
||||||
|
const tracy_zone = tracy.trace(@src());
|
||||||
|
defer tracy_zone.end();
|
||||||
|
|
||||||
|
const full_path = std.fs.path.resolve(allocator, &.{ folder_path, "zls.json" }) catch return null;
|
||||||
|
defer allocator.free(full_path);
|
||||||
|
return loadFromFile(allocator, full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke this once all config values have been changed.
|
||||||
|
pub fn configChanged(config: *Config, allocator: std.mem.Allocator, builtin_creation_dir: ?[]const u8) !void {
|
||||||
|
// Find the zig executable in PATH
|
||||||
|
find_zig: {
|
||||||
|
if (config.zig_exe_path) |exe_path| {
|
||||||
|
if (std.fs.path.isAbsolute(exe_path)) not_valid: {
|
||||||
|
std.fs.cwd().access(exe_path, .{}) catch break :not_valid;
|
||||||
|
break :find_zig;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.zig_exe_path) |exe_path| blk: {
|
||||||
|
logger.info("Using zig executable {s}", .{exe_path});
|
||||||
|
|
||||||
|
if (config.zig_lib_path != null) break :blk;
|
||||||
|
|
||||||
|
var env = getZigEnv(allocator, exe_path) orelse break :blk;
|
||||||
|
defer std.json.parseFree(Env, env, .{ .allocator = allocator });
|
||||||
|
|
||||||
|
// Make sure the path is absolute
|
||||||
|
config.zig_lib_path = try std.fs.realpathAlloc(allocator, env.lib_dir.?);
|
||||||
|
logger.info("Using zig lib path '{s}'", .{config.zig_lib_path.?});
|
||||||
|
} else {
|
||||||
|
logger.warn("Zig executable path not specified in zls.json and could not be found in PATH", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.zig_lib_path == null) {
|
||||||
|
logger.warn("Zig standard library path not specified in zls.json and could not be resolved from the zig executable", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.builtin_path == null and config.zig_exe_path != null and builtin_creation_dir != null) blk: {
|
||||||
|
const result = try std.ChildProcess.exec(.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.argv = &.{
|
||||||
|
config.zig_exe_path.?,
|
||||||
|
"build-exe",
|
||||||
|
"--show-builtin",
|
||||||
|
},
|
||||||
|
.max_output_bytes = 1024 * 1024 * 50,
|
||||||
|
});
|
||||||
|
defer allocator.free(result.stdout);
|
||||||
|
defer allocator.free(result.stderr);
|
||||||
|
|
||||||
|
var d = try std.fs.cwd().openDir(builtin_creation_dir.?, .{});
|
||||||
|
defer d.close();
|
||||||
|
|
||||||
|
const f = d.createFile("builtin.zig", .{}) catch |err| switch (err) {
|
||||||
|
error.AccessDenied => break :blk,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
defer f.close();
|
||||||
|
|
||||||
|
try f.writer().writeAll(result.stdout);
|
||||||
|
|
||||||
|
config.builtin_path = try std.fs.path.join(allocator, &.{ builtin_creation_dir.?, "builtin.zig" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == config.global_cache_path) {
|
||||||
|
const cache_dir_path = (try known_folders.getPath(allocator, .cache)) orelse {
|
||||||
|
logger.warn("Known-folders could not fetch the cache path", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer allocator.free(cache_dir_path);
|
||||||
|
|
||||||
|
config.global_cache_path = try std.fs.path.resolve(allocator, &[_][]const u8{ cache_dir_path, "zls" });
|
||||||
|
|
||||||
|
std.fs.cwd().makePath(config.global_cache_path.?) catch |err| switch (err) {
|
||||||
|
error.PathAlreadyExists => {},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == config.build_runner_path) {
|
||||||
|
config.build_runner_path = try std.fs.path.resolve(allocator, &[_][]const u8{ config.global_cache_path.?, "build_runner.zig" });
|
||||||
|
|
||||||
|
const file = try std.fs.createFileAbsolute(config.build_runner_path.?, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
try file.writeAll(@embedFile("special/build_runner.zig"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Env = struct {
|
||||||
|
zig_exe: []const u8,
|
||||||
|
lib_dir: ?[]const u8,
|
||||||
|
std_dir: []const u8,
|
||||||
|
global_cache_dir: []const u8,
|
||||||
|
version: []const u8,
|
||||||
|
target: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// result has to be freed with `std.json.parseFree`
|
||||||
|
pub fn getZigEnv(allocator: std.mem.Allocator, zig_exe_path: []const u8) ?Env {
|
||||||
|
const zig_env_result = std.ChildProcess.exec(.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.argv = &[_][]const u8{ zig_exe_path, "env" },
|
||||||
|
}) catch {
|
||||||
|
logger.err("Failed to execute zig env", .{});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
defer {
|
||||||
|
allocator.free(zig_env_result.stdout);
|
||||||
|
allocator.free(zig_env_result.stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (zig_env_result.term) {
|
||||||
|
.Exited => |code| {
|
||||||
|
if (code != 0) {
|
||||||
|
logger.err("zig env failed with error_code: {}", .{code});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => logger.err("zig env invocation failed", .{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
var token_stream = std.json.TokenStream.init(zig_env_result.stdout);
|
||||||
|
return std.json.parse(
|
||||||
|
Env,
|
||||||
|
&token_stream,
|
||||||
|
.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.ignore_unknown_fields = true,
|
||||||
|
},
|
||||||
|
) catch {
|
||||||
|
logger.err("Failed to parse zig env JSON result", .{});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Configuration = getConfigurationType();
|
||||||
|
pub const DidChangeConfigurationParams = struct {
|
||||||
|
settings: ?Configuration,
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns a Struct which is the same as `Config` except that every field is optional.
|
||||||
|
fn getConfigurationType() type {
|
||||||
|
var config_info: std.builtin.Type = @typeInfo(Config);
|
||||||
|
var fields: [config_info.Struct.fields.len]std.builtin.Type.StructField = undefined;
|
||||||
|
for (config_info.Struct.fields) |field, i| {
|
||||||
|
fields[i] = field;
|
||||||
|
if (@typeInfo(field.field_type) != .Optional) {
|
||||||
|
fields[i].field_type = @Type(std.builtin.Type{
|
||||||
|
.Optional = .{ .child = field.field_type },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config_info.Struct.fields = fields[0..];
|
||||||
|
config_info.Struct.decls = &.{};
|
||||||
|
return @Type(config_info);
|
||||||
|
}
|
@ -4,6 +4,7 @@ const build_options = @import("build_options");
|
|||||||
const tracy = @import("tracy.zig");
|
const tracy = @import("tracy.zig");
|
||||||
const known_folders = @import("known-folders");
|
const known_folders = @import("known-folders");
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
|
const configuration = @import("configuration.zig");
|
||||||
const Server = @import("Server.zig");
|
const Server = @import("Server.zig");
|
||||||
const setup = @import("setup.zig");
|
const setup = @import("setup.zig");
|
||||||
const readRequestHeader = @import("header.zig").readRequestHeader;
|
const readRequestHeader = @import("header.zig").readRequestHeader;
|
||||||
@ -65,7 +66,7 @@ fn getConfig(
|
|||||||
free_old_config_path: bool,
|
free_old_config_path: bool,
|
||||||
) !ConfigWithPath {
|
) !ConfigWithPath {
|
||||||
if (config_path) |path| {
|
if (config_path) |path| {
|
||||||
if (Config.loadFromFile(allocator, path)) |conf| {
|
if (configuration.loadFromFile(allocator, path)) |conf| {
|
||||||
return ConfigWithPath{
|
return ConfigWithPath{
|
||||||
.config = conf,
|
.config = conf,
|
||||||
.config_path = path,
|
.config_path = path,
|
||||||
@ -82,7 +83,7 @@ fn getConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (try known_folders.getPath(allocator, .local_configuration)) |path| {
|
if (try known_folders.getPath(allocator, .local_configuration)) |path| {
|
||||||
if (Config.loadFromFolder(allocator, path)) |conf| {
|
if (configuration.loadFromFolder(allocator, path)) |conf| {
|
||||||
return ConfigWithPath{
|
return ConfigWithPath{
|
||||||
.config = conf,
|
.config = conf,
|
||||||
.config_path = path,
|
.config_path = path,
|
||||||
@ -92,7 +93,7 @@ fn getConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (try known_folders.getPath(allocator, .global_configuration)) |path| {
|
if (try known_folders.getPath(allocator, .global_configuration)) |path| {
|
||||||
if (Config.loadFromFolder(allocator, path)) |conf| {
|
if (configuration.loadFromFolder(allocator, path)) |conf| {
|
||||||
return ConfigWithPath{
|
return ConfigWithPath{
|
||||||
.config = conf,
|
.config = conf,
|
||||||
.config_path = path,
|
.config_path = path,
|
||||||
|
@ -178,7 +178,13 @@ pub const Initialize = struct {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ClientInfo = struct {
|
||||||
|
name: []const u8,
|
||||||
|
version: ?[]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
params: struct {
|
params: struct {
|
||||||
|
clientInfo: ?ClientInfo,
|
||||||
capabilities: ClientCapabilities,
|
capabilities: ClientCapabilities,
|
||||||
workspaceFolders: ?[]const types.WorkspaceFolder,
|
workspaceFolders: ?[]const types.WorkspaceFolder,
|
||||||
},
|
},
|
||||||
|
170
src/setup.zig
170
src/setup.zig
@ -90,19 +90,11 @@ pub fn askSelectOne(prompt: []const u8, comptime Options: type) !Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(comptime fmt: []const u8, args: anytype) void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
stdout.print(fmt, args) catch @panic("Could not write to stdout");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(text: []const u8) void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
stdout.writeAll(text) catch @panic("Could not write to stdout");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wizard(allocator: std.mem.Allocator) !void {
|
pub fn wizard(allocator: std.mem.Allocator) !void {
|
||||||
@setEvalBranchQuota(2500);
|
@setEvalBranchQuota(2500);
|
||||||
write(
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
|
||||||
|
try stdout.writeAll(
|
||||||
\\Welcome to the ZLS configuration wizard!
|
\\Welcome to the ZLS configuration wizard!
|
||||||
\\ *
|
\\ *
|
||||||
\\ |\
|
\\ |\
|
||||||
@ -127,7 +119,7 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (global_path == null and local_path == null) {
|
if (global_path == null and local_path == null) {
|
||||||
write("Could not open a global or local config directory.\n");
|
try stdout.writeAll("Could not open a global or local config directory.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var config_path: []const u8 = undefined;
|
var config_path: []const u8 = undefined;
|
||||||
@ -137,17 +129,17 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
if (local_path) |p| {
|
if (local_path) |p| {
|
||||||
config_path = p;
|
config_path = p;
|
||||||
} else {
|
} else {
|
||||||
write("Could not find a local config directory.\n");
|
try stdout.writeAll("Could not find a local config directory.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var dir = std.fs.cwd().openDir(config_path, .{}) catch |err| {
|
var dir = std.fs.cwd().openDir(config_path, .{}) catch |err| {
|
||||||
print("Could not open {s}: {}.\n", .{ config_path, err });
|
try stdout.print("Could not open {s}: {}.\n", .{ config_path, err });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
var file = dir.createFile("zls.json", .{}) catch |err| {
|
var file = dir.createFile("zls.json", .{}) catch |err| {
|
||||||
print("Could not create {s}/zls.json: {}.\n", .{ config_path, err });
|
try stdout.print("Could not create {s}/zls.json: {}.\n", .{ config_path, err });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer file.close();
|
defer file.close();
|
||||||
@ -157,9 +149,9 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
defer if (zig_exe_path) |p| allocator.free(p);
|
defer if (zig_exe_path) |p| allocator.free(p);
|
||||||
|
|
||||||
if (zig_exe_path) |path| {
|
if (zig_exe_path) |path| {
|
||||||
print("Found zig executable '{s}' in PATH.\n", .{path});
|
try stdout.print("Found zig executable '{s}' in PATH.\n", .{path});
|
||||||
} else {
|
} else {
|
||||||
write("Could not find 'zig' in PATH\n");
|
try stdout.writeAll("Could not find 'zig' in PATH\n");
|
||||||
zig_exe_path = try askString(allocator, if (builtin.os.tag == .windows)
|
zig_exe_path = try askString(allocator, if (builtin.os.tag == .windows)
|
||||||
\\What is the path to the 'zig' executable you would like to use?
|
\\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),
|
\\Note that due to a bug in zig (https://github.com/ziglang/zig/issues/6044),
|
||||||
@ -168,7 +160,6 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
"What is the path to the 'zig' executable you would like to use?", std.fs.MAX_PATH_BYTES);
|
"What is the path to the 'zig' executable you would like to use?", std.fs.MAX_PATH_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
const editor = try askSelectOne("Which code editor do you use?", enum { VSCode, Sublime, Kate, Neovim, Vim8, Emacs, Doom, Spacemacs, Helix, Other });
|
|
||||||
const snippets = try askBool("Do you want to enable snippets?");
|
const snippets = try askBool("Do you want to enable snippets?");
|
||||||
const ast_check = try askBool("Do you want to enable ast-check diagnostics?");
|
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 autofix = try askBool("Do you want to zls to automatically try to fix errors on save? (supports adding & removing discards)");
|
||||||
@ -177,15 +168,6 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
const semantic_tokens = try askBool("Do you want to enable semantic highlighting?");
|
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 inlay_hints = try askBool("Do you want to enable inlay hints?");
|
||||||
const operator_completions = try askBool("Do you want to enable .* and .? completions?");
|
const operator_completions = try askBool("Do you want to enable .* and .? completions?");
|
||||||
const include_at_in_builtins = switch (editor) {
|
|
||||||
.Sublime => !try askBool("Are you using a Sublime Text version > 4000?"),
|
|
||||||
.VSCode, .Kate, .Neovim, .Vim8, .Emacs, .Doom, .Spacemacs, .Helix => false,
|
|
||||||
else => try askBool("Should the @ sign be included in completions of builtin functions?\nChange this later if `@inc` completes to `include` or `@@include`"),
|
|
||||||
};
|
|
||||||
const max_detail_length: usize = switch (editor) {
|
|
||||||
.Sublime => 256,
|
|
||||||
else => 1024 * 1024,
|
|
||||||
};
|
|
||||||
|
|
||||||
std.debug.print("Writing config to {s}/zls.json ... ", .{config_path});
|
std.debug.print("Writing config to {s}/zls.json ... ", .{config_path});
|
||||||
|
|
||||||
@ -200,144 +182,18 @@ pub fn wizard(allocator: std.mem.Allocator) !void {
|
|||||||
.enable_semantic_tokens = semantic_tokens,
|
.enable_semantic_tokens = semantic_tokens,
|
||||||
.enable_inlay_hints = inlay_hints,
|
.enable_inlay_hints = inlay_hints,
|
||||||
.operator_completions = operator_completions,
|
.operator_completions = operator_completions,
|
||||||
.include_at_in_builtins = include_at_in_builtins,
|
|
||||||
.max_detail_length = max_detail_length,
|
|
||||||
}, .{
|
}, .{
|
||||||
.whitespace = .{},
|
.whitespace = .{},
|
||||||
}, out);
|
}, out);
|
||||||
|
|
||||||
write("successful.\n\n\n\n");
|
try stdout.writeAll(
|
||||||
|
\\successful.
|
||||||
// Keep synced with README.md
|
|
||||||
switch (editor) {
|
|
||||||
.VSCode => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Visual Studio Code, install the 'ZLS for VSCode' extension from
|
|
||||||
\\'https://github.com/zigtools/zls-vscode/releases' or via the extensions menu.
|
|
||||||
\\ZLS will automatically be installed if it is not found in your PATH
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Sublime => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Sublime, install the `LSP` package from
|
|
||||||
\\https://github.com/sublimelsp/LSP/releases or via Package Control.
|
|
||||||
\\Then, add the following snippet to LSP's user settings:
|
|
||||||
\\
|
\\
|
||||||
\\For Sublime Text 3:
|
\\You can find information on how to setup zls for your editor on zigtools.github.io/install-zls/
|
||||||
\\
|
\\
|
||||||
\\{
|
\\Thank you for choosing ZLS!
|
||||||
\\ "clients": {
|
|
||||||
\\ "zig": {
|
|
||||||
\\ "command": ["zls"],
|
|
||||||
\\ "enabled": true,
|
|
||||||
\\ "languageId": "zig",
|
|
||||||
\\ "scopes": ["source.zig"],
|
|
||||||
\\ "syntaxes": ["Packages/Zig Language/Syntaxes/Zig.tmLanguage"]
|
|
||||||
\\ }
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
\\
|
\\
|
||||||
\\For Sublime Text 4:
|
|
||||||
\\
|
|
||||||
\\{
|
|
||||||
\\ "clients": {
|
|
||||||
\\ "zig": {
|
|
||||||
\\ "command": ["zls"],
|
|
||||||
\\ "enabled": true,
|
|
||||||
\\ "selector": "source.zig"
|
|
||||||
\\ }
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
.Kate => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Kate, enable `LSP client` plugin in Kate settings.
|
|
||||||
\\Then, add the following snippet to `LSP client's` user settings:
|
|
||||||
\\(or paste it in `LSP client's` GUI settings)
|
|
||||||
\\
|
|
||||||
\\{
|
|
||||||
\\ "servers": {
|
|
||||||
\\ "zig": {
|
|
||||||
\\ "command": ["zls"],
|
|
||||||
\\ "url": "https://github.com/zigtools/zls",
|
|
||||||
\\ "highlightingModeRegex": "^Zig$"
|
|
||||||
\\ }
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Neovim, .Vim8 => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Neovim/Vim8, we recommend using CoC engine.
|
|
||||||
\\You can get it from https://github.com/neoclide/coc.nvim.
|
|
||||||
\\Then, simply issue cmd from Neovim/Vim8 `:CocConfig`, and add this to your CoC config:
|
|
||||||
\\
|
|
||||||
\\{
|
|
||||||
\\ "languageserver": {
|
|
||||||
\\ "zls" : {
|
|
||||||
\\ "command": "command_or_path_to_zls",
|
|
||||||
\\ "filetypes": ["zig"]
|
|
||||||
\\ }
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Emacs => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Emacs, install lsp-mode (https://github.com/emacs-lsp/lsp-mode) from melpa.
|
|
||||||
\\Zig mode (https://github.com/ziglang/zig-mode) is also useful!
|
|
||||||
\\Then, add the following to your emacs config:
|
|
||||||
\\
|
|
||||||
\\(require 'lsp-mode)
|
|
||||||
\\(setq lsp-zig-zls-executable "<path to zls>")
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Doom => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Doom Emacs, enable the lsp module
|
|
||||||
\\And install the `zig-mode` (https://github.com/ziglang/zig-mode)
|
|
||||||
\\package by adding `(package! zig-mode)` to your packages.el file.
|
|
||||||
\\
|
|
||||||
\\(use-package! zig-mode
|
|
||||||
\\ :hook ((zig-mode . lsp-deferred))
|
|
||||||
\\ :custom (zig-format-on-save nil)
|
|
||||||
\\ :config
|
|
||||||
\\ (after! lsp-mode
|
|
||||||
\\ (add-to-list 'lsp-language-id-configuration '(zig-mode . "zig"))
|
|
||||||
\\ (lsp-register-client
|
|
||||||
\\ (make-lsp-client
|
|
||||||
\\ :new-connection (lsp-stdio-connection "<path to zls>")
|
|
||||||
\\ :major-modes '(zig-mode)
|
|
||||||
\\ :server-id 'zls))))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Spacemacs => {
|
|
||||||
write(
|
|
||||||
\\To use ZLS in Spacemacs, add the `lsp` and `zig` layers
|
|
||||||
\\to `dotspacemacs-configuration-layers` in your .spacemacs file.
|
|
||||||
\\Then, if you don't have `zls` in your PATH, add the following to
|
|
||||||
\\`dotspacemacs/user-config` in your .spacemacs file:
|
|
||||||
\\
|
|
||||||
\\(setq lsp-zig-zls-executable "<path to zls>")
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Helix => {
|
|
||||||
write(
|
|
||||||
\\Helix has out of the box support for ZLS
|
|
||||||
\\Make sure you have added ZLS to your PATH
|
|
||||||
\\run hx --health to check if helix has found it.
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.Other => {
|
|
||||||
write(
|
|
||||||
\\We might not *officially* support your editor, but you can definitely still use ZLS!
|
|
||||||
\\Simply configure your editor for use with language servers and point it to the ZLS executable!
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
write("\n\nThank you for choosing ZLS!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn findZig(allocator: std.mem.Allocator) !?[]const u8 {
|
pub fn findZig(allocator: std.mem.Allocator) !?[]const u8 {
|
||||||
|
Loading…
Reference in New Issue
Block a user