selection: containedRow for getting the row that contains a screen point

This commit is contained in:
Mitchell Hashimoto
2023-03-23 10:01:09 -07:00
parent 530f28a706
commit d4cbe88c98

View File

@ -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;
{