diff options
| -rw-r--r-- | src/dump.zig | 9 | ||||
| -rw-r--r-- | src/main.zig | 30 | ||||
| -rw-r--r-- | test/hxdiff.zig | 6 | ||||
| -rw-r--r-- | test/snapshot.zig | 8 | ||||
| -rw-r--r-- | test/snapshots/logquiet.golden | 31 | ||||
| -rw-r--r-- | test/snapshots/logquiet.snap | 16 |
6 files changed, 91 insertions, 9 deletions
diff --git a/src/dump.zig b/src/dump.zig index 06b9cec6..51221197 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -1,6 +1,11 @@ const std = @import("std"); const builtin = @import("builtin"); +// scoped, not bare std.log: main.zig's logFn drops the unscoped .default scope +// wholesale (ghostty and uucode log there too), and a corrupt dump's parse +// diagnostic is the only thing that says WHICH field went bad. +const log = std.log.scoped(.dump); + /// Where the next dump goes. $PARDES_DUMP wins verbatim (the snapshot harness /// pins it for deterministic goldens); else $XDG_DATA_HOME|~/.local/share /// /pardes/pardes-<utc>.zon — timestamped so dumps never overwrite each other. @@ -148,7 +153,7 @@ pub fn readZon(gpa: std.mem.Allocator, bytes: []const u8, label: []const u8) !St pub fn readZonZ(gpa: std.mem.Allocator, source: [:0]const u8, label: []const u8) !State { if (builtin.os.tag == .emscripten) { const state = std.zon.parse.fromSliceAlloc(State, gpa, source, null, .{}) catch |err| { - std.log.err("parse dump {s}: {s}", .{ label, @errorName(err) }); + log.err("parse dump {s}: {s}", .{ label, @errorName(err) }); return err; }; errdefer std.zon.parse.free(gpa, state); @@ -159,7 +164,7 @@ pub fn readZonZ(gpa: std.mem.Allocator, source: [:0]const u8, label: []const u8) var diag: std.zon.parse.Diagnostics = .{}; defer diag.deinit(gpa); const state = std.zon.parse.fromSliceAlloc(State, gpa, source, &diag, .{}) catch |err| { - std.log.err("parse dump {s}: {f}", .{ label, diag }); + log.err("parse dump {s}: {f}", .{ label, diag }); return err; }; errdefer std.zon.parse.free(gpa, state); diff --git a/src/main.zig b/src/main.zig index 47121691..61c0049e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,13 +18,29 @@ pub const std_options_debug_io: std.Io = if (is_emscripten) else std_options_debug_threaded_io.?.io(); -// ghostty-vt's stream logs every mode it doesn't implement, and in the tty -// shell stderr IS the screen — the warning scribbles over the grid. macOS -// bash/readline sends one such mode (1034, meta-key) on every startup, so -// keep the stream scope to errors; linux shells just never tripped it. -pub const std_options: std.Options = .{ - .log_scope_levels = &.{.{ .scope = .stream, .level = .err }}, -}; +// Every std.log call in the process — ours and every dependency's — funnels +// through this one function. ghostty-vt narrates whatever it does not +// implement in the bytes a child writes to its pty (`debug(stream)`, +// `warning(stream): ignoring unimplemented CSI p`, `debug(kitty_gfx)`; opening +// yazi is worth several lines before it has drawn anything), and in the tty +// shell stderr IS the screen — those land on top of the rendered grid, and in +// the gui/web shells on the console. So drop the libraries at every level: an +// `err` painted over the UI is no better than a debug one. Only pardes' own +// scopes get through, because their messages carry detail the error returns +// don't (gui's SDL_GetError strings, dump's zon parse diagnostic) — .default +// is NOT one of them, ghostty and uucode both log unscoped. Set PARDES_LOG to +// get the real logger back: `PARDES_LOG=1 pardes 2>/tmp/pardes.log`. +pub const std_options: std.Options = .{ .logFn = logFn }; + +fn logFn( + comptime level: std.log.Level, + comptime scope: @EnumLiteral(), + comptime format: []const u8, + args: anytype, +) void { + if (scope != .gui and scope != .dump and std.c.getenv("PARDES_LOG") == null) return; + std.log.defaultLog(level, scope, format, args); +} // A panic must restore the terminal (cooked mode, main screen, mouse off) // before the trace prints, or it lands garbled in a raw alt screen. recover() diff --git a/test/hxdiff.zig b/test/hxdiff.zig index dc901263..b04cc0b5 100644 --- a/test/hxdiff.zig +++ b/test/hxdiff.zig @@ -49,6 +49,12 @@ const std = @import("std"); const libc = std.c; const pardes = @import("pardes"); +// Its own root, so src/main.zig's logFn does not apply here: the core it +// drives feeds every case's text through ghostty-vt, which narrates each +// sequence it does not implement. Cut the libraries back to errors; the +// per-case report goes through std.debug, so a mismatch stays loud. +pub const std_options: std.Options = .{ .log_level = .err }; + const gpa = std.heap.page_allocator; pub fn main(init: std.process.Init) !void { diff --git a/test/snapshot.zig b/test/snapshot.zig index 54ba0271..5417530d 100644 --- a/test/snapshot.zig +++ b/test/snapshot.zig @@ -32,6 +32,14 @@ const libc = std.c; const ghostty_vt = @import("ghostty-vt"); const eh = @import("e2e_harness.zig"); +// This harness parses the app's output with its OWN ghostty-vt, which narrates +// every sequence it does not implement — vaxis's startup capability probes +// alone cost four `debug(stream)`/`warning(stream)`/`debug(kitty_gfx)` lines +// per script, burying the PASS/FAIL report under 200 lines of chatter. Cut the +// libraries back to errors; the report itself goes through std.debug, so a +// failure stays loud either way. +pub const std_options: std.Options = .{ .log_level = .err }; + extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; extern "c" fn execvp(file: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; diff --git a/test/snapshots/logquiet.golden b/test/snapshots/logquiet.golden new file mode 100644 index 00000000..86b1567a --- /dev/null +++ b/test/snapshots/logquiet.golden @@ -0,0 +1,31 @@ +== snap quiet grid=100x30 cursor=62,4 +|Kill Newcol Tutor Debug Colors NextColor Crt Dump +| TTY /tmp/pardes-snap/logquiet/cwd Del +| $ ls +| $ printf '\033_Ga=q,i=1,s=1,v=1,f=24,t=d;AAAA\033\\\033[>0p' +| $ '\033_Ga=q,i=1,s=1,v=1,f=24,t=d;AAAA\033\\\033[>0p'Gi=1;OK +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| +| diff --git a/test/snapshots/logquiet.snap b/test/snapshots/logquiet.snap new file mode 100644 index 00000000..02d52495 --- /dev/null +++ b/test/snapshots/logquiet.snap @@ -0,0 +1,16 @@ +# ghostty-vt narrates every sequence it does not implement through std.log, +# and a pardes child's stderr is this pty — so a library log line lands on the +# rendered grid, exactly where it does on a real user's terminal. The two +# sequences below are the cheapest ones that trip it: a kitty graphics query +# (`debug(kitty_gfx): executing kitty graphics command`) and a private CSI p +# (`warning(stream): ignoring unimplemented CSI p with intermediates: >`). +# The golden is the assertion that neither reaches the screen. +start 30 100 -n 1 +wait 8000 Kill Newcol +stable 700 20000 +key c-b +stable 600 8000 +text printf '\033_Ga=q,i=1,s=1,v=1,f=24,t=d;AAAA\033\\\033[>0p' +key enter +stable 700 20000 +snap quiet |
