summaryrefslogtreecommitdiff
path: root/src/edit.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 14:23:50 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit505d15e238e4e9c5020a8e567d700bdbf67a8528 (patch)
treed0e136a29cc5f3416046157824f07c5e3bb1f67e /src/edit.zig
parent17c0d06618085de8d0bbf8152750ffa862db0080 (diff)
downloadpardes-505d15e238e4e9c5020a8e567d700bdbf67a8528.tar.gz
pardes-505d15e238e4e9c5020a8e567d700bdbf67a8528.zip
A paste into a terminal is xterm's paste: bracketed when the program set 2004, and the control bytes a paste could smuggle a command in by, ESC first, become spaces, so no ESC[201~ in the text ends the brackets early
Every paste path goes through edit.typeToTty (the GUI's and the tty's paste, p from a register, the acme chord's paste, a REPL's send), which now takes ghostty's paste encoder. A 9P write to a terminal's body or pty/data stays typed input, not a paste. The GUI test shell's stdin now takes a bracketed paste as one paste, as the tty does, not as keys. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/edit.zig')
-rw-r--r--src/edit.zig39
1 files changed, 29 insertions, 10 deletions
diff --git a/src/edit.zig b/src/edit.zig
index 98c60efb..1305d66f 100644
--- a/src/edit.zig
+++ b/src/edit.zig
@@ -111,19 +111,12 @@ pub fn clipRequest(p: *Pardes, id: usize, mode: @FieldType(ClipRequest, "mode"))
p.emit(.read_clipboard);
}
+/// Text pasted, yanked or sent into the program at `id`, as xterm pastes
+/// (terminal.encodePaste).
pub fn typeToTty(p: *Pardes, id: usize, pane: *const Pane, text: []const u8) void {
if (text.len == 0) return;
- if (panes.terminal.bracketedPaste(pane)) {
- p.emitWrite(id, "\x1b[200~");
- p.emitWrite(id, text);
- p.emitWrite(id, "\x1b[201~");
- return;
- }
const cp = p.scratch.allocator().dupe(u8, text) catch return;
- for (cp) |*ch| {
- if (ch.* == '\n') ch.* = '\r';
- }
- p.emitWrite(id, cp);
+ for (panes.terminal.encodePaste(pane, cp)) |part| if (part.len > 0) p.emitWrite(id, part);
}
pub fn applyPaste(p: *Pardes, bytes: []const u8) void {
@@ -2405,3 +2398,29 @@ test "a typed edit's known end is where the texts' comparison would find it" {
try std.testing.expectEqual(by_scan.col, t.last_edit.?.col);
}
}
+
+test "a paste into a terminal is xterm's: bracketed when the program asked, control bytes spaces, so no ESC[201~ ends the brackets early" {
+ if (comptime !panes.terminal.enabled) return error.SkipZigTest;
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .cols = 60, .rows = 16, .tty_only = true });
+ defer p.deinit();
+ var out: std.ArrayList(u8) = .empty;
+ defer out.deinit(gpa);
+ const take = struct {
+ fn f(pp: *Pardes, o: *std.ArrayList(u8)) ![]const u8 {
+ o.clearRetainingCapacity();
+ while (pp.nextEffect()) |effect| {
+ if (effect == .write and effect.write.pane == 0) try o.appendSlice(pp.gpa, effect.write.bytes.slice());
+ }
+ return o.items;
+ }
+ }.f;
+ _ = try take(p, &out);
+ try std.testing.expectEqual(.tty, p.panes[0].?.body.mode);
+ p.update(.{ .paste = "echo a\nb\x03\tc" });
+ try std.testing.expectEqualStrings("echo a\rb \tc", try take(p, &out));
+ p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[?2004h" } });
+ _ = try take(p, &out);
+ p.update(.{ .paste = "x\x1b[201~y\n" });
+ try std.testing.expectEqualStrings("\x1b[200~x [201~y\n\x1b[201~", try take(p, &out));
+}