From 898c61ee7cddaade523885bb9b02a89678b00fcc Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 27 Sep 2026 22:41:19 -0300 Subject: Give the message log to Messages as its own fields Not a pure move: state moves. Pardes's three message-log fields (`messages: [message_log]LoggedMessage`, `messages_head`, `messages_len`) become the fields of Messages.zig itself (`log`, `head`, `len`), and Pardes embeds one as `messages: Messages = .{}`. messageLog reads only the ring, so it now takes `m: *const Messages` and is called `p.messages.messageLog(i)` (Output's +Messages view and the log test); logMessage still needs the panes and the 9P event log, so it keeps `p: *Pardes` and writes `p.messages.log/head/len`. No behaviour change. Co-Authored-By: Claude Opus 5.5 --- src/Messages.zig | 47 ++++++++++++++++++++++++++++------------------- src/Output.zig | 3 +-- src/pardes.zig | 8 ++------ 3 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/Messages.zig b/src/Messages.zig index 6b041369..053950da 100644 --- a/src/Messages.zig +++ b/src/Messages.zig @@ -19,6 +19,15 @@ const Key = pardes.Key; const Rect = layout.Rect; const Pardes = pardes.Pardes; +const Messages = @This(); + +/// The message-row log: a fixed ring, never allocated, never grown. See +/// `logMessage` and the `Messages` builtin. +log: [limits.message_log]LoggedMessage = @splat(.{}), +/// Next slot to write. `len` saturates at the ring's size. +head: usize = 0, +len: usize = 0, + pub const LoggedMessage = struct { pub const cap = 256; text: [cap]u8 = undefined, @@ -347,8 +356,8 @@ fn logMessage(p: *Pardes, id: usize, text: []const u8) void { // /log hears every one: a client that retried and failed the same way // is waiting on that second line. Only the +Messages view collapses. ctlfs.events.noteMessage(p, serial, kept); - if (p.messages_len > 0) { - const last = &p.messages[(p.messages_head + limits.message_log - 1) % limits.message_log]; + if (p.messages.len > 0) { + const last = &p.messages.log[(p.messages.head + limits.message_log - 1) % limits.message_log]; if (last.serial == serial and std.mem.eql(u8, Message.body(last.slice()), Message.body(kept))) { @@ -360,20 +369,20 @@ fn logMessage(p: *Pardes, id: usize, text: []const u8) void { return; } } - const slot = &p.messages[p.messages_head]; + const slot = &p.messages.log[p.messages.head]; slot.len = @intCast(kept.len); @memcpy(slot.text[0..slot.len], kept); 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); + p.messages.head = (p.messages.head + 1) % limits.message_log; + p.messages.len = @min(p.messages.len + 1, limits.message_log); } /// The log oldest-first, which is reading order. -pub fn messageLog(p: *const Pardes, i: usize) ?*const LoggedMessage { - if (i >= p.messages_len) return null; - const first = (p.messages_head + limits.message_log - p.messages_len) % limits.message_log; - return &p.messages[(first + i) % limits.message_log]; +pub fn messageLog(m: *const Messages, i: usize) ?*const LoggedMessage { + if (i >= m.len) return null; + const first = (m.head + limits.message_log - m.len) % limits.message_log; + return &m.log[(first + i) % limits.message_log]; } pub fn reportError(p: *Pardes, id: usize, operation: []const u8, err: anyerror) void { @@ -432,38 +441,38 @@ test "the message log keeps what the row forgets, and collapses repeats" { setMessage(p, 0, "14:32:09 saved /x.zig"); // same event, later clock setMessage(p, 0, "save: AccessDenied"); - try std.testing.expectEqual(@as(usize, 2), p.messages_len); - const first = messageLog(p, 0).?; + try std.testing.expectEqual(@as(usize, 2), p.messages.len); + const first = p.messages.messageLog(0).?; try std.testing.expectEqual(@as(u16, 2), first.repeats); // ...and the NEWEST wording is what survives, so the row carries the last // time it happened rather than the first. try std.testing.expectEqualStrings("14:32:09 saved /x.zig", first.slice()); - try std.testing.expectEqualStrings("save: AccessDenied", messageLog(p, 1).?.slice()); - try std.testing.expect(messageLog(p, 2) == null); + try std.testing.expectEqualStrings("save: AccessDenied", p.messages.messageLog(1).?.slice()); + try std.testing.expect(p.messages.messageLog(2) == null); // The same text from a DIFFERENT pane is a different event: one pane's // failure must not be recorded as another's. p.newScratchBelow(0); const other = p.active; setMessage(p, other, "save: AccessDenied"); - try std.testing.expectEqual(@as(usize, 3), p.messages_len); - try std.testing.expectEqual(p.panes[other].?.serial, messageLog(p, 2).?.serial); + try std.testing.expectEqual(@as(usize, 3), p.messages.len); + try std.testing.expectEqual(p.panes[other].?.serial, p.messages.messageLog(2).?.serial); // Progress is NOT logged: it arrives several times a second for a whole // index and would push everything else out (`setStatus`). setStatus(p, 0, "14:32:10 lsp rust-analyzer: Indexing 47%"); - try std.testing.expectEqual(@as(usize, 3), p.messages_len); + try std.testing.expectEqual(@as(usize, 3), p.messages.len); // The ring wraps rather than grows, and still reads oldest-first. for (0..limits.message_log + 5) |i| { var buf: [32]u8 = undefined; setMessage(p, 0, std.fmt.bufPrint(&buf, "line {d}", .{i}) catch unreachable); } - try std.testing.expectEqual(@as(usize, limits.message_log), p.messages_len); - try std.testing.expectEqualStrings("line 5", messageLog(p, 0).?.slice()); + try std.testing.expectEqual(@as(usize, limits.message_log), p.messages.len); + try std.testing.expectEqualStrings("line 5", p.messages.messageLog(0).?.slice()); var last_buf: [32]u8 = undefined; const want_last = std.fmt.bufPrint(&last_buf, "line {d}", .{limits.message_log + 4}) catch unreachable; - try std.testing.expectEqualStrings(want_last, messageLog(p, limits.message_log - 1).?.slice()); + try std.testing.expectEqualStrings(want_last, p.messages.messageLog(limits.message_log - 1).?.slice()); } test "Msg writes the transient row by hand, bare or with text, and input ends it" { diff --git a/src/Output.zig b/src/Output.zig index b216c0cf..6df2fd34 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -4,7 +4,6 @@ const panes = @import("panes.zig"); const std = @import("std"); const pardes = @import("pardes.zig"); const exec = @import("exec.zig"); -const Messages = @import("Messages.zig"); const config = @import("config.zig"); const Pardes = pardes.Pardes; const modal = @import("modal.zig"); @@ -686,7 +685,7 @@ pub fn openMessages(p: *Pardes, id: usize) !void { var out: std.Io.Writer.Allocating = .init(p.gpa); errdefer out.deinit(); var i: usize = 0; - while (Messages.messageLog(p, i)) |m| : (i += 1) { + while (p.messages.messageLog(i)) |m| : (i += 1) { if (m.serial != 0) try out.writer.print("{d}: ", .{m.serial}); try out.writer.writeAll(m.slice()); if (m.repeats > 1) try out.writer.print(" (x{d})", .{m.repeats}); diff --git a/src/pardes.zig b/src/pardes.zig index 44f0d2ea..d2ac5868 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -3777,12 +3777,8 @@ pub const Pardes = struct { /// The in-program answers behind every unimplemented host method. Per /// instance, so several cores behind one fan-out host stay independent. fallback: host_io.Fallback, - /// The message-row log: a fixed ring, never allocated, never grown. See - /// `logMessage` and the `Messages` builtin. - messages: [limits.message_log]Messages.LoggedMessage = @splat(.{}), - /// Next slot to write. `messages_len` saturates at the ring's size. - messages_head: usize = 0, - messages_len: usize = 0, + /// The message-row log (see Messages.zig). + messages: Messages = .{}, in_q: [64]Event = undefined, in_head: usize = 0, in_len: usize = 0, -- cgit v1.3