mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 08:16:13 +03:00
terminal: Selection.containedRow
This commit is contained in:
@ -278,6 +278,62 @@ pub fn contains(self: Selection, s: *const Screen, pin: Pin) bool {
|
|||||||
return p.y > tl.y and p.y < br.y;
|
return p.y > tl.y and p.y < br.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a selection for a single row in the screen. This will return null
|
||||||
|
/// if the row is not included in the selection.
|
||||||
|
pub fn containedRow(self: Selection, s: *const Screen, pin: Pin) ?Selection {
|
||||||
|
const tl_pin = self.topLeft(s);
|
||||||
|
const br_pin = self.bottomRight(s);
|
||||||
|
|
||||||
|
// This is definitely not very efficient. Low-hanging fruit to
|
||||||
|
// improve this.
|
||||||
|
const tl = s.pages.pointFromPin(.screen, tl_pin).?.screen;
|
||||||
|
const br = s.pages.pointFromPin(.screen, br_pin).?.screen;
|
||||||
|
const p = s.pages.pointFromPin(.screen, pin).?.screen;
|
||||||
|
|
||||||
|
if (p.y < tl.y or p.y > br.y) return null;
|
||||||
|
|
||||||
|
// Rectangle case: we can return early as the x range will always be the
|
||||||
|
// same. We've already validated that the row is in the selection.
|
||||||
|
if (self.rectangle) return init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = tl.x } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = br.x } }).?,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (p.y == tl.y) {
|
||||||
|
// If the selection is JUST this line, return it as-is.
|
||||||
|
if (p.y == br.y) {
|
||||||
|
return init(tl_pin, br_pin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection top-left line matches only.
|
||||||
|
return init(
|
||||||
|
tl_pin,
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = s.pages.cols - 1 } }).?,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row is our bottom selection, so we return the selection from the
|
||||||
|
// beginning of the line to the br. We know our selection is more than
|
||||||
|
// one line (due to conditionals above)
|
||||||
|
if (p.y == br.y) {
|
||||||
|
assert(p.y != tl.y);
|
||||||
|
return init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = 0 } }).?,
|
||||||
|
br_pin,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Row is somewhere between our selection lines so we return the full line.
|
||||||
|
return init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = 0 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .y = p.y, .x = s.pages.cols - 1 } }).?,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Possible adjustments to the selection.
|
/// Possible adjustments to the selection.
|
||||||
pub const Adjustment = enum {
|
pub const Adjustment = enum {
|
||||||
left,
|
left,
|
||||||
@ -1231,3 +1287,123 @@ test "Selection: contains, rectangle" {
|
|||||||
try testing.expect(!sel.contains(&s, s.pages.pin(.{ .screen = .{ .x = 12, .y = 1 } }).?));
|
try testing.expect(!sel.contains(&s, s.pages.pin(.{ .screen = .{ .x = 12, .y = 1 } }).?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Selection: containedRow" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var s = try Screen.init(testing.allocator, 10, 5, 0);
|
||||||
|
defer s.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
const sel = Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 5, .y = 1 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Not contained
|
||||||
|
try testing.expect(sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 4 } }).?,
|
||||||
|
) == null);
|
||||||
|
|
||||||
|
// Start line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
sel.start(),
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = s.pages.cols - 1, .y = 1 } }).?,
|
||||||
|
false,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
|
).?);
|
||||||
|
|
||||||
|
// End line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 0, .y = 3 } }).?,
|
||||||
|
sel.end(),
|
||||||
|
false,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
||||||
|
).?);
|
||||||
|
|
||||||
|
// Middle line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 0, .y = 2 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = s.pages.cols - 1, .y = 2 } }).?,
|
||||||
|
false,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
|
).?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectangle
|
||||||
|
{
|
||||||
|
const sel = Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 6, .y = 3 } }).?,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Not contained
|
||||||
|
try testing.expect(sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 4 } }).?,
|
||||||
|
) == null);
|
||||||
|
|
||||||
|
// Start line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 1 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 6, .y = 1 } }).?,
|
||||||
|
true,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
|
).?);
|
||||||
|
|
||||||
|
// End line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 3 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 6, .y = 3 } }).?,
|
||||||
|
true,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 3 } }).?,
|
||||||
|
).?);
|
||||||
|
|
||||||
|
// Middle line
|
||||||
|
try testing.expectEqual(Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 3, .y = 2 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 6, .y = 2 } }).?,
|
||||||
|
true,
|
||||||
|
), sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||||
|
).?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single-line selection
|
||||||
|
{
|
||||||
|
const sel = Selection.init(
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 2, .y = 1 } }).?,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 6, .y = 1 } }).?,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Not contained
|
||||||
|
try testing.expect(sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 0 } }).?,
|
||||||
|
) == null);
|
||||||
|
try testing.expect(sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 2 } }).?,
|
||||||
|
) == null);
|
||||||
|
|
||||||
|
// Contained
|
||||||
|
try testing.expectEqual(sel, sel.containedRow(
|
||||||
|
&s,
|
||||||
|
s.pages.pin(.{ .screen = .{ .x = 1, .y = 1 } }).?,
|
||||||
|
).?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user