summaryrefslogtreecommitdiff
path: root/test/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 13:01:28 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commitffc700d14d61fd5e6ca874a186932300c447c836 (patch)
tree364b733b061f4fec14cef0a12862fa125cbde35b /test/panes.zig
parentfc2738eea560769c8d150a7cc955fe8d54121298 (diff)
downloadpardes-ffc700d14d61fd5e6ca874a186932300c447c836.tar.gz
pardes-ffc700d14d61fd5e6ca874a186932300c447c836.zip
Mirror raw terminal mouse selections for text transfer
Diffstat (limited to 'test/panes.zig')
-rw-r--r--test/panes.zig149
1 files changed, 149 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig
index ded5b075..0577154f 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -12,6 +12,154 @@ const image = pardes.image;
const syntax = pardes.syntax;
const filesystem = pardes.filesystem;
+const TtySelectionTests = struct {
+ fn noChildInput(p: *Pardes) !void {
+ while (p.nextEffect()) |effect| {
+ if (effect == .write) return error.UnexpectedChildInput;
+ if (effect == .set_clipboard) p.perform(effect);
+ }
+ }
+
+ test "tty selection yields to a new modal cursor selection" {
+ if (comptime !panes.Terminal.enabled) return error.SkipZigTest;
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 12 });
+ defer p.deinit();
+ p.presentation.enabled = false;
+ const pane = p.panes[0].?;
+ pane.tty_filter = false;
+ while (p.nextEffect()) |_| {}
+ p.update(.{ .output = .{ .pane = 0, .bytes = "alpha" } });
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const x = rect.x + config.GUTTER;
+ const y = if (p.settings.tag_bottom) rect.y else rect.y + pardes.BOX_H;
+ JumpSelectionTests.drag(p, x + 1, y, x + 3, y);
+ try std.testing.expectEqualStrings("lph", p.yank orelse return error.MissingAutomaticTtyYank);
+ p.update(.{ .key = .{ .cp = p.opts.tty_toggle, .ctrl = true } });
+ p.update(.{ .key = .{ .cp = 'l' } });
+ try std.testing.expectEqual(@as(i32, 4), pane.cur_col);
+ p.update(.{ .key = .{ .cp = 'y' } });
+ try std.testing.expectEqualStrings("a", p.yank.?);
+ try noChildInput(p);
+ }
+
+ test "tty selection preserves exact rectangle through yank clipboard and mode changes" {
+ if (comptime !panes.Terminal.enabled) return error.SkipZigTest;
+ for ([_]bool{ false, true }) |prompt| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 12 });
+ defer p.deinit();
+ const pane = p.panes[0].?;
+ pane.mode = .tty;
+ pane.tty_filter = false;
+ p.presentation.enabled = false;
+ while (p.nextEffect()) |_| {}
+ p.update(.{ .output = .{ .pane = 0, .bytes = if (prompt)
+ "\x1b]133;A\x07$ \x1b]133;B\x07abc"
+ else
+ "alpha\r\nbravo" } });
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const x = rect.x + config.GUTTER;
+ const y = if (p.settings.tag_bottom) rect.y else rect.y + pardes.BOX_H;
+ JumpSelectionTests.drag(p, x + @as(u16, if (prompt) 0 else 1), y, x + 3, y + @as(u16, if (prompt) 0 else 1));
+ const expected = if (prompt) "$ ab" else "lph\nrav";
+ try std.testing.expectEqual(Pane.Mode.tty, pane.mode);
+ try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?);
+ try std.testing.expectEqualStrings(expected, p.yank orelse return error.MissingAutomaticTtyYank);
+ try noChildInput(p);
+
+ try std.testing.expect(p.executeBuiltinLine(0, "ClipYank"));
+ try noChildInput(p);
+ try std.testing.expectEqualStrings(expected, p.fallback.clipboard.items);
+ p.update(.{ .key = .{ .cp = p.opts.tty_toggle, .ctrl = true } });
+ try std.testing.expectEqual(Pane.Mode.normal, pane.mode);
+ const normal = try p.render(frame.allocator());
+ if (!prompt) {
+ const selection_bg: pardes.Color = .{ .rgb = p.theme().sel_bg };
+ try std.testing.expect(!std.meta.eql(selection_bg, normal.at(x + 4, y).style.bg));
+ try std.testing.expect(!std.meta.eql(selection_bg, normal.at(x, y + 1).style.bg));
+ }
+ try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?);
+ p.update(.{ .key = .{ .cp = 'y' } });
+ try std.testing.expectEqualStrings(expected, p.yank.?);
+ try noChildInput(p);
+ p.update(.{ .key = .{ .cp = p.opts.tty_toggle, .ctrl = true } });
+ try std.testing.expectEqual(Pane.Mode.tty, pane.mode);
+ try noChildInput(p);
+ _ = try p.render(frame.allocator());
+ try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?);
+ p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b]133;C\x07" ++ "\r\nnew output" ** 30 } });
+ _ = try p.render(frame.allocator());
+ try std.testing.expectEqual(Pane.Mode.tty, pane.mode);
+ try std.testing.expectEqualStrings(expected, pardes.test_api.heldSelection(p, 0).?);
+ try std.testing.expectEqualStrings(expected, p.yank.?);
+ try noChildInput(p);
+ }
+ }
+
+ test "tty selection pastes across panes once with existing bracket and mouse protocols" {
+ if (comptime !panes.Terminal.enabled) return error.SkipZigTest;
+ for ([_]bool{ false, true }) |bracketed| {
+ for ([_]bool{ false, true }) |reports_mouse| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 24 });
+ defer p.deinit();
+ p.presentation.enabled = false;
+ p.spawnTty(0);
+ for (p.panes[0..2]) |slot| {
+ slot.?.mode = .tty;
+ slot.?.tty_filter = false;
+ slot.?.greet = false;
+ slot.?.vweight = 1;
+ }
+ p.update(.tick);
+ while (p.nextEffect()) |_| {}
+ p.update(.{ .output = .{ .pane = 0, .bytes = "alpha\r\nbravo" } });
+ p.update(.{ .output = .{ .pane = 1, .bytes = "target> " } });
+ if (bracketed) p.update(.{ .output = .{ .pane = 1, .bytes = "\x1b[?2004h" } });
+ if (reports_mouse) p.update(.{ .output = .{ .pane = 1, .bytes = "\x1b[?1000h\x1b[?1006h" } });
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const source = p.rects[0];
+ const sx = source.x + config.GUTTER;
+ const sy = if (p.settings.tag_bottom) source.y else source.y + pardes.BOX_H;
+ JumpSelectionTests.drag(p, sx + 1, sy, sx + 3, sy + 1);
+ try std.testing.expectEqualStrings("lph\nrav", p.yank orelse return error.MissingAutomaticTtyYank);
+ try noChildInput(p);
+
+ const target = p.rects[1];
+ const tx = target.x + config.GUTTER + 2;
+ const ty = if (p.settings.tag_bottom) target.y else target.y + pardes.BOX_H;
+ p.update(.{ .mouse = .{ .button = .left, .kind = .press, .col = tx, .row = ty } });
+ p.update(.{ .mouse = .{ .button = .right, .kind = .press, .col = tx, .row = ty } });
+ p.update(.{ .mouse = .{ .button = .right, .kind = .release, .col = tx, .row = ty } });
+ p.update(.{ .mouse = .{ .button = .left, .kind = .release, .col = tx, .row = ty } });
+ var written: std.Io.Writer.Allocating = .init(p.gpa);
+ defer written.deinit();
+ while (p.nextEffect()) |effect| {
+ if (effect == .write) {
+ try std.testing.expectEqual(@as(u8, 1), effect.write.pane);
+ try written.writer.writeAll(effect.write.bytes.slice());
+ }
+ }
+ const expected = try std.fmt.allocPrint(p.gpa, "{s}{s}", .{
+ if (reports_mouse) "\x1b[<0;3;1M\x1b[<0;3;1m" else "",
+ if (bracketed) "\x1b[200~lph\nrav\x1b[201~" else "lph\rrav",
+ });
+ defer p.gpa.free(expected);
+ try std.testing.expectEqualStrings(expected, written.written());
+ try std.testing.expectEqual(Pane.Mode.tty, p.panes[0].?.mode);
+ try std.testing.expectEqual(Pane.Mode.tty, p.panes[1].?.mode);
+ try std.testing.expectEqualStrings("lph\nrav", pardes.test_api.heldSelection(p, 0).?);
+ }
+ }
+ }
+};
+
const ClickCursorTests = struct {
fn click(p: *Pardes, button: pardes.Mouse.Button, x: u16, y: u16) void {
p.update(.{ .mouse = .{ .button = button, .kind = .press, .col = x, .row = y } });
@@ -2824,6 +2972,7 @@ const TerminalTests = struct {
};
test {
+ _ = TtySelectionTests;
_ = ClickCursorTests;
_ = JumpSelectionTests;
_ = @import("output.zig");