From df0b51b6299bb4f6d79791b819e1aa627137b3aa Mon Sep 17 00:00:00 2001 From: nullptrdevs <16590917+nullptrdevs@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:17:26 -0800 Subject: [PATCH 1/3] Some fixes from downstream --- src/base.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base.zig b/src/base.zig index 3bdc5d5..1e68607 100644 --- a/src/base.zig +++ b/src/base.zig @@ -68,7 +68,7 @@ pub fn Map(comptime Key: type, comptime Value: type) type { pub fn UnionParser(comptime T: type) type { return struct { pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) std.json.ParseError(@TypeOf(source.*))!T { - const json_value = try std.json.parseFromTokenSourceLeaky(std.json.Value, allocator, source, options); + const json_value = try std.json.Value.jsonParse(allocator, source, options); return try jsonParseFromValue(allocator, json_value, options); } @@ -95,7 +95,7 @@ pub fn EnumCustomStringValues(comptime T: type, comptime contains_empty_enum: bo const KV = struct { []const u8, T }; const fields = @typeInfo(T).Union.fields; var kvs_array: [fields.len - 1]KV = undefined; - inline for (fields[0 .. fields.len - 1], 0..) |field, i| { + for (fields[0 .. fields.len - 1], 0..) |field, i| { kvs_array[i] = .{ field.name, @field(T, field.name) }; } break :build_kvs kvs_array[0..]; From f4780cfdffe7996b2a5256acfe57a8951967ecea Mon Sep 17 00:00:00 2001 From: nullptrdevs <16590917+nullptrdevs@users.noreply.github.com> Date: Sun, 10 Dec 2023 01:16:25 -0800 Subject: [PATCH 2/3] Update base.zig --- src/base.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base.zig b/src/base.zig index 1e68607..443e33f 100644 --- a/src/base.zig +++ b/src/base.zig @@ -117,7 +117,7 @@ pub fn EnumCustomStringValues(comptime T: type, comptime contains_empty_enum: bo } pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) std.json.ParseError(@TypeOf(source.*))!T { - const slice = try std.json.parseFromTokenSourceLeaky([]const u8, allocator, source, options); + const slice = try std.json.Value.jsonParse(allocator, source, options); if (contains_empty_enum and slice.len == 0) return .empty; return map.get(slice) orelse return .{ .custom_value = slice }; } From bb83587d50f8c50f13f8dda86b8fce6582d04a6d Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 16 Feb 2024 03:31:14 +0100 Subject: [PATCH 3/3] more debitrotting Note that the tests still don't work --- .gitattributes | 1 + .github/workflows/main.yml | 18 ++++----- README.md | 2 +- build.zig | 5 ++- libs/tres | 1 - src/MetaModel.zig | 12 ------ src/base.zig | 2 +- src/main.zig | 23 +++++++++-- tests/tests.zig | 78 ++++++++------------------------------ 9 files changed, 48 insertions(+), 94 deletions(-) delete mode 160000 libs/tres diff --git a/.gitattributes b/.gitattributes index b27d219..2a74073 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ * text=auto *.zig text=auto eol=lf +*.zon text=auto eol=lf diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d255c08..b107ee2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,28 +12,26 @@ jobs: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - uses: goto-bus-stop/setup-zig@v1 + - uses: actions/checkout@v4 + + - uses: goto-bus-stop/setup-zig@v2 with: version: master - - run: zig version - run: zig env - - name: Build - run: zig build + - run: zig build - name: Build artifacts - if: ${{ matrix.os == 'ubuntu-latest' }} run: | - mkdir -p "artifacts/" - cd artifacts wget https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/main/protocol/metaModel.json - ../zig-out/bin/lspmm-zig + zig build run -- metaModel.json artifacts/lsp.zig + + - run: zig fmt --ast-check --check artifacts/lsp.zig - name: Upload artifacts if: ${{ matrix.os == 'ubuntu-latest' }} uses: actions/upload-artifact@v2 with: name: builds - path: artifacts/* \ No newline at end of file + path: artifacts/* diff --git a/README.md b/README.md index 42de039..bfeae3a 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,5 @@ Zig LSP codegen from the newly released, official metamodel! This actually good 1. `git clone` 2. Plop `metaModel.json` in this cloned repo. A copy can be found [here](https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/metaModel.json). -3. `zig build run` +3. `zig build run -- metaModel.json lsp.zig` 4. Tada! You should now have a `lsp.zig` file that can be used to your heart's content! Enjoy :) diff --git a/build.zig b/build.zig index 3fd1247..b3edd9d 100644 --- a/build.zig +++ b/build.zig @@ -1,11 +1,11 @@ const std = @import("std"); -pub fn build(b: *std.build.Builder) void { +pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const exe = b.addExecutable(.{ - .name = "lspmm-zig", + .name = "zig-lsp-codegen", .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, @@ -28,6 +28,7 @@ pub fn build(b: *std.build.Builder) void { const tests = b.addTest(.{ .root_source_file = .{ .path = "tests/tests.zig" }, .target = target, + .optimize = optimize, }); test_step.dependOn(&b.addRunArtifact(tests).step); diff --git a/libs/tres b/libs/tres deleted file mode 160000 index 93d0cf7..0000000 --- a/libs/tres +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 93d0cf792f05f67f30df16cde41ecdb375817d62 diff --git a/src/MetaModel.zig b/src/MetaModel.zig index ac5cd5f..e6d9d11 100644 --- a/src/MetaModel.zig +++ b/src/MetaModel.zig @@ -27,10 +27,6 @@ pub const BaseTypes = enum { string, boolean, null, - - pub fn jsonStringify(self: @This(), options: std.json.StringifyOptions, out_stream: anytype) !void { - try std.json.stringify(@tagName(self), options, out_stream); - } }; pub const TypeKind = enum { @@ -45,10 +41,6 @@ pub const TypeKind = enum { stringLiteral, integerLiteral, booleanLiteral, - - pub fn jsonStringify(self: @This(), options: std.json.StringifyOptions, out_stream: anytype) !void { - try std.json.stringify(@tagName(self), options, out_stream); - } }; /// Indicates in which direction a message is sent in the protocol. @@ -56,10 +48,6 @@ pub const MessageDirection = enum { clientToServer, serverToClient, both, - - pub fn jsonStringify(self: @This(), options: std.json.StringifyOptions, out_stream: anytype) !void { - try std.json.stringify(@tagName(self), options, out_stream); - } }; /// Represents a base type like `string` or `DocumentUri`. diff --git a/src/base.zig b/src/base.zig index 443e33f..779b025 100644 --- a/src/base.zig +++ b/src/base.zig @@ -123,7 +123,7 @@ pub fn EnumCustomStringValues(comptime T: type, comptime contains_empty_enum: bo } pub fn jsonParseFromValue(allocator: std.mem.Allocator, source: std.json.Value, options: std.json.ParseOptions) std.json.ParseFromValueError!T { - const slice = try std.json.parseFromValueLeaky([]const u8, allocator, source, options); + const slice = try std.json.innerParse([]const u8, allocator, source, options); if (contains_empty_enum and slice.len == 0) return .empty; return map.get(slice) orelse return .{ .custom_value = slice }; } diff --git a/src/main.zig b/src/main.zig index 115b729..19fecc6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7,10 +7,16 @@ pub fn main() !void { const gpa = general_purpose_allocator.allocator(); - const model_file = try std.fs.cwd().openFile("metaModel.json", .{}); - defer model_file.close(); + var arg_it = try std.process.ArgIterator.initWithAllocator(gpa); - const model_file_source = try model_file.readToEndAlloc(gpa, std.math.maxInt(usize)); + _ = arg_it.skip(); // skip self exe + const model_file_path = try gpa.dupe(u8, arg_it.next() orelse std.debug.panic("first argument must be the path to the metaModel.json", .{})); + defer gpa.free(model_file_path); + + const out_file_path = try gpa.dupe(u8, arg_it.next() orelse std.debug.panic("second argument must be the output path to the generated zig code", .{})); + defer gpa.free(out_file_path); + + const model_file_source = try std.fs.cwd().readFileAlloc(gpa, model_file_path, std.math.maxInt(usize)); defer gpa.free(model_file_source); const json_value = try std.json.parseFromSlice(std.json.Value, gpa, model_file_source, .{}); @@ -37,7 +43,9 @@ pub fn main() !void { } else try zig_tree.render(gpa); defer if (zig_tree.errors.len == 0) gpa.free(output_source); - var out_file = try std.fs.cwd().createFile("lsp.zig", .{}); + std.fs.cwd().makePath(std.fs.path.dirname(out_file_path) orelse ".") catch {}; + + var out_file = try std.fs.cwd().createFile(out_file_path, .{}); defer out_file.close(); try out_file.writeAll(output_source); @@ -379,11 +387,18 @@ fn writeEnumeration(writer: anytype, meta_model: MetaModel, enumeration: MetaMod .uinteger => try writer.print("pub const {} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.name)}), } + // WORKAROUND: the enumeration value `pascal` appears twice in LanguageKind + var found_pascal = false; + var contains_empty_enum = false; for (enumeration.values) |entry| { if (entry.documentation) |docs| try writeDocs(writer, docs); switch (entry.value) { .string => |value| { + if (std.mem.eql(u8, value, "pascal")) { + if (found_pascal) continue; + found_pascal = true; + } if (value.len == 0) contains_empty_enum = true; const name = if (value.len == 0) "empty" else value; try writer.print("{},\n", .{std.zig.fmtId(name)}); diff --git a/tests/tests.zig b/tests/tests.zig index 76b8a4d..c2ba087 100644 --- a/tests/tests.zig +++ b/tests/tests.zig @@ -2,13 +2,6 @@ const Server = @This(); const lsp = @import("lsp"); const std = @import("std"); -const tres = @import("tres"); - -arena: std.heap.ArenaAllocator, -parser: std.json.Parser, - -read_buf: std.ArrayList(u8), -write_buf: std.ArrayList(u8), const SampleDirection = enum { client_to_server, @@ -36,72 +29,34 @@ const SampleEntryKind = enum { // TODO: Handle responses const SampleEntry = struct { isLSPMessage: bool, - @"type": SampleEntryKind, + type: SampleEntryKind, message: std.json.Value, }; -pub fn readLine(self: *Server, reader: anytype) !void { - while (true) { - var byte = try reader.readByte(); - - if (byte == '\n') { - return; - } - - if (self.read_buf.items.len == self.read_buf.capacity) { - try self.read_buf.ensureTotalCapacity(self.read_buf.capacity + 1); - } - - try self.read_buf.append(byte); - } -} - -pub fn flushArena(self: *Server) void { - self.arena.deinit(); - self.arena.state = .{}; -} - test { - @setEvalBranchQuota(100_000); - - var log_dir = try std.fs.cwd().openDir("samples", .{ .iterate = true }); - defer log_dir.close(); - - var log = try log_dir.openFile("amogus-json.log", .{}); - defer log.close(); + var log_file = try std.fs.cwd().openFile("samples/amogus-json.log", .{}); + defer log_file.close(); - var reader = log.reader(); - // reader.readAll() + const reader = log_file.reader(); - const allocator = std.heap.page_allocator; - var arena = std.heap.ArenaAllocator.init(allocator); - - var server = Server{ - .arena = arena, - .parser = std.json.Parser.init(arena.allocator(), false), - - .read_buf = try std.ArrayList(u8).initCapacity(allocator, 1024), - .write_buf = try std.ArrayList(u8).initCapacity(allocator, 1024), - }; - - var parser = std.json.Parser.init(server.arena.allocator(), false); + var read_buffer = std.ArrayList(u8).init(std.testing.allocator); + defer read_buffer.deinit(); while (true) { - server.readLine(reader) catch |err| switch (err) { - error.EndOfStream => return, - else => return std.log.err("{s}", .{err}), + try reader.readUntilDelimiterArrayList(&read_buffer, '\n', std.math.maxInt(u32)) catch |err| switch (err) { + error.EndOfStream => break, + else => |e| return e, }; - const tree = try parser.parse(server.read_buf.items); - defer parser.reset(); - const entry = try tres.parse(SampleEntry, tree.root, arena.allocator()); + const parsed_sample_entry = try std.json.parseFromSlice(SampleEntry, std.testing.allocator, read_buffer.items, .{}); + const sample_entry = parsed_sample_entry.value; - if (entry.isLSPMessage) { - switch (entry.@"type") { + if (sample_entry.isLSPMessage) { + switch (sample_entry.type) { .@"send-notification", .@"receive-notification", => a: { - _ = tres.parse(lsp.Notification, entry.message, allocator) catch |err| { + _ = tres.parse(lsp.Notification, sample_entry.message, std.testing.allocator) catch |err| { // Ignore unknown methods such as custom VSCode LSP methods if (err == error.UnknownMethod) break :a; std.log.err("Cannot handle Request or Notification of method \"{s}\"", .{entry.message.Object.get("method").?.String}); @@ -111,7 +66,7 @@ test { .@"send-request", .@"receive-request", => a: { - _ = tres.parse(lsp.Request, entry.message, allocator) catch |err| { + _ = tres.parse(lsp.Request, entry.message, std.testing.allocator) catch |err| { // Ignore unknown methods such as custom VSCode LSP methods if (err == error.UnknownMethod) break :a; std.log.err("Cannot handle Request or Notification of method \"{s}\"", .{entry.message.Object.get("method").?.String}); @@ -121,8 +76,5 @@ test { else => {}, } } - - server.read_buf.items.len = 0; - // arena.deinit(); } }