From 73050ca20405fc14c2e3a53b692954b1771dfe24 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 17:42:30 -0300 Subject: fs.md says a body write to a terminal is typed input, never a bracketed paste A write to a terminal's body goes to its child as typed keys: no bracketed-paste marks around it, even when the program set 2004, so a newline in it is Enter and a shell runs each line. fs.md did not say which, and a script cannot guess. It does now, and a test writes to a terminal whose program asked for bracketed paste and checks the bytes sent are only the ones written. Co-Authored-By: Claude Opus 5.5 --- docs/fs.md | 4 +++- src/ninep/ctl.zig | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/fs.md b/docs/fs.md index 1c496c5f..5c952403 100644 --- a/docs/fs.md +++ b/docs/fs.md @@ -402,7 +402,9 @@ component. A directory (`/`, `~`, `foo/`) is no file name: refused, EISDIR **`body`** reads the text; a write appends; `>` (OTRUNC) replaces it all. A terminal's body is its history as plain text in logical lines (wrapped -rows joined), frozen per open; writing it sends input to the child. A PDF's +rows joined), frozen per open; writing it sends input to the child as typed +keys, never a paste: no bracketed-paste marks around it, even when the +program asked for them, so a newline in it is Enter. A PDF's body is the text layer of the page shown; images and PDFs take no write (`this pane has no text`). diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 10336e38..118b6488 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -1633,6 +1633,20 @@ test "get reloads the pane from disk and reports a missing file without losing d try testing.expect(pane_files.dirtyOf(pane)); } +test "a body write to a terminal is typed input, not a paste, even to a program that asked for bracketed paste" { + const p = try Pardes.init(testing.allocator, .{ .cols = 90, .rows = 30 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const term = for (p.panes, 0..) |slot, id| { + if (slot) |pane| if (pane.isTerminal()) break id; + } else return error.SkipZigTest; + p.update(.{ .output = .{ .pane = @intCast(term), .bytes = "\x1b[?2004h" } }); + while (p.nextEffect()) |_| {} + const w = wr(p, Node.of(p.panes[term].?.serial, .body), "ls\n"); + try testing.expectEqual(Status.ok, w.reply.status); + try testing.expectEqualStrings("ls\n", w.pty_buf[0..w.pty_len]); +} + test "Save path of a file pane logs a save record naming that path" { const p = try withFile(testing.allocator, "one\n"); defer p.deinit(); -- cgit v1.3