summaryrefslogtreecommitdiff
path: root/src/ninep/tree.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 01:48:39 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit5b11fc35b961eb03c8816bf957bbf8e27a22ae3e (patch)
tree3a0022699f4c92ec86b2249cf3455139809d56a4 /src/ninep/tree.zig
parent5b5896965758ff8f2077c8e8e2f0250f580967c3 (diff)
downloadpardes-5b11fc35b961eb03c8816bf957bbf8e27a22ae3e.tar.gz
pardes-5b11fc35b961eb03c8816bf957bbf8e27a22ae3e.zip
The workspace tag has /tagexec, every exec file reads back what it touched, and a tag's words log as the session's
/tagexec runs a word as a click in the workspace tag, as col/<serial>/exec does in a column's. A column's exec was write-only; it and /tagexec now read back the serials the last write touched, as /exec does. A click in a tag no longer goes to the focused pane's event reader, and what a word run from a column's ctl or exec or /tagexec says is logged msg -, not against the focused pane. The README, fs.md and the skill say so, and document the answer record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/tree.zig')
-rw-r--r--src/ninep/tree.zig36
1 files changed, 23 insertions, 13 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index baf8c772..085d03a3 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -191,6 +191,7 @@ pub const TopFile = enum(u5) {
layout,
tag,
col,
+ tagexec,
pub fn fileName(f: TopFile) []const u8 {
return if (f == .root) "/" else @tagName(f);
@@ -199,7 +200,7 @@ pub const TopFile = enum(u5) {
pub fn mode(f: TopFile) u16 {
return switch (f) {
.root, .pane, .col => 0o755,
- .look, .exec, .log, .focus, .ctl, .tag => 0o666,
+ .look, .exec, .log, .focus, .ctl, .tag, .tagexec => 0o666,
.README, .index, .status, .screen, .listeners, .new, .commands, .layout => 0o444,
};
}
@@ -468,10 +469,14 @@ fn attrOf(p: *Pardes, target: Target) ?Reply.Attr {
.dir = c.file == .dir,
.mode = switch (c.file) {
.dir => 0o755,
- .ctl, .exec => 0o222,
- .tag => 0o666,
+ .ctl => 0o222,
+ .tag, .exec => 0o666,
+ },
+ .size = switch (c.file) {
+ .tag => cols.headerText(p, c.serial).?.len + 1,
+ .exec => ctl.resultsLen(p),
+ else => 0,
},
- .size = if (c.file == .tag) cols.headerText(p, c.serial).?.len + 1 else 0,
.mtime = p.fs.started,
.append = c.file == .tag,
};
@@ -513,7 +518,7 @@ fn topSize(p: *Pardes, f: TopFile) u64 {
.index => pane.indexLen(p),
.README => fs.help.len,
.status => ctl.statusLen(p),
- .look, .exec => ctl.resultsLen(p),
+ .look, .exec, .tagexec => ctl.resultsLen(p),
.log => events.logLen(&p.fs.log),
.listeners => listeners: {
var buf: [screen.listeners_cap]u8 = undefined;
@@ -618,7 +623,7 @@ fn readdir(p: *Pardes, req: Req, target: Target) Reply {
.root => {
// The guide, then what describes the session, then the
// files that act, then the directories.
- inline for (.{ TopFile.README, TopFile.index, TopFile.status, TopFile.focus, TopFile.ctl, TopFile.commands, TopFile.look, TopFile.exec, TopFile.log, TopFile.screen, TopFile.listeners, TopFile.layout, TopFile.tag, TopFile.pane, TopFile.col }) |t| {
+ inline for (.{ TopFile.README, TopFile.index, TopFile.status, TopFile.focus, TopFile.ctl, TopFile.commands, TopFile.look, TopFile.exec, TopFile.log, TopFile.screen, TopFile.listeners, TopFile.layout, TopFile.tag, TopFile.tagexec, TopFile.pane, TopFile.col }) |t| {
if (skip > 0) skip -= 1 else stageDirent(out, p.gpa, @intFromEnum(t), t.dir(), t.fileName());
}
if (skip > 0) skip -= 1 else stageDirent(out, p.gpa, fs.os_root, true, "os");
@@ -861,7 +866,7 @@ fn readFile(p: *Pardes, req: Req, target: Target) Reply {
break :index stagedReply(p, req);
},
.status => ctl.readStatus(p, req),
- .look, .exec => ctl.readResults(p, req),
+ .look, .exec, .tagexec => ctl.readResults(p, req),
.log => events.readLog(p, req),
.screen => screen.readSnapshot(p, req, null),
.listeners => screen.readListeners(p, req),
@@ -899,7 +904,11 @@ fn readFile(p: *Pardes, req: Req, target: Target) Reply {
if (t.file.inPty() and !pn.isTerminal()) return Reply.fail(req.tag, E.NOENT);
return pane.read(p, req, id, pn, t.file);
},
- .col => |c| return if (c.file == .tag) cols.read(p, req, c.serial) else Reply.fail(req.tag, E.PERM),
+ .col => |c| return switch (c.file) {
+ .tag => cols.read(p, req, c.serial),
+ .exec => ctl.readResults(p, req),
+ else => Reply.fail(req.tag, E.PERM),
+ },
}
}
@@ -911,6 +920,7 @@ fn write(p: *Pardes, req: Req, target: Target) Reply {
.focus => ctl.writeFocus(p, req),
.ctl => ctl.writeRoot(p, req),
.tag => cols.write(p, req, null),
+ .tagexec => cols.writeExec(p, req, null),
else => Reply.fail(req.tag, E.PERM),
},
.pane => |t| {
@@ -1069,13 +1079,13 @@ test "readdir lists the root and a pane directory without creating anything" {
@memcpy(top_bytes[0..top_reply.bytes.len], top_reply.bytes);
const top = th.dirents(top_bytes[0..top_reply.bytes.len], &buf);
try testing.expect(top.len >= 10);
- for ([_][]const u8{ "README", "index", "status", "focus", "ctl", "commands", "look", "exec", "log", "screen", "listeners", "layout", "tag", "pane", "col", "os" }, 0..) |name, i| {
+ for ([_][]const u8{ "README", "index", "status", "focus", "ctl", "commands", "look", "exec", "log", "screen", "listeners", "layout", "tag", "tagexec", "pane", "col", "os" }, 0..) |name, i| {
try testing.expectEqualStrings(name, top[i].name);
- try testing.expectEqual(i >= 13, top[i].dir);
+ try testing.expectEqual(i >= 14, top[i].dir);
}
- try testing.expectEqual(@intFromEnum(TopFile.pane), top[13].node);
- try testing.expectEqual(@intFromEnum(TopFile.col), top[14].node);
- try testing.expectEqual(fs.os_root, top[15].node);
+ try testing.expectEqual(@intFromEnum(TopFile.pane), top[14].node);
+ try testing.expectEqual(@intFromEnum(TopFile.col), top[15].node);
+ try testing.expectEqual(fs.os_root, top[16].node);
var idbuf: [16]u8 = undefined;
try testing.expect(th.nameAt(top, try std.fmt.bufPrint(&idbuf, "{d}", .{serial})) == null);
try testing.expect(th.nameAt(top, "self") == null);