mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-20 02:36:22 +03:00
terminal2: a selection can be tracked or untracked
This commit is contained in:
@ -19,38 +19,48 @@ const Pin = PageList.Pin;
|
|||||||
// depended on this behavior so I kept it despite the inefficiency. In the
|
// depended on this behavior so I kept it despite the inefficiency. In the
|
||||||
// future, we should take a look at this again!
|
// future, we should take a look at this again!
|
||||||
|
|
||||||
/// Start and end of the selection. There is no guarantee that
|
/// The bounds of the selection.
|
||||||
/// start is before end or vice versa. If a user selects backwards,
|
bounds: Bounds,
|
||||||
/// start will be after end, and vice versa. Use the struct functions
|
|
||||||
/// to not have to worry about this.
|
|
||||||
///
|
|
||||||
/// These are always tracked pins so that they automatically update as
|
|
||||||
/// the screen they're attached to gets scrolled, erased, etc.
|
|
||||||
start: *Pin,
|
|
||||||
end: *Pin,
|
|
||||||
|
|
||||||
/// Whether or not this selection refers to a rectangle, rather than whole
|
/// Whether or not this selection refers to a rectangle, rather than whole
|
||||||
/// lines of a buffer. In this mode, start and end refer to the top left and
|
/// lines of a buffer. In this mode, start and end refer to the top left and
|
||||||
/// bottom right of the rectangle, or vice versa if the selection is backwards.
|
/// bottom right of the rectangle, or vice versa if the selection is backwards.
|
||||||
rectangle: bool = false,
|
rectangle: bool = false,
|
||||||
|
|
||||||
|
/// The bounds of the selection. A selection bounds can be either tracked
|
||||||
|
/// or untracked. Untracked bounds are unsafe beyond the point the terminal
|
||||||
|
/// screen may be modified, since they may point to invalid memory. Tracked
|
||||||
|
/// bounds are always valid and will be updated as the screen changes, but
|
||||||
|
/// are more expensive to exist.
|
||||||
|
///
|
||||||
|
/// In all cases, start and end can be in any order. There is no guarantee that
|
||||||
|
/// start is before end or vice versa. If a user selects backwards,
|
||||||
|
/// start will be after end, and vice versa. Use the struct functions
|
||||||
|
/// to not have to worry about this.
|
||||||
|
pub const Bounds = union(enum) {
|
||||||
|
untracked: struct {
|
||||||
|
start: Pin,
|
||||||
|
end: Pin,
|
||||||
|
},
|
||||||
|
|
||||||
|
tracked: struct {
|
||||||
|
start: *Pin,
|
||||||
|
end: *Pin,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/// Initialize a new selection with the given start and end pins on
|
/// Initialize a new selection with the given start and end pins on
|
||||||
/// the screen. The screen will be used for pin tracking.
|
/// the screen. The screen will be used for pin tracking.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
s: *Screen,
|
start_pin: Pin,
|
||||||
start: Pin,
|
end_pin: Pin,
|
||||||
end: Pin,
|
|
||||||
rect: bool,
|
rect: bool,
|
||||||
) !Selection {
|
) Selection {
|
||||||
// Track our pins
|
|
||||||
const tracked_start = try s.pages.trackPin(start);
|
|
||||||
errdefer s.pages.untrackPin(tracked_start);
|
|
||||||
const tracked_end = try s.pages.trackPin(end);
|
|
||||||
errdefer s.pages.untrackPin(tracked_end);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.start = tracked_start,
|
.bounds = .{ .untracked = .{
|
||||||
.end = tracked_end,
|
.start = start_pin,
|
||||||
|
.end = end_pin,
|
||||||
|
} },
|
||||||
.rectangle = rect,
|
.rectangle = rect,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -59,8 +69,71 @@ pub fn deinit(
|
|||||||
self: Selection,
|
self: Selection,
|
||||||
s: *Screen,
|
s: *Screen,
|
||||||
) void {
|
) void {
|
||||||
s.pages.untrackPin(self.start);
|
switch (self.bounds) {
|
||||||
s.pages.untrackPin(self.end);
|
.tracked => |v| {
|
||||||
|
s.pages.untrackPin(v.start);
|
||||||
|
s.pages.untrackPin(v.end);
|
||||||
|
},
|
||||||
|
|
||||||
|
.untracked => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The starting pin of the selection. This is NOT ordered.
|
||||||
|
pub fn start(self: *Selection) *Pin {
|
||||||
|
return switch (self.bounds) {
|
||||||
|
.untracked => |*v| &v.start,
|
||||||
|
.tracked => |v| v.start,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ending pin of the selection. This is NOT ordered.
|
||||||
|
pub fn end(self: *Selection) *Pin {
|
||||||
|
return switch (self.bounds) {
|
||||||
|
.untracked => |*v| &v.end,
|
||||||
|
.tracked => |v| v.end,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn startConst(self: Selection) Pin {
|
||||||
|
return switch (self.bounds) {
|
||||||
|
.untracked => |v| v.start,
|
||||||
|
.tracked => |v| v.start.*,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endConst(self: Selection) Pin {
|
||||||
|
return switch (self.bounds) {
|
||||||
|
.untracked => |v| v.end,
|
||||||
|
.tracked => |v| v.end.*,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if this is a tracked selection.
|
||||||
|
pub fn tracked(self: *const Selection) bool {
|
||||||
|
return switch (self.bounds) {
|
||||||
|
.untracked => false,
|
||||||
|
.tracked => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this selection a tracked selection. It is asserted this is
|
||||||
|
/// an untracked selection.
|
||||||
|
pub fn track(self: *Selection, s: *Screen) !void {
|
||||||
|
assert(!self.tracked());
|
||||||
|
|
||||||
|
// Track our pins
|
||||||
|
const start_pin = self.bounds.untracked.start;
|
||||||
|
const end_pin = self.bounds.untracked.end;
|
||||||
|
const tracked_start = try s.pages.trackPin(start_pin);
|
||||||
|
errdefer s.pages.untrackPin(tracked_start);
|
||||||
|
const tracked_end = try s.pages.trackPin(end_pin);
|
||||||
|
errdefer s.pages.untrackPin(tracked_end);
|
||||||
|
|
||||||
|
self.bounds = .{ .tracked = .{
|
||||||
|
.start = tracked_start,
|
||||||
|
.end = tracked_end,
|
||||||
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The order of the selection:
|
/// The order of the selection:
|
||||||
@ -78,8 +151,8 @@ pub fn deinit(
|
|||||||
pub const Order = enum { forward, reverse, mirrored_forward, mirrored_reverse };
|
pub const Order = enum { forward, reverse, mirrored_forward, mirrored_reverse };
|
||||||
|
|
||||||
pub fn order(self: Selection, s: *const Screen) Order {
|
pub fn order(self: Selection, s: *const Screen) Order {
|
||||||
const start_pt = s.pages.pointFromPin(.screen, self.start.*).?.screen;
|
const start_pt = s.pages.pointFromPin(.screen, self.startConst()).?.screen;
|
||||||
const end_pt = s.pages.pointFromPin(.screen, self.end.*).?.screen;
|
const end_pt = s.pages.pointFromPin(.screen, self.endConst()).?.screen;
|
||||||
|
|
||||||
if (self.rectangle) {
|
if (self.rectangle) {
|
||||||
// Reverse (also handles single-column)
|
// Reverse (also handles single-column)
|
||||||
@ -121,32 +194,31 @@ pub fn adjust(
|
|||||||
s: *const Screen,
|
s: *const Screen,
|
||||||
adjustment: Adjustment,
|
adjustment: Adjustment,
|
||||||
) void {
|
) void {
|
||||||
//const screen_end = Screen.RowIndexTag.screen.maxLen(screen) - 1;
|
|
||||||
|
|
||||||
// Note that we always adjusts "end" because end always represents
|
// Note that we always adjusts "end" because end always represents
|
||||||
// the last point of the selection by mouse, not necessarilly the
|
// the last point of the selection by mouse, not necessarilly the
|
||||||
// top/bottom visually. So this results in the right behavior
|
// top/bottom visually. So this results in the right behavior
|
||||||
// whether the user drags up or down.
|
// whether the user drags up or down.
|
||||||
|
const end_pin = self.end();
|
||||||
switch (adjustment) {
|
switch (adjustment) {
|
||||||
.up => if (self.end.up(1)) |new_end| {
|
.up => if (end_pin.up(1)) |new_end| {
|
||||||
self.end.* = new_end;
|
end_pin.* = new_end;
|
||||||
} else {
|
} else {
|
||||||
self.end.x = 0;
|
end_pin.x = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
.down => {
|
.down => {
|
||||||
// Find the next non-blank row
|
// Find the next non-blank row
|
||||||
var current = self.end.*;
|
var current = end_pin.*;
|
||||||
while (current.down(1)) |next| : (current = next) {
|
while (current.down(1)) |next| : (current = next) {
|
||||||
const rac = next.rowAndCell();
|
const rac = next.rowAndCell();
|
||||||
const cells = next.page.data.getCells(rac.row);
|
const cells = next.page.data.getCells(rac.row);
|
||||||
if (page.Cell.hasTextAny(cells)) {
|
if (page.Cell.hasTextAny(cells)) {
|
||||||
self.end.* = next;
|
end_pin.* = next;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we're at the bottom, just go to the end of the line
|
// If we're at the bottom, just go to the end of the line
|
||||||
self.end.x = self.end.page.data.size.cols - 1;
|
end_pin.x = end_pin.page.data.size.cols - 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -154,13 +226,13 @@ pub fn adjust(
|
|||||||
var it = s.pages.cellIterator(
|
var it = s.pages.cellIterator(
|
||||||
.left_up,
|
.left_up,
|
||||||
.{ .screen = .{} },
|
.{ .screen = .{} },
|
||||||
s.pages.pointFromPin(.screen, self.end.*).?,
|
s.pages.pointFromPin(.screen, end_pin.*).?,
|
||||||
);
|
);
|
||||||
_ = it.next();
|
_ = it.next();
|
||||||
while (it.next()) |next| {
|
while (it.next()) |next| {
|
||||||
const rac = next.rowAndCell();
|
const rac = next.rowAndCell();
|
||||||
if (rac.cell.hasText()) {
|
if (rac.cell.hasText()) {
|
||||||
self.end.* = next;
|
end_pin.* = next;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,33 +243,33 @@ pub fn adjust(
|
|||||||
// until we find a non-empty cell.
|
// until we find a non-empty cell.
|
||||||
var it = s.pages.cellIterator(
|
var it = s.pages.cellIterator(
|
||||||
.right_down,
|
.right_down,
|
||||||
s.pages.pointFromPin(.screen, self.end.*).?,
|
s.pages.pointFromPin(.screen, end_pin.*).?,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
_ = it.next();
|
_ = it.next();
|
||||||
while (it.next()) |next| {
|
while (it.next()) |next| {
|
||||||
const rac = next.rowAndCell();
|
const rac = next.rowAndCell();
|
||||||
if (rac.cell.hasText()) {
|
if (rac.cell.hasText()) {
|
||||||
self.end.* = next;
|
end_pin.* = next;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.page_up => if (self.end.up(s.pages.rows)) |new_end| {
|
.page_up => if (end_pin.up(s.pages.rows)) |new_end| {
|
||||||
self.end.* = new_end;
|
end_pin.* = new_end;
|
||||||
} else {
|
} else {
|
||||||
self.adjust(s, .home);
|
self.adjust(s, .home);
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO(paged-terminal): this doesn't take into account blanks
|
// TODO(paged-terminal): this doesn't take into account blanks
|
||||||
.page_down => if (self.end.down(s.pages.rows)) |new_end| {
|
.page_down => if (end_pin.down(s.pages.rows)) |new_end| {
|
||||||
self.end.* = new_end;
|
end_pin.* = new_end;
|
||||||
} else {
|
} else {
|
||||||
self.adjust(s, .end);
|
self.adjust(s, .end);
|
||||||
},
|
},
|
||||||
|
|
||||||
.home => self.end.* = s.pages.pin(.{ .screen = .{
|
.home => end_pin.* = s.pages.pin(.{ .screen = .{
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
} }).?,
|
} }).?,
|
||||||
@ -212,8 +284,8 @@ pub fn adjust(
|
|||||||
const rac = next.rowAndCell();
|
const rac = next.rowAndCell();
|
||||||
const cells = next.page.data.getCells(rac.row);
|
const cells = next.page.data.getCells(rac.row);
|
||||||
if (page.Cell.hasTextAny(cells)) {
|
if (page.Cell.hasTextAny(cells)) {
|
||||||
self.end.* = next;
|
end_pin.* = next;
|
||||||
self.end.x = cells.len - 1;
|
end_pin.x = cells.len - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,8 +301,7 @@ test "Selection: adjust right" {
|
|||||||
|
|
||||||
// Simple movement right
|
// Simple movement right
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -241,17 +312,16 @@ test "Selection: adjust right" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 3,
|
.y = 3,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Already at end of the line.
|
// Already at end of the line.
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 2 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -262,17 +332,16 @@ test "Selection: adjust right" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 3,
|
.y = 3,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Already at end of the screen
|
// Already at end of the screen
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -283,11 +352,11 @@ test "Selection: adjust right" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 3,
|
.y = 3,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,8 +368,7 @@ test "Selection: adjust left" {
|
|||||||
|
|
||||||
// Simple movement left
|
// Simple movement left
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -312,17 +380,16 @@ test "Selection: adjust left" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 2,
|
.x = 2,
|
||||||
.y = 3,
|
.y = 3,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Already at beginning of the line.
|
// Already at beginning of the line.
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 0, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -334,11 +401,11 @@ test "Selection: adjust left" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 2,
|
.y = 2,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,8 +417,7 @@ test "Selection: adjust left skips blanks" {
|
|||||||
|
|
||||||
// Same line
|
// Same line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -363,17 +429,16 @@ test "Selection: adjust left skips blanks" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 2,
|
.x = 2,
|
||||||
.y = 3,
|
.y = 3,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge
|
// Edge
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 0, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -385,11 +450,11 @@ test "Selection: adjust left skips blanks" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 2,
|
.x = 2,
|
||||||
.y = 2,
|
.y = 2,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +466,7 @@ test "Selection: adjust up" {
|
|||||||
|
|
||||||
// Not on the first line
|
// Not on the first line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -413,17 +477,16 @@ test "Selection: adjust up" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 3,
|
.x = 3,
|
||||||
.y = 2,
|
.y = 2,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On the first line
|
// On the first line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 0 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 0 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -434,11 +497,11 @@ test "Selection: adjust up" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,8 +513,7 @@ test "Selection: adjust down" {
|
|||||||
|
|
||||||
// Not on the first line
|
// Not on the first line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -462,17 +524,16 @@ test "Selection: adjust down" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 5,
|
.x = 5,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 3,
|
.x = 3,
|
||||||
.y = 4,
|
.y = 4,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On the last line
|
// On the last line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 4 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 4 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -483,11 +544,11 @@ test "Selection: adjust down" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 4,
|
.y = 4,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,8 +560,7 @@ test "Selection: adjust down with not full screen" {
|
|||||||
|
|
||||||
// On the last line
|
// On the last line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 2 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -512,11 +572,11 @@ test "Selection: adjust down with not full screen" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 2,
|
.y = 2,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,8 +588,7 @@ test "Selection: adjust home" {
|
|||||||
|
|
||||||
// On the last line
|
// On the last line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 2 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -541,11 +600,11 @@ test "Selection: adjust home" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 1,
|
.y = 1,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,8 +616,7 @@ test "Selection: adjust end with not full screen" {
|
|||||||
|
|
||||||
// On the last line
|
// On the last line
|
||||||
{
|
{
|
||||||
var sel = try Selection.init(
|
var sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 0 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 4, .y = 0 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -570,11 +628,11 @@ test "Selection: adjust end with not full screen" {
|
|||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.start.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.start().*).?);
|
||||||
try testing.expectEqual(point.Point{ .screen = .{
|
try testing.expectEqual(point.Point{ .screen = .{
|
||||||
.x = 4,
|
.x = 4,
|
||||||
.y = 2,
|
.y = 2,
|
||||||
} }, s.pages.pointFromPin(.screen, sel.end.*).?);
|
} }, s.pages.pointFromPin(.screen, sel.end().*).?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,8 +645,7 @@ test "Selection: order, standard" {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// forward, multi-line
|
// forward, multi-line
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -599,8 +656,7 @@ test "Selection: order, standard" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// reverse, multi-line
|
// reverse, multi-line
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -611,8 +667,7 @@ test "Selection: order, standard" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// forward, same-line
|
// forward, same-line
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -623,8 +678,7 @@ test "Selection: order, standard" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// forward, single char
|
// forward, single char
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -635,8 +689,7 @@ test "Selection: order, standard" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// reverse, single line
|
// reverse, single line
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
false,
|
false,
|
||||||
@ -661,8 +714,7 @@ test "Selection: order, rectangle" {
|
|||||||
// BR - bottom right
|
// BR - bottom right
|
||||||
{
|
{
|
||||||
// forward (TL -> BR)
|
// forward (TL -> BR)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -673,8 +725,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// reverse (BR -> TL)
|
// reverse (BR -> TL)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -685,8 +736,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// mirrored_forward (TR -> BL)
|
// mirrored_forward (TR -> BL)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 3 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -697,8 +747,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// mirrored_reverse (BL -> TR)
|
// mirrored_reverse (BL -> TR)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 3 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -709,8 +758,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// forward, single line (left -> right )
|
// forward, single line (left -> right )
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -721,8 +769,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// reverse, single line (right -> left)
|
// reverse, single line (right -> left)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -733,8 +780,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// forward, single column (top -> bottom)
|
// forward, single column (top -> bottom)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -745,8 +791,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// reverse, single column (bottom -> top)
|
// reverse, single column (bottom -> top)
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
@ -757,8 +802,7 @@ test "Selection: order, rectangle" {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// forward, single cell
|
// forward, single cell
|
||||||
const sel = try Selection.init(
|
const sel = Selection.init(
|
||||||
&s,
|
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
true,
|
true,
|
||||||
|
Reference in New Issue
Block a user