diff options
| -rw-r--r-- | .agents/skills/pardes-9p/SKILL.md | 11 | ||||
| -rw-r--r-- | docs/fs.md | 13 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 15 |
3 files changed, 26 insertions, 13 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md index f4542a50..63dec104 100644 --- a/.agents/skills/pardes-9p/SKILL.md +++ b/.agents/skills/pardes-9p/SKILL.md @@ -217,14 +217,17 @@ not the read-until-EOF helper. `pty/data` captures output while held open; it is not a history replay. Both files are shared, consuming queues, not per-client broadcasts, so a slow reader loses older data. Holding `event` open intercepts that pane's Look and Exec clicks -- and lines written to that pane's -own `look`/`exec`, as `F` records at `0 0` with the text, and clicks in a -terminal's body, also at `0 0` -- so it is not a -passive logger. To have a record done, write it back: the short form +own `look`/`exec`, or to the root's while it has the keyboard, as `F` records +at `0 0` with the text, and clicks in a terminal's body, also at `0 0` -- so +it is not a passive logger: a helper holding `event` that writes its own +pane's exec gets its command back as a record; run it through `ctl` or write +the record back. To have a record done, write it back: the short form `<origin><action><q0> <q1>\n` acts on that range's text, and the whole record as read acts on its text when the range is empty (the only way for a record at `0 0`). Chord reports need explicit handling. A record is `<origin><action><q0> <q1> <flag> <n> <text>\n` and its text may hold -newlines: read `n` bytes of text, never up to the next newline. +newlines: read `n` bytes of text, never up to the next newline (acme counts +runes; pardes counts bytes, as all its offsets are). A Restore puts a new editor under every client: the Restore write is answered, then every connection is hung up (their fids name the old editor's panes); dial again, and the new log names the restored panes then @@ -255,9 +255,11 @@ directory. `body` appends on write and replaces on truncating open. `sel` reads the selected text and writing it replaces the selection. `errors` appends to the directory's `+Errors` pane. Holding `event` open redirects the pane's Look and Exec clicks to that client, and so does a line written to the -pane's own `look` or `exec`, a click with no place in the text: an `F` record -at `0 0` carrying the line (the root's `look` and `exec` still act at once); -writing a record back performs the action. acme takes back only `<origin> +pane's own `look` or `exec`, or to the root's while that pane has the +keyboard, a click with no place in the text: an `F` record at `0 0` carrying +the line. So a client holding `event` that wants a command run gets its own +exec back as a record: it runs it through `ctl`, or writes the record back. +Writing a record back performs the action. acme takes back only `<origin> <action><q0> <q1>`, the text of that range; pardes takes the record whole as it was read too, and for an empty range acts on its text, which is how such a line is done. A click in a file's body carries the offsets of the text it @@ -397,7 +399,10 @@ recorded before anything else. Control characters in a record become spaces, so a record is one line. (An `event` record is not: acme's `<origin><action><q0> <q1> <flag> <n> <text>\n`, whose text may hold newlines; read `n` bytes of it rather than -up to a newline -- bytes here, where acme counts runes.) An +up to a newline -- bytes here, where acme counts runes. Every offset and +count pardes serves is in bytes, `#n` and `q0`/`q1` too; the event count +follows them rather than switch alone, so an acme library reads pardes +correctly for ASCII text and not beyond it.) An open freezes the ring's text the way `/screen` freezes a frame: reads walk it and end. Writing `follow` to that same open makes reads past it wait for the next record, one per read; a follower the ring outran reads `lost N` first. diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 1ba78457..c1cfa1f6 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -51,13 +51,14 @@ fn noteResult(p: *Pardes, serial: u32) void { /// Runs one look or exec from the pane `id`, then records the panes it /// created; failing that, a look records the pane it focused and an exec /// the pane it acted on (even one it closed). -fn run(p: *Pardes, id: usize, exec: bool, text: []const u8, own: bool) void { +fn run(p: *Pardes, id: usize, exec: bool, text: []const u8) void { const context = p.panes[id].?.serial; // A pane an event reader holds hears its clicks instead of acting on // them (editors/acme/look.c:53 and exec.c:173 send them to winevent), - // and a line written to its own look or exec is a click on it; the - // reader writes the record back to have it done. - if (own and p.panes[id].?.fs.readers > 0) { + // and a line written to a look or exec is a click: on that pane, or for + // the root's, on the pane with the keyboard. The reader writes the + // record back to have it done. + if (p.panes[id].?.fs.readers > 0) { const named = std.meta.stringToEnum(Builtin, exec_line.commandText(text)) != null; var flag: u32 = pardes.ctlfs.events.flag_expansion; if (named) flag |= pardes.ctlfs.events.flag_builtin; @@ -94,7 +95,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool) Reply { if (!apply) continue; const id = if (serial) |s| p.paneBySerial(s) orelse break else p.active; if (p.panes[id] == null) return Reply.fail(req.tag, E.NOENT); - run(p, id, exec, text, serial != null); + run(p, id, exec, text); } } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; @@ -909,6 +910,10 @@ test "a pane's own look and exec go to the event reader holding it, which writes try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "routed") != null); try testing.expectEqual(tree.Status.ok, wr(p, Node.of(serial, .look), "one.txt:3\n").reply.status); try testing.expectEqualStrings("FL0 0 6 9 one.txt:3\n", call(p, .{ .tag = 3, .op = .read, .node = event, .handle = h, .size = 256 }).bytes); + // The root's is a click at the pane with the keyboard, this one. + try testing.expectEqual(tree.Status.ok, wr(p, root_exec, "Msg rooted\n").reply.status); + try testing.expect(std.mem.indexOf(u8, pane.msg[0..pane.msg_len], "rooted") == null); + try testing.expectEqualStrings("FX0 0 2 10 Msg rooted\n", call(p, .{ .tag = 5, .op = .read, .node = event, .handle = h, .size = 256 }).bytes); _ = call(p, .{ .tag = 4, .op = .release, .node = event, .handle = h }); // With no reader, the root's and the pane's act at once. try testing.expectEqual(tree.Status.ok, wr(p, Node.of(serial, .exec), "Msg direct\n").reply.status); |
