add vscode config generation to zig build gen
(#862)
* add vscode config generation to `zig build gen` * correctly handle removing configs in config_gen.zig * update log messages in config_gen.zig
This commit is contained in:
parent
d86d05d969
commit
417bf9bd0a
@ -133,6 +133,7 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
exe.install();
|
exe.install();
|
||||||
|
|
||||||
const gen_exe = b.addExecutable("zls_gen", "src/config_gen/config_gen.zig");
|
const gen_exe = b.addExecutable("zls_gen", "src/config_gen/config_gen.zig");
|
||||||
|
gen_exe.addPackage(.{ .name = "tres", .source = .{ .path = "src/tres/tres.zig" } });
|
||||||
|
|
||||||
const gen_cmd = gen_exe.run();
|
const gen_cmd = gen_exe.run();
|
||||||
gen_cmd.addArgs(&.{
|
gen_cmd.addArgs(&.{
|
||||||
@ -141,6 +142,10 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
b.fmt("{s}/README.md", .{b.build_root}),
|
b.fmt("{s}/README.md", .{b.build_root}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (b.option([]const u8, "vscode-config-path", "Output path to vscode-config")) |path| {
|
||||||
|
gen_cmd.addArg(b.pathFromRoot(path));
|
||||||
|
}
|
||||||
|
|
||||||
const gen_step = b.step("gen", "Regenerate config files");
|
const gen_step = b.step("gen", "Regenerate config files");
|
||||||
gen_step.dependOn(&gen_cmd.step);
|
gen_step.dependOn(&gen_cmd.step);
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const tres = @import("tres");
|
||||||
|
|
||||||
const ConfigOption = struct {
|
const ConfigOption = struct {
|
||||||
/// Name of config option
|
/// Name of config option
|
||||||
@ -10,7 +12,7 @@ const ConfigOption = struct {
|
|||||||
/// 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`
|
/// If set, this option can be configured through `zls --config`
|
||||||
/// currently unused but could laer be used to automatically generate queries for setup.zig
|
/// currently unused but could later be used to automatically generate queries for setup.zig
|
||||||
setup_question: ?[]const u8,
|
setup_question: ?[]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,9 +48,7 @@ fn zigTypeToTypescript(ty: []const u8) ![]const u8 {
|
|||||||
fn generateConfigFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
fn generateConfigFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
_ = allocator;
|
_ = allocator;
|
||||||
|
|
||||||
const config_file = try std.fs.openFileAbsolute(path, .{
|
const config_file = try std.fs.createFileAbsolute(path, .{});
|
||||||
.mode = .write_only,
|
|
||||||
});
|
|
||||||
defer config_file.close();
|
defer config_file.close();
|
||||||
|
|
||||||
var buff_out = std.io.bufferedWriter(config_file.writer());
|
var buff_out = std.io.bufferedWriter(config_file.writer());
|
||||||
@ -69,10 +69,10 @@ fn generateConfigFile(allocator: std.mem.Allocator, config: Config, path: []cons
|
|||||||
\\{s}: {s} = {s},
|
\\{s}: {s} = {s},
|
||||||
\\
|
\\
|
||||||
, .{
|
, .{
|
||||||
std.mem.trim(u8, option.description, " \t\n\r"),
|
std.mem.trim(u8, option.description, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.name, " \t\n\r"),
|
std.mem.trim(u8, option.name, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.type, " \t\n\r"),
|
std.mem.trim(u8, option.type, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.default, " \t\n\r"),
|
std.mem.trim(u8, option.default, &std.ascii.whitespace),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ fn generateSchemaFile(allocator: std.mem.Allocator, config: Config, path: []cons
|
|||||||
\\ "properties":
|
\\ "properties":
|
||||||
);
|
);
|
||||||
|
|
||||||
try serializeObjectMap(properties, .{
|
try tres.stringify(properties, .{
|
||||||
.whitespace = .{
|
.whitespace = .{
|
||||||
.indent_level = 1,
|
.indent_level = 1,
|
||||||
},
|
},
|
||||||
@ -122,26 +122,26 @@ fn generateSchemaFile(allocator: std.mem.Allocator, config: Config, path: []cons
|
|||||||
|
|
||||||
_ = try buff_out.write("\n}\n");
|
_ = try buff_out.write("\n}\n");
|
||||||
try buff_out.flush();
|
try buff_out.flush();
|
||||||
|
try schema_file.setEndPos(try schema_file.getPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn updateREADMEFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
fn updateREADMEFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
var readme_file = try std.fs.openFileAbsolute(path, .{ .mode = .read_write });
|
var readme_file = try std.fs.openFileAbsolute(path, .{ .mode = .read_write });
|
||||||
defer readme_file.close();
|
defer readme_file.close();
|
||||||
|
|
||||||
var readme = std.ArrayListUnmanaged(u8){
|
var readme = try readme_file.readToEndAlloc(allocator, std.math.maxInt(usize));
|
||||||
.items = try readme_file.readToEndAlloc(allocator, std.math.maxInt(usize)),
|
defer allocator.free(readme);
|
||||||
};
|
|
||||||
defer readme.deinit(allocator);
|
|
||||||
|
|
||||||
const start_indicator = "<!-- DO NOT EDIT | THIS SECTION IS AUTO-GENERATED | DO NOT EDIT -->";
|
const start_indicator = "<!-- DO NOT EDIT | THIS SECTION IS AUTO-GENERATED | DO NOT EDIT -->";
|
||||||
const end_indicator = "<!-- 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 start = start_indicator.len + (std.mem.indexOf(u8, readme, start_indicator) orelse return error.SectionNotFound);
|
||||||
const end = std.mem.indexOfPos(u8, readme.items, start, end_indicator) orelse return error.SectionNotFound;
|
const end = std.mem.indexOfPos(u8, readme, start, end_indicator) orelse return error.SectionNotFound;
|
||||||
|
|
||||||
var new_readme = std.ArrayListUnmanaged(u8){};
|
try readme_file.seekTo(0);
|
||||||
defer new_readme.deinit(allocator);
|
var writer = readme_file.writer();
|
||||||
var writer = new_readme.writer(allocator);
|
|
||||||
|
try writer.writeAll(readme[0..start]);
|
||||||
|
|
||||||
try writer.writeAll(
|
try writer.writeAll(
|
||||||
\\
|
\\
|
||||||
@ -155,29 +155,95 @@ fn updateREADMEFile(allocator: std.mem.Allocator, config: Config, path: []const
|
|||||||
\\| `{s}` | `{s}` | `{s}` | {s} |
|
\\| `{s}` | `{s}` | `{s}` | {s} |
|
||||||
\\
|
\\
|
||||||
, .{
|
, .{
|
||||||
std.mem.trim(u8, option.name, " \t\n\r"),
|
std.mem.trim(u8, option.name, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.type, " \t\n\r"),
|
std.mem.trim(u8, option.type, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.default, " \t\n\r"),
|
std.mem.trim(u8, option.default, &std.ascii.whitespace),
|
||||||
std.mem.trim(u8, option.description, " \t\n\r"),
|
std.mem.trim(u8, option.description, &std.ascii.whitespace),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try readme.replaceRange(allocator, start, end - start, new_readme.items);
|
try writer.writeAll(readme[end..]);
|
||||||
|
|
||||||
try readme_file.seekTo(0);
|
try readme_file.setEndPos(try readme_file.getPos());
|
||||||
try readme_file.writeAll(readme.items);
|
}
|
||||||
|
|
||||||
|
const ConfigurationProperty = struct {
|
||||||
|
scope: []const u8 = "resource",
|
||||||
|
type: []const u8,
|
||||||
|
description: []const u8,
|
||||||
|
@"enum": ?[]const []const u8 = null,
|
||||||
|
format: ?[]const u8 = null,
|
||||||
|
default: ?std.json.Value = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn generateVSCodeConfigFile(allocator: std.mem.Allocator, config: Config, path: []const u8) !void {
|
||||||
|
var config_file = try std.fs.createFileAbsolute(path, .{});
|
||||||
|
defer config_file.close();
|
||||||
|
|
||||||
|
const predefined_configurations: usize = 3;
|
||||||
|
var configuration: std.StringArrayHashMapUnmanaged(ConfigurationProperty) = .{};
|
||||||
|
try configuration.ensureTotalCapacity(allocator, predefined_configurations + @intCast(u32, config.options.len));
|
||||||
|
defer {
|
||||||
|
for (configuration.keys()[predefined_configurations..]) |name| allocator.free(name);
|
||||||
|
configuration.deinit(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.putAssumeCapacityNoClobber("trace.server", .{
|
||||||
|
.scope = "window",
|
||||||
|
.type = "string",
|
||||||
|
.@"enum" = &.{"off", "message", "verbose"},
|
||||||
|
.description = "Traces the communication between VS Code and the language server.",
|
||||||
|
.default = .{.String = "off"},
|
||||||
|
});
|
||||||
|
configuration.putAssumeCapacityNoClobber("check_for_update", .{
|
||||||
|
.type = "boolean",
|
||||||
|
.description = "Whether to automatically check for new updates",
|
||||||
|
.default = .{.Bool = true},
|
||||||
|
});
|
||||||
|
configuration.putAssumeCapacityNoClobber("path", .{
|
||||||
|
.type = "string",
|
||||||
|
.description = "Path to `zls` executable. Example: `C:/zls/zig-cache/bin/zls.exe`.",
|
||||||
|
.format = "path",
|
||||||
|
.default = null,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (config.options) |option| {
|
||||||
|
const name = try std.fmt.allocPrint(allocator, "zls.{s}", .{option.name});
|
||||||
|
|
||||||
|
var parser = std.json.Parser.init(allocator, false);
|
||||||
|
const default = (try parser.parse(option.default)).root;
|
||||||
|
|
||||||
|
configuration.putAssumeCapacityNoClobber(name, .{
|
||||||
|
.type = try zigTypeToTypescript(option.type),
|
||||||
|
.description = option.description,
|
||||||
|
.format = if (std.mem.indexOf(u8, option.name, "path") != null) "path" else null,
|
||||||
|
.default = if(default == .Null) null else default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffered_writer = std.io.bufferedWriter(config_file.writer());
|
||||||
|
var writer = buffered_writer.writer();
|
||||||
|
|
||||||
|
try tres.stringify(configuration, .{
|
||||||
|
.whitespace = .{},
|
||||||
|
.emit_null_optional_fields = false,
|
||||||
|
}, writer);
|
||||||
|
|
||||||
|
try buffered_writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var arg_it = std.process.args();
|
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
var gpa = general_purpose_allocator.allocator();
|
||||||
|
|
||||||
|
var arg_it = try std.process.argsWithAllocator(gpa);
|
||||||
|
defer arg_it.deinit();
|
||||||
|
|
||||||
_ = arg_it.next() orelse @panic("");
|
_ = arg_it.next() orelse @panic("");
|
||||||
const config_path = arg_it.next() orelse @panic("first argument must be path to Config.zig");
|
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 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");
|
const readme_path = arg_it.next() orelse @panic("third argument must be path to README.md");
|
||||||
|
const maybe_vscode_config_path = arg_it.next();
|
||||||
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
var gpa = general_purpose_allocator.allocator();
|
|
||||||
|
|
||||||
const parse_options = std.json.ParseOptions{
|
const parse_options = std.json.ParseOptions{
|
||||||
.allocator = gpa,
|
.allocator = gpa,
|
||||||
@ -190,50 +256,19 @@ pub fn main() !void {
|
|||||||
try generateSchemaFile(gpa, config, schema_path);
|
try generateSchemaFile(gpa, config, schema_path);
|
||||||
try updateREADMEFile(gpa, config, readme_path);
|
try updateREADMEFile(gpa, config, readme_path);
|
||||||
|
|
||||||
std.log.warn(
|
if (maybe_vscode_config_path) |vscode_config_path| {
|
||||||
\\ If you have added a new configuration option and it should be configuration through the config wizard, then edit src/setup.zig
|
try generateVSCodeConfigFile(gpa, config, vscode_config_path);
|
||||||
, .{});
|
}
|
||||||
|
|
||||||
std.log.info(
|
if (builtin.os.tag == .windows) {
|
||||||
\\ Changing configuration options may also require editing the `package.json` from zls-vscode at https://github.com/zigtools/zls-vscode/blob/master/package.json
|
std.log.warn("Running on windows may result in CRLF and LF mismatch", .{});
|
||||||
, .{});
|
}
|
||||||
}
|
|
||||||
|
try std.io.getStdOut().writeAll(
|
||||||
fn serializeObjectMap(
|
\\If you have added a new configuration option and it should be configuration through the config wizard, then edit `src/setup.zig`
|
||||||
value: anytype,
|
\\
|
||||||
options: std.json.StringifyOptions,
|
\\Changing configuration options may also require editing the `package.json` from zls-vscode at https://github.com/zigtools/zls-vscode/blob/master/package.json
|
||||||
out_stream: anytype,
|
\\You can use `zig build gen -Dvscode-config-path=/path/to/output/file.json` to generate the new configuration properties which you can then copy into `package.json`
|
||||||
) @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('}');
|
|
||||||
}
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
// Run with node
|
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const sourceOfTruth = fs.readFileSync(path.join(__dirname, "..", "Config.zig"));
|
|
||||||
|
|
||||||
const lines = sourceOfTruth.toString().split("\n");
|
|
||||||
|
|
||||||
function mapType(type) {
|
|
||||||
switch (type) {
|
|
||||||
case "?[]const u8":
|
|
||||||
return "string";
|
|
||||||
|
|
||||||
case "bool":
|
|
||||||
return "boolean";
|
|
||||||
|
|
||||||
case "usize":
|
|
||||||
return "integer";
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error("unknown type!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let comment = null;
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line.startsWith("///")) {
|
|
||||||
if (comment === null) comment = line.slice(3).trim();
|
|
||||||
else comment += line.slice(3);
|
|
||||||
} else if (comment) {
|
|
||||||
const name = line.split(":")[0].trim();
|
|
||||||
const type = line.split(":")[1].split("=")[0].trim();
|
|
||||||
const defaultValue = line.split(":")[1].split("=")[1].trim().replace(",","");
|
|
||||||
|
|
||||||
console.log(`"zls.${name}": ${JSON.stringify({
|
|
||||||
"scope": "resource",
|
|
||||||
"type": mapType(type),
|
|
||||||
"description": comment,
|
|
||||||
"default": JSON.parse(defaultValue)
|
|
||||||
})},`);
|
|
||||||
|
|
||||||
comment = null;
|
|
||||||
}
|
|
||||||
}
|
|
2
src/tres
2
src/tres
@ -1 +1 @@
|
|||||||
Subproject commit 16774b94efa61757a5302a690837dfb8cf750a11
|
Subproject commit fb23d644500ae5b93dd71b5a8406d0c83e8e4fbe
|
Loading…
Reference in New Issue
Block a user