From 542dd489149e33915d19878a277e23b3a9c0b070 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 27 Sep 2026 17:41:55 -0300 Subject: Record /log whether or not anyone reads it, with the editor's messages in it /log is now one ring (64 KiB, 4 KiB on the board) that records new, del, rename and save, and a msg line for everything the editor says. An open freezes it, so cat log shows what happened lately and ends; writing follow to that same open makes reads past it wait for newer records, and a follower the ring outran reads lost N first. The message log keeps the pane's serial, not its reusable slot. A test now fails when the tree serves a file /README never mentions; it caught pty/status, now documented along with typing through pty/data. Co-Authored-By: Claude Opus 5.5 --- src/pardes.zig | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src/pardes.zig') diff --git a/src/pardes.zig b/src/pardes.zig index ca09caa1..6d294230 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -672,9 +672,11 @@ test "the message log keeps what the row forgets, and collapses repeats" { // The same text from a DIFFERENT pane is a different event: one pane's // failure must not be recorded as another's. - p.setMessage(1, "save: AccessDenied"); + p.newScratchBelow(0); + const other = p.active; + p.setMessage(other, "save: AccessDenied"); try std.testing.expectEqual(@as(usize, 3), p.messages_len); - try std.testing.expectEqual(@as(u8, 1), p.messageLog(2).?.pane); + try std.testing.expectEqual(p.panes[other].?.serial, p.messageLog(2).?.serial); // Progress is NOT logged: it arrives several times a second for a whole // index and would push everything else out (`setStatus`). @@ -6254,8 +6256,10 @@ pub const LoggedMessage = struct { pub const cap = 256; text: [cap]u8 = undefined, len: u16 = 0, - /// Which pane said it, or 0xff for one said to no pane in particular. - pane: u8 = 0xff, + /// The serial of the pane that said it, or 0 for none in particular. A + /// serial, not a slot: slots are reused, so an old line would name + /// whichever pane took the slot since. + serial: u32 = 0, /// How many times in a row. Saturating: a key held down against the same /// refusal is one event, not four hundred. repeats: u16 = 0, @@ -7432,11 +7436,11 @@ pub const Pardes = struct { fn logMessage(p: *Pardes, id: usize, text: []const u8) void { if (text.len == 0) return; - const pane: u8 = if (id < MAX_PANES) @intCast(id) else 0xff; + const serial: u32 = if (id < MAX_PANES) if (p.panes[id]) |pane| pane.serial else 0 else 0; const kept = text[0..@min(text.len, LoggedMessage.cap)]; if (p.messages_len > 0) { const last = &p.messages[(p.messages_head + limits.message_log - 1) % limits.message_log]; - if (last.pane == pane and + if (last.serial == serial and std.mem.eql(u8, Message.body(last.slice()), Message.body(kept))) { // The NEWEST wording wins, so the row carries the latest clock @@ -7447,10 +7451,11 @@ pub const Pardes = struct { return; } } + ctlfs.events.noteMessage(p, serial, kept); const slot = &p.messages[p.messages_head]; slot.len = @intCast(kept.len); @memcpy(slot.text[0..slot.len], kept); - slot.pane = pane; + slot.serial = serial; slot.repeats = 1; p.messages_head = (p.messages_head + 1) % limits.message_log; p.messages_len = @min(p.messages_len + 1, limits.message_log); -- cgit v1.3