diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 10:49:56 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 68a74ec034a24c9b016497880e375f291498ae5f (patch) | |
| tree | 968a3eeec731d3ea632ab98f872074863ec6183b | |
| parent | 73e797e3f31eb52b0074b86cd16949c827b75670 (diff) | |
| download | pardes-68a74ec034a24c9b016497880e375f291498ae5f.tar.gz pardes-68a74ec034a24c9b016497880e375f291498ae5f.zip | |
A terminal's new directory is a rename in the log, and its ctl counts its body
The log said new N / and later del N <cwd> for a terminal, with no rename
in between, though a terminal is named by its directory. setCwd now logs
the rename once the pane has been announced. A terminal's ctl line gave
body length 0 while its body reads its history; it now counts that
history (without keeping a copy).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/fs.md | 3 | ||||
| -rw-r--r-- | src/Terminal.zig | 16 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 3 | ||||
| -rw-r--r-- | src/ninep/pty.zig | 29 | ||||
| -rw-r--r-- | src/pardes.zig | 3 |
5 files changed, 52 insertions, 2 deletions
@@ -294,7 +294,8 @@ entries carry no sizes, and neither does `/screen`, which has no length until an open renders its frame; stat the entry. `/log` is one ring (64 KiB) that records whether or not anyone reads it: -`new`, `del`, `rename` and `save <serial> <name>`, and `msg <serial|-> <text>` +`new`, `del`, `rename` (a terminal's too, as its shell changes directory, +since a terminal is named by its directory) and `save <serial> <name>`, and `msg <serial|-> <text>` for every line the editor says, repeats included (with `verbose` on, that includes each builtin announcing itself as it runs). A `msg` said while a pane is being made can precede that pane's `new`; panes present at boot are diff --git a/src/Terminal.zig b/src/Terminal.zig index 16d5af26..0bec3ce8 100644 --- a/src/Terminal.zig +++ b/src/Terminal.zig @@ -368,6 +368,22 @@ pub fn screenTextAlloc(pane: *Pane, gpa: std.mem.Allocator) ![]const u8 { return state.vt.screens.active.dumpStringAlloc(gpa, .{ .screen = .{} }); } +/// How long `screenTextAlloc` would be, counted without keeping it: the +/// length a terminal's body reads as, which its ctl line reports. +pub fn screenTextLen(pane: *Pane) usize { + if (comptime !enabled) return 0; + const state = pane.terminal orelse return 0; + const screen = state.vt.screens.active; + var buf: [256]u8 = undefined; + var counting: std.Io.Writer.Discarding = .init(&buf); + screen.dumpString(&counting.writer, .{ + .tl = screen.pages.getTopLeft(.screen), + .br = screen.pages.getBottomRight(.screen) orelse return 0, + .unwrap = false, + }) catch return 0; + return @intCast(counting.fullCount()); +} + /// Release the emulator's heap. The Pane allocation itself is the core's. pub fn deinitEmulator(pane: *Pane, gpa: std.mem.Allocator) void { if (comptime !enabled) return; diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 5fe04f5c..c1f78bab 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -156,7 +156,8 @@ pub fn paneText(p: *Pardes, pane: *Pane, buf: []u8) []const u8 { w.print("{d:>11} {d:>11} {d:>11} {d:>11} {d:>11} {d:>11} ", .{ pane.serial, pane_files.tagOf(p, pane).len, - pane_files.bodyOf(pane).len, + // A terminal's body is its history, read whole on an open's first read. + if (pane.isTerminal()) panes.Terminal.screenTextLen(pane) else pane_files.bodyOf(pane).len, @as(u32, 0), @intFromBool(pane_files.dirtyOf(pane)), pane.cols, diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index 0a55df94..50d14624 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -849,6 +849,35 @@ test "a run's answer says cut when its output's start is gone, and reads a bound try testing.expectEqualStrings("error out of memory\n", sh.answered(p, node, h, into)); } +test "a terminal's ctl counts its body, and a new directory is a rename in the log" { + const gpa = testing.allocator; + const p = try withTerm(gpa); + defer p.deinit(); + const serial = serialOf(p); + p.update(.{ .output = .{ .pane = 0, .bytes = "some history\r\nmore" } }); + while (p.nextEffect()) |_| {} + const opened = call(p, .{ .tag = 1, .op = .open, .node = Node.of(serial, .body) }); + const body_len = call(p, .{ .tag = 2, .op = .read, .node = Node.of(serial, .body), .handle = opened.reply.handle, .size = 1 << 16 }).bytes.len; + _ = call(p, .{ .tag = 3, .op = .release, .node = Node.of(serial, .body), .handle = opened.reply.handle }); + try testing.expect(body_len > 0); + var fields = std.mem.tokenizeScalar(u8, rd(p, Node.of(serial, .ctl), 0, 4096).bytes, ' '); + _ = fields.next(); + _ = fields.next(); + try testing.expectEqual(body_len, try std.fmt.parseInt(usize, fields.next().?, 10)); + + const log = @intFromEnum(tree.TopFile.log); + const f = call(p, .{ .tag = 4, .op = .open, .node = log }).reply.handle; + const frozen = call(p, .{ .tag = 5, .op = .read, .node = log, .handle = f, .size = 1 << 16 }).bytes.len; + _ = call(p, .{ .tag = 6, .op = .write, .node = log, .handle = f, .data = "follow" }); + p.setCwd(0, "/somewhere/else"); + var want: [64]u8 = undefined; + try testing.expectEqualStrings( + try std.fmt.bufPrint(&want, "rename {d} /somewhere/else\n", .{serial}), + call(p, .{ .tag = 7, .op = .read, .node = log, .handle = f, .off = frozen, .size = 4096 }).bytes, + ); + _ = call(p, .{ .tag = 8, .op = .release, .node = log, .handle = f }); +} + test "closing a terminal pane asks its shell to hang up the pty" { const p = try withTerm(testing.allocator); defer p.deinit(); diff --git a/src/pardes.zig b/src/pardes.zig index 405e7839..084b2216 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4298,6 +4298,9 @@ pub const Pardes = struct { if (std.mem.eql(u8, cur, cwd)) return; pane.setOwnedCwd(cwd) catch |err| return p.reportError(id, "directory", err); if (look.lookHoverPane(p) == id) look.cancelLookHover(p); + // A terminal is named by its directory, so /log hears it renamed; one + // not announced yet is announced under the new name. + if (pane.isTerminal() and !pane.fs.unannounced) ctlfs.events.noteLog(p, .rename, pane); } pub fn shellBin(p: *const Pardes) []const u8 { |
