const std = @import("std"); const bun = @import("root").bun; const MutableString = bun.MutableString; const Output = bun.Output; const default_allocator = bun.default_allocator; const string = bun.string; const JSC = bun.JSC; const JSValue = JSC.JSValue; const JSGlobalObject = JSC.JSGlobalObject; const ZigConsoleClient = JSC.ZigConsoleClient; const DiffMatchPatch = @import("../../deps/diffz/DiffMatchPatch.zig"); pub const DiffFormatter = struct { received_string: ?string = null, expected_string: ?string = null, received: ?JSValue = null, expected: ?JSValue = null, globalObject: *JSGlobalObject, not: bool = false, pub fn format(this: DiffFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { if (this.expected_string != null and this.received_string != null) { const received = this.received_string.?; const expected = this.expected_string.?; var dmp = DiffMatchPatch.default; dmp.diff_timeout = 200; var diffs = try dmp.diff(default_allocator, received, expected, false); defer diffs.deinit(default_allocator); const equal_fmt = "{s}"; const delete_fmt = "{s}"; const insert_fmt = "{s}"; try writer.writeAll("Expected: "); for (diffs.items) |df| { switch (df.operation) { .delete => continue, .insert => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text}); } }, .equal => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text}); } }, } } try writer.writeAll("\nReceived: "); for (diffs.items) |df| { switch (df.operation) { .insert => continue, .delete => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text}); } }, .equal => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text}); } }, } } return; } if (this.received == null or this.expected == null) return; const received = this.received.?; const expected = this.expected.?; var received_buf = MutableString.init(default_allocator, 0) catch unreachable; var expected_buf = MutableString.init(default_allocator, 0) catch unreachable; defer { received_buf.deinit(); expected_buf.deinit(); } { var buffered_writer_ = MutableString.BufferedWriter{ .context = &received_buf }; var buffered_writer = &buffered_writer_; var buf_writer = buffered_writer.writer(); const Writer = @TypeOf(buf_writer); const fmt_options = ZigConsoleClient.FormatOptions{ .enable_colors = false, .add_newline = false, .flush = false, .ordered_properties = true, .quote_strings = true, }; ZigConsoleClient.format( .Debug, this.globalObject, @ptrCast([*]const JSValue, &received), 1, Writer, Writer, buf_writer, fmt_options, ); buffered_writer.flush() catch unreachable; buffered_writer_.context = &expected_buf; ZigConsoleClient.format( .Debug, this.globalObject, @ptrCast([*]const JSValue, &this.expected), 1, Writer, Writer, buf_writer, fmt_options, ); buffered_writer.flush() catch unreachable; } const received_slice = received_buf.toOwnedSliceLeaky(); const expected_slice = expected_buf.toOwnedSliceLeaky(); if (this.not) { const not_fmt = "Expected: not {s}"; if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(not_fmt, true), .{expected_slice}); } else { try writer.print(Output.prettyFmt(not_fmt, false), .{expected_slice}); } return; } switch (received.determineDiffMethod(expected, this.globalObject)) { .none => { const fmt = "Expected: {any}\nReceived: {any}"; var formatter = ZigConsoleClient.Formatter{ .globalThis = this.globalObject, .quote_strings = true }; if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(fmt, true), .{ expected.toFmt(this.globalObject, &formatter), received.toFmt(this.globalObject, &formatter), }); return; } try writer.print(Output.prettyFmt(fmt, true), .{ expected.toFmt(this.globalObject, &formatter), received.toFmt(this.globalObject, &formatter), }); return; }, .character => { var dmp = DiffMatchPatch.default; dmp.diff_timeout = 200; var diffs = try dmp.diff(default_allocator, received_slice, expected_slice, false); defer diffs.deinit(default_allocator); const equal_fmt = "{s}"; const delete_fmt = "{s}"; const insert_fmt = "{s}"; try writer.writeAll("Expected: "); for (diffs.items) |df| { switch (df.operation) { .delete => continue, .insert => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text}); } }, .equal => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text}); } }, } } try writer.writeAll("\nReceived: "); for (diffs.items) |df| { switch (df.operation) { .insert => continue, .delete => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text}); } }, .equal => { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text}); } else { try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text}); } }, } } return; }, .line => { var dmp = DiffMatchPatch.default; dmp.diff_timeout = 200; var diffs = try dmp.diffLines(default_allocator, received_slice, expected_slice); defer diffs.deinit(default_allocator); const equal_fmt = " {s}"; const delete_fmt = "+ {s}"; const insert_fmt = "- {s}"; var insert_count: usize = 0; var delete_count: usize = 0; for (diffs.items) |df| { var prev: usize = 0; var curr: usize = 0; switch (df.operation) { .equal => { while (curr < df.text.len) { if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) { if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(equal_fmt, true), .{df.text[prev .. curr + 1]}); } else { try writer.print(Output.prettyFmt(equal_fmt, false), .{df.text[prev .. curr + 1]}); } prev = curr + 1; } curr += 1; } }, .insert => { while (curr < df.text.len) { if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) { insert_count += 1; if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(insert_fmt, true), .{df.text[prev .. curr + 1]}); } else { try writer.print(Output.prettyFmt(insert_fmt, false), .{df.text[prev .. curr + 1]}); } prev = curr + 1; } curr += 1; } }, .delete => { while (curr < df.text.len) { if (curr == df.text.len - 1 or df.text[curr] == '\n' and curr != 0) { delete_count += 1; if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt(delete_fmt, true), .{df.text[prev .. curr + 1]}); } else { try writer.print(Output.prettyFmt(delete_fmt, false), .{df.text[prev .. curr + 1]}); } prev = curr + 1; } curr += 1; } }, } if (df.text[df.text.len - 1] != '\n') try writer.writeAll("\n"); } if (Output.enable_ansi_colors) { try writer.print(Output.prettyFmt("\n- Expected - {d}\n", true), .{insert_count}); try writer.print(Output.prettyFmt("+ Received + {d}", true), .{delete_count}); return; } try writer.print("\n- Expected - {d}\n", .{insert_count}); try writer.print("+ Received + {d}", .{delete_count}); return; }, .word => { // not implemented // https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs#word-mode }, } return; } };