From 9351cab038999b321cc3aa535b74403d4de25451 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 13 Mar 2024 13:05:53 -0700 Subject: [PATCH] terminal: Screen clone should preserve selection order --- src/terminal/Screen.zig | 58 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index fd295fc9c..04db073a6 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -263,12 +263,28 @@ pub fn clonePool( // Preserve our selection if we have one. const sel: ?Selection = if (self.selection) |sel| sel: { assert(sel.tracked()); - const start_pin = pin_remap.get(sel.bounds.tracked.start) orelse start: { + + const ordered: struct { + tl: *Pin, + br: *Pin, + } = switch (sel.order(self)) { + .forward, .mirrored_forward => .{ + .tl = sel.bounds.tracked.start, + .br = sel.bounds.tracked.end, + }, + .reverse, .mirrored_reverse => .{ + .tl = sel.bounds.tracked.end, + .br = sel.bounds.tracked.start, + }, + }; + + const start_pin = pin_remap.get(ordered.tl) orelse start: { // No start means it is outside the cloned area. We change it // to the top-left. break :start try pages.trackPin(.{ .page = pages.pages.first.? }); }; - const end_pin = pin_remap.get(sel.bounds.tracked.end) orelse end: { + + const end_pin = pin_remap.get(ordered.br) orelse end: { // No end means it is outside the cloned area. We change it // to the bottom-right. break :end try pages.trackPin(pages.pin(.{ .active = .{ @@ -276,6 +292,7 @@ pub fn clonePool( .y = pages.rows - 1, } }) orelse break :sel null); }; + break :sel .{ .bounds = .{ .tracked = .{ .start = start_pin, @@ -2877,6 +2894,43 @@ test "Screen: clone contains selection end cutoff" { } } +test "Screen: clone contains selection end cutoff reversed" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 5, 3, 1); + defer s.deinit(); + try s.testWriteString("1ABCD\n2EFGH\n3IJKL"); + + // Select a single line + try s.select(Selection.init( + s.pages.pin(.{ .active = .{ .x = 2, .y = 2 } }).?, + s.pages.pin(.{ .active = .{ .x = 0, .y = 1 } }).?, + false, + )); + + // Clone + var s2 = try s.clone( + alloc, + .{ .active = .{ .y = 0 } }, + .{ .active = .{ .y = 1 } }, + ); + defer s2.deinit(); + + // Our selection should remain valid + { + const sel = s2.selection.?; + try testing.expectEqual(point.Point{ .active = .{ + .x = 0, + .y = 1, + } }, s2.pages.pointFromPin(.active, sel.start()).?); + try testing.expectEqual(point.Point{ .active = .{ + .x = s2.pages.cols - 1, + .y = 2, + } }, s2.pages.pointFromPin(.active, sel.end()).?); + } +} + test "Screen: clone basic" { const testing = std.testing; const alloc = testing.allocator;