From 740effb5c9e18e7638fde196000030c899e8798e Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 07:29:46 -0300 Subject: A root exec or look that fails logs its err against the pane it ran at `err - exec: ...` said nothing of where the line ran; the root's exec and look run at the keyboard's pane, and their err now names it, as a click there would. Co-Authored-By: Claude Opus 5.5 --- src/ninep/ctl.zig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/ninep/ctl.zig') diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 7c68210a..4f52d661 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -170,6 +170,7 @@ pub fn completeEnd(p: *Pardes, data: []const u8) usize { /// A null `serial` clicks at whichever pane is active as each line runs, /// which is what the root's own look and exec mean; a serial stays put. pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Reply { + p.fs.ran_at = 0; for ([2]bool{ false, true }) |apply| { if (apply) p.fs.results_len = 0; var it = Messages.init(p, req.data); @@ -181,6 +182,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep if (!apply) continue; const id = if (serial) |s| p.paneBySerial(s) orelse break else p.active; if (p.panes[id] == null) return Reply.fail(req.tag, E.NOENT); + p.fs.ran_at = p.panes[id].?.serial; p.fs.no_pane_slot = false; if (captured(p, req, id, exec, text, in_tag)) |refusal| return refusal; // A pane it would have opened had no slot: the write fails, and @@ -1728,6 +1730,17 @@ test "Mount dials its peer and fails, mounting nothing, when none answers; a loo try testing.expect(th.logHas(p, "look: /n/gone/f.txt: dial failed: no answer")); } +test "a root exec that fails logs its err against the pane it ran at" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const f = &p.panes[0].?.file.?; + f.saved_revision = f.revision -% 1; + f.saved_hash = 0; + try testing.expect(wr(p, root_exec, "Exit\n").errno() != 0); + var want: [32]u8 = undefined; + try testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "err {d} exec: ", .{serialOf(p)}))); +} + test "size is monotonic: growing is never refused, and a size once taken is taken again" { const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 }); defer p.deinit(); @@ -1983,7 +1996,10 @@ test "a builtin that fails through exec, tagexec or a column's exec fails the wr const h = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle; const text = call(p, .{ .tag = 2, .op = .read, .node = log, .handle = h, .size = 1 << 16 }).bytes; try testing.expect(std.mem.indexOf(u8, text, "msg ") == null); - try testing.expectEqual(@as(usize, 4), std.mem.count(u8, text, "Kill: no running command has that first word")); + // The root's exec ran at the pane, so its err and the pane's exec's are + // the same record, counted. + try testing.expectEqual(@as(usize, 3), std.mem.count(u8, text, "Kill: no running command has that first word")); + try testing.expect(std.mem.indexOf(u8, text, "first word (x2)") != null); _ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h }); // A control character is refused with its reason. try testing.expectEqualStrings(e_control, wr(p, root_exec, "Msg a\x01b\n").reply.ename); -- cgit v1.3