mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 00:36:07 +03:00
selection: containedRow for getting the row that contains a screen point
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
const Selection = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const point = @import("point.zig");
|
||||
const Screen = @import("Screen.zig");
|
||||
const ScreenPoint = point.ScreenPoint;
|
||||
@ -93,6 +94,42 @@ pub fn containsRow(self: Selection, p: ScreenPoint) bool {
|
||||
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, screen: *const Screen, p: ScreenPoint) ?Selection {
|
||||
const tl = self.topLeft();
|
||||
const br = self.bottomRight();
|
||||
if (p.y < tl.y or p.y > br.y) return null;
|
||||
|
||||
if (p.y == tl.y) {
|
||||
// If the selection is JUST this line, return it as-is.
|
||||
if (p.y == br.y) return self;
|
||||
|
||||
// Selection top-left line matches only.
|
||||
return .{
|
||||
.start = tl,
|
||||
.end = .{ .y = tl.y, .x = screen.cols - 1 },
|
||||
};
|
||||
}
|
||||
|
||||
// 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 .{
|
||||
.start = .{ .y = br.y, .x = 0 },
|
||||
.end = br,
|
||||
};
|
||||
}
|
||||
|
||||
// Row is somewhere between our selection lines so we return the full line.
|
||||
return .{
|
||||
.start = .{ .y = p.y, .x = 0 },
|
||||
.end = .{ .y = p.y, .x = screen.cols - 1 },
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the top left point of the selection.
|
||||
pub fn topLeft(self: Selection) ScreenPoint {
|
||||
return switch (self.order()) {
|
||||
@ -162,6 +199,58 @@ test "Selection: contains" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: containedRow" {
|
||||
const testing = std.testing;
|
||||
var screen = try Screen.init(testing.allocator, 5, 10, 0);
|
||||
defer screen.deinit();
|
||||
|
||||
{
|
||||
const sel: Selection = .{
|
||||
.start = .{ .x = 5, .y = 1 },
|
||||
.end = .{ .x = 3, .y = 3 },
|
||||
};
|
||||
|
||||
// Not contained
|
||||
try testing.expect(sel.containedRow(&screen, .{ .x = 1, .y = 4 }) == null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(Selection{
|
||||
.start = sel.start,
|
||||
.end = .{ .x = screen.cols - 1, .y = 1 },
|
||||
}, sel.containedRow(&screen, .{ .x = 1, .y = 1 }).?);
|
||||
|
||||
// End line
|
||||
try testing.expectEqual(Selection{
|
||||
.start = .{ .x = 0, .y = 3 },
|
||||
.end = sel.end,
|
||||
}, sel.containedRow(&screen, .{ .x = 2, .y = 3 }).?);
|
||||
|
||||
// Middle line
|
||||
try testing.expectEqual(Selection{
|
||||
.start = .{ .x = 0, .y = 2 },
|
||||
.end = .{ .x = screen.cols - 1, .y = 2 },
|
||||
}, sel.containedRow(&screen, .{ .x = 2, .y = 2 }).?);
|
||||
}
|
||||
|
||||
// Single-line selection
|
||||
{
|
||||
const sel: Selection = .{
|
||||
.start = .{ .x = 2, .y = 1 },
|
||||
.end = .{ .x = 6, .y = 1 },
|
||||
};
|
||||
|
||||
// Not contained
|
||||
try testing.expect(sel.containedRow(&screen, .{ .x = 1, .y = 0 }) == null);
|
||||
try testing.expect(sel.containedRow(&screen, .{ .x = 1, .y = 2 }) == null);
|
||||
|
||||
// Contained
|
||||
try testing.expectEqual(Selection{
|
||||
.start = sel.start,
|
||||
.end = sel.end,
|
||||
}, sel.containedRow(&screen, .{ .x = 1, .y = 1 }).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: within" {
|
||||
const testing = std.testing;
|
||||
{
|
||||
|
Reference in New Issue
Block a user