diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 21:19:57 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 137dc2ea4be0fc5baf4cab1d8a757ce426e5cbb2 (patch) | |
| tree | b4dba1252b54a135c5afe972bf4193f31219703a | |
| parent | f02c10bfa6d02d305ddb6d7a28b12c40035274c7 (diff) | |
| download | pardes-137dc2ea4be0fc5baf4cab1d8a757ce426e5cbb2.tar.gz pardes-137dc2ea4be0fc5baf4cab1d8a757ce426e5cbb2.zip | |
Views generated by each read stat 0, as acme's do: data, xdata, index, layout, recent and listeners; fs.md says which files have real lengths
A stat of index, layout, recent or listeners built the whole view to
count it, and data and xdata reported the body's length though a read
answers what addr selects. Those now stat 0, as screen and pane/new
already did, and fs.md lists the files whose length is real and says to
read the rest to the end.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/fs.md | 10 | ||||
| -rw-r--r-- | src/ninep/cols.zig | 10 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 3 | ||||
| -rw-r--r-- | src/ninep/pane.zig | 26 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 23 |
5 files changed, 23 insertions, 49 deletions
@@ -194,8 +194,14 @@ reads at once; the next is refused `too many reads waiting: 128`. A mount keeps answering everything else beside them. Through a FUSE mount bash's `read -t` cannot time out: wrap the loop in `timeout N`. -**Stats.** Lengths are real (for `event` and `pty/data` the next record's, -zero when none waits; for `log` what an open would freeze). The qid +**Stats.** A file whose text is kept has its real length: a pane's +`body`, `tag`, `name`, `ctl`, `sel` and the range and flag files, the +workspace's `tag`, the root `ctl`, `status`, `commands`, `README`, the +`look`/`exec` answers, `focus`; `event` and `pty/data` the next record's, +zero when none waits; `log` what an open would freeze. A view generated by +each read stats 0, as acme's do: `screen`, `data`, `xdata`, `index`, +`layout`, `recent`, `listeners`, `pane/new`. Read those to the end rather +than trust a length (`cat` does). The qid version of `body`, `data` and `xdata` is the pane's revision, so a stat sees an edit land. Modes are 0644/0666, 0444 read-only, 0222 write-only. diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index 6828612c..b2e62d28 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -43,13 +43,6 @@ pub fn writeLayout(p: *Pardes, w: *std.Io.Writer) !void { if (p.activeColumn() orelse active) |c| try w.print("active {d}\n", .{layout.columnSerial(p, c)}) else try w.writeAll("active -\n"); } -pub fn layoutLen(p: *Pardes) u64 { - var buf: [256]u8 = undefined; - var counting: std.Io.Writer.Discarding = .init(&buf); - writeLayout(p, &counting.writer) catch return 0; - return counting.fullCount(); -} - /// A header by its node: the workspace's (`serial` null) or the column's /// with that serial; null when that column has closed. const Header = struct { text: *Text, col: ?usize }; @@ -532,7 +525,8 @@ test "layout lists each column's place, panes and state, and the active one" { // Newcol gives the new, empty column's tag the keyboard. try testing.expect(std.mem.indexOf(u8, text, "current empty\n") != null); try testing.expect(std.mem.endsWith(u8, text, try std.fmt.bufPrint(&want, "active {d}\n", .{new_serial}))); - try testing.expectEqual(@as(u64, text.len), th.call(p, .{ .tag = 1, .op = .getattr, .node = layout_node }).reply.attr.size); + try testing.expect(text.len > 0); + try testing.expectEqual(@as(u64, 0), th.call(p, .{ .tag = 1, .op = .getattr, .node = layout_node }).reply.attr.size); // generated by each read // /index says which column each pane is in, by its serial, last. try testing.expect(std.mem.endsWith(u8, th.rd(p, @intFromEnum(tree.TopFile.index), 0, 4096).bytes, try std.fmt.bufPrint(&want, " {d}\n", .{first_serial}))); try testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "newcol {d}\n", .{new_serial}))); diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 63f89cdf..fc0558f5 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -2932,7 +2932,8 @@ test "/index shows a name as the log does: a newline in it is \\n, controls spac const index = rd(p, @intFromEnum(tree.TopFile.index), 0, 4096).bytes; try testing.expect(std.mem.indexOf(u8, index, "/tmp/two\\nlines \\xff ") != null); try testing.expectEqual(@as(usize, 1), std.mem.count(u8, index, "\n")); - try testing.expectEqual(@as(u64, index.len), call(p, .{ .tag = 1, .op = .getattr, .node = @intFromEnum(tree.TopFile.index) }).reply.attr.size); + try testing.expect(index.len > 0); + try testing.expectEqual(@as(u64, 0), call(p, .{ .tag = 1, .op = .getattr, .node = @intFromEnum(tree.TopFile.index) }).reply.attr.size); // Its tag never carries the raw newline either. const tag = rd(p, Node.of(serialOf(p), .tag), 0, 4096).bytes; try testing.expect(std.mem.indexOf(u8, tag, "/tmp/two\\nlines") != null); diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 14877f0f..3b5f2e6c 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -398,7 +398,9 @@ pub fn fileSize(p: *Pardes, id: usize, f: PaneFile) u64 { return switch (f) { // A terminal's body reads its screen and history, as text. .body => if (pane.file == null and pane.isTerminal()) panes.terminal.screenTextLen(pane) else if (pdfText(p, pane)) |t| t.len else bodyOf(pane).len, - .data, .xdata => bodyOf(pane).len, + // What addr selects, generated by each read: no length, as acme's + // data and xdata have none. + .data, .xdata => 0, .tag => tagOf(p, pane).len, .name => nameOf(p, pane).len + 1, .ctl => ctl: { @@ -437,23 +439,6 @@ pub fn columnOf(p: *Pardes, id: usize) u32 { return pardes.layout.columnSerial(p, at.col); } -pub fn indexLen(p: *Pardes) u64 { - var n: u64 = 0; - var last: u32 = 0; - while (tree.nextSerialAfter(p, last)) |serial| { - last = serial; - const pane = p.panes[p.paneBySerial(serial).?].?; - var digits: [16]u8 = undefined; - // unreachable: a u32 serial is at most 10 digits - n += (std.fmt.bufPrint(&digits, "{d}", .{serial}) catch unreachable).len; - var name_buf: [4 * 4096]u8 = undefined; - n += 1 + kindOf(pane).len + 3 + events.shown(nameOf(p, pane), &name_buf).len + 1; - // unreachable: a column index is under 16 digits - n += 1 + (std.fmt.bufPrint(&digits, "{d}", .{columnOf(p, p.paneBySerial(serial).?)}) catch unreachable).len; - } - return n; -} - // ---- reads ---- pub fn read(p: *Pardes, req: Req, id: usize, pane: *Pane, file: PaneFile) Reply { @@ -1081,7 +1066,7 @@ const withFile = th.withFile; const serialOf = th.serialOf; const Status = tree.Status; -test "index lists serial kind dirty flag and name and stat reports its length" { +test "index lists serial kind dirty flag and name, and stats 0, a view generated by each read" { const gpa = testing.allocator; const p = try withFile(gpa, "hello\nthere\n"); defer p.deinit(); @@ -1095,7 +1080,7 @@ test "index lists serial kind dirty flag and name and stat reports its length" { try testing.expect(std.mem.indexOf(u8, a.bytes, clean_row) != null); const at = std.mem.indexOf(u8, a.bytes, clean_row).?; const stat = call(p, .{ .tag = 1, .op = .getattr, .node = index }); - try testing.expectEqual(@as(u64, a.bytes.len), stat.reply.attr.size); + try testing.expectEqual(@as(u64, 0), stat.reply.attr.size); try testing.expectEqual(@as(u16, 0o444), stat.reply.attr.mode); pane.file.?.saved_revision = pane.file.?.revision -% 1; @@ -1107,7 +1092,6 @@ test "index lists serial kind dirty flag and name and stat reports its length" { const serial = try th.newPane(p); const both = rd(p, index, 0, 4096); - try testing.expectEqual(@as(u64, both.bytes.len), indexLen(p)); var expected: [4200]u8 = undefined; const made = p.panes[p.paneBySerial(serial).?].?; try testing.expect(std.mem.endsWith(u8, both.bytes, try std.fmt.bufPrint(&expected, "\n{d} text 0 {s} {d}\n", .{ serial, nameOf(p, made), columnOf(p, p.paneBySerial(serial).?) }))); diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index c0ace5be..a7f3f2a4 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -253,13 +253,6 @@ pub const TopFile = enum(u5) { pub const root: u64 = @intFromEnum(TopFile.root); -fn recentLen(p: *Pardes) u64 { - var buf: [64]u8 = undefined; - var counting: std.Io.Writer.Discarding = .init(&buf); - pardes.panes.Output.recentText(p, &counting.writer, false) catch return 0; - return counting.fullCount(); -} - /// Declaration order is readdir order. pub const PaneFile = enum(u5) { dir = 0, @@ -643,23 +636,19 @@ fn attrOf(p: *Pardes, target: Target) ?Reply.Attr { fn topSize(p: *Pardes, f: TopFile) u64 { return switch (f) { - // ponytail: /screen has no length until an open renders its frame, - // and /pane/new none until an open has a pane to name. - .root, .pane, .col, .screen, .new => 0, - .layout => cols.layoutLen(p), + // Views generated whole by each read have no length, as acme's + // generated files have none: /screen until an open renders its + // frame, /pane/new until an open has a pane to name, and the index, + // layout, recent list and listeners, which a stat would otherwise + // have to build to count. + .root, .pane, .col, .screen, .new, .layout, .index, .recent, .listeners => 0, .tag => cols.headerText(p, null).?.len, - .index => pane.indexLen(p), .README => fs.help.len, .status => ctl.statusLen(p), .look, .exec, .tagexec => ctl.resultsLen(p), .log => events.logLen(&p.fs.log), - .listeners => listeners: { - var buf: [screen.listeners_cap]u8 = undefined; - break :listeners screen.listenersText(p, &buf).len; - }, .ctl => ctl.rootLen(p), .commands => ctl.commandsLen(), - .recent => recentLen(p), .focus => focus: { if (p.header_focus) break :focus 0; const pn = p.panes[p.active] orelse break :focus 0; |
