From 53888e53935fff9657a0322a1fe6b504b3a01d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Sun, 4 Jan 2026 07:07:57 +0100 Subject: [PATCH] Pretty-print JSON diagnostics in snapshots --- build.zig | 1 + src/main.zig | 28 ++++++-- .../reject/container_children.diag | 33 ++++++++-- test/conformance/reject/heading_sequence.diag | 65 +++++++++++++++++-- .../reject/inline_identifier_dash.diag | 15 ++++- test/conformance/reject/nested_top_level.diag | 13 +++- test/conformance/reject/ref_in_heading.diag | 15 ++++- test/conformance/reject/string_cr_escape.diag | 15 ++++- .../conformance/reject/time_relative_fmt.diag | 13 +++- 9 files changed, 171 insertions(+), 27 deletions(-) diff --git a/build.zig b/build.zig index 8d8607f..2570831 100644 --- a/build.zig +++ b/build.zig @@ -112,6 +112,7 @@ pub fn build(b: *std.Build) void { const diag_file = b.fmt("{s}.diag", .{path[0 .. path.len - 5]}); const test_run = b.addRunArtifact(exe); + test_run.addArgs(&.{"--json-diagnostics"}); test_run.addFileArg(b.path(path)); test_run.expectExitCode(1); const generated_diag = test_run.captureStdErr(); diff --git a/src/main.zig b/src/main.zig index 776d241..fffdc9d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -34,17 +34,25 @@ pub fn main() !u8 { options, ); - for (diagnostics.items.items) |diag| { - try stderr.interface.print("{s}:{f}: {f}\n", .{ - options.file_path, - diag.location, - diag.code, - }); + if (options.json_diagnostics) { + const json_options: std.json.Stringify.Options = .{ .whitespace = .indent_2 }; + try std.json.Stringify.value(diagnostics.items.items, json_options, &stderr.interface); + try stderr.interface.writeByte('\n'); + } else { + for (diagnostics.items.items) |diag| { + try stderr.interface.print("{s}:{f}: {f}\n", .{ + options.file_path, + diag.location, + diag.code, + }); + } } try stderr.interface.flush(); parse_result catch |err| { - std.log.err("failed to parse \"{s}\": {t}", .{ options.file_path, err }); + if (!options.json_diagnostics) { + std.log.err("failed to parse \"{s}\": {t}", .{ options.file_path, err }); + } return 1; }; @@ -73,6 +81,7 @@ fn parse_and_process(allocator: std.mem.Allocator, diagnostics: *hdoc.Diagnostic const CliOptions = struct { format: RenderFormat = .html, file_path: []const u8, + json_diagnostics: bool = false, }; const RenderFormat = enum { @@ -98,6 +107,11 @@ fn parse_options(stderr: *std.Io.Writer, argv: []const []const u8) !CliOptions { i += 1; continue; } + if (std.mem.eql(u8, value, "--json-diagnostics")) { + options.json_diagnostics = true; + i += 1; + continue; + } return error.InvalidCli; } diff --git a/test/conformance/reject/container_children.diag b/test/conformance/reject/container_children.diag index d6354d0..9d4bba4 100644 --- a/test/conformance/reject/container_children.diag +++ b/test/conformance/reject/container_children.diag @@ -1,4 +1,29 @@ -/workspace/hyperdoc/test/conformance/reject/container_children.hdoc:5:5: Node not allowed here. -/workspace/hyperdoc/test/conformance/reject/container_children.hdoc:4:3: Node requires list body. -/workspace/hyperdoc/test/conformance/reject/container_children.hdoc:10:3: Node not allowed here. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/container_children.hdoc": InvalidFile +[ + { + "code": { + "illegal_child_item": {} + }, + "location": { + "line": 5, + "column": 5 + } + }, + { + "code": { + "list_body_required": {} + }, + "location": { + "line": 4, + "column": 3 + } + }, + { + "code": { + "illegal_child_item": {} + }, + "location": { + "line": 10, + "column": 3 + } + } +] diff --git a/test/conformance/reject/heading_sequence.diag b/test/conformance/reject/heading_sequence.diag index ecae9b9..02e90f2 100644 --- a/test/conformance/reject/heading_sequence.diag +++ b/test/conformance/reject/heading_sequence.diag @@ -1,6 +1,59 @@ -/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc:3:1: h3 requires a preceding h2. -/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc:5:1: h3 requires a preceding h2. -/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc:3:1: Inserted automatic h1 to fill heading level gap. -/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc:3:1: Inserted automatic h2 to fill heading level gap. -/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc:5:1: Inserted automatic h2 to fill heading level gap. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/heading_sequence.hdoc": InvalidFile +[ + { + "code": { + "invalid_heading_sequence": { + "level": "h3", + "missing": "h2" + } + }, + "location": { + "line": 3, + "column": 1 + } + }, + { + "code": { + "invalid_heading_sequence": { + "level": "h3", + "missing": "h2" + } + }, + "location": { + "line": 5, + "column": 1 + } + }, + { + "code": { + "automatic_heading_insertion": { + "level": "h1" + } + }, + "location": { + "line": 3, + "column": 1 + } + }, + { + "code": { + "automatic_heading_insertion": { + "level": "h2" + } + }, + "location": { + "line": 3, + "column": 1 + } + }, + { + "code": { + "automatic_heading_insertion": { + "level": "h2" + } + }, + "location": { + "line": 5, + "column": 1 + } + } +] diff --git a/test/conformance/reject/inline_identifier_dash.diag b/test/conformance/reject/inline_identifier_dash.diag index 0528512..3c8dfb8 100644 --- a/test/conformance/reject/inline_identifier_dash.diag +++ b/test/conformance/reject/inline_identifier_dash.diag @@ -1,2 +1,13 @@ -/workspace/hyperdoc/test/conformance/reject/inline_identifier_dash.hdoc:3:9: Invalid identifier character: '-'. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/inline_identifier_dash.hdoc": SyntaxError +[ + { + "code": { + "invalid_identifier_character": { + "char": 45 + } + }, + "location": { + "line": 3, + "column": 9 + } + } +] diff --git a/test/conformance/reject/nested_top_level.diag b/test/conformance/reject/nested_top_level.diag index 064fdbe..76ea6e6 100644 --- a/test/conformance/reject/nested_top_level.diag +++ b/test/conformance/reject/nested_top_level.diag @@ -1,2 +1,11 @@ -/workspace/hyperdoc/test/conformance/reject/nested_top_level.hdoc:4:3: Node not allowed here. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/nested_top_level.hdoc": InvalidFile +[ + { + "code": { + "illegal_child_item": {} + }, + "location": { + "line": 4, + "column": 3 + } + } +] diff --git a/test/conformance/reject/ref_in_heading.diag b/test/conformance/reject/ref_in_heading.diag index 60d0cd0..68602ea 100644 --- a/test/conformance/reject/ref_in_heading.diag +++ b/test/conformance/reject/ref_in_heading.diag @@ -1,2 +1,13 @@ -/workspace/hyperdoc/test/conformance/reject/ref_in_heading.hdoc:5:14: \\ref is not allowed in this context. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/ref_in_heading.hdoc": InvalidFile +[ + { + "code": { + "inline_not_allowed": { + "node_type": "\\ref" + } + }, + "location": { + "line": 5, + "column": 14 + } + } +] diff --git a/test/conformance/reject/string_cr_escape.diag b/test/conformance/reject/string_cr_escape.diag index f85f8c7..ac57a8b 100644 --- a/test/conformance/reject/string_cr_escape.diag +++ b/test/conformance/reject/string_cr_escape.diag @@ -1,2 +1,13 @@ -/workspace/hyperdoc/test/conformance/reject/string_cr_escape.hdoc:3:8: Forbidden control character U+000D. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/string_cr_escape.hdoc": InvalidFile +[ + { + "code": { + "illegal_character": { + "codepoint": 13 + } + }, + "location": { + "line": 3, + "column": 8 + } + } +] diff --git a/test/conformance/reject/time_relative_fmt.diag b/test/conformance/reject/time_relative_fmt.diag index 5cbffa2..decc5a5 100644 --- a/test/conformance/reject/time_relative_fmt.diag +++ b/test/conformance/reject/time_relative_fmt.diag @@ -1,2 +1,11 @@ -/workspace/hyperdoc/test/conformance/reject/time_relative_fmt.hdoc:3:15: Invalid 'fmt' value for date/time. -error: failed to parse "/workspace/hyperdoc/test/conformance/reject/time_relative_fmt.hdoc": InvalidFile +[ + { + "code": { + "invalid_date_time_fmt": {} + }, + "location": { + "line": 3, + "column": 15 + } + } +]