terminal: screen lineIterator

This commit is contained in:
Mitchell Hashimoto
2024-03-15 19:46:14 -07:00
parent f4fa54984c
commit e18a77739c
3 changed files with 85 additions and 1 deletions

View File

@ -3717,6 +3717,7 @@ test "Screen: write long emoji" {
try testing.expectEqual(@as(usize, 5), s.cursor.x);
}
// X
test "Screen: lineIterator" {
const testing = std.testing;
const alloc = testing.allocator;
@ -3745,6 +3746,7 @@ test "Screen: lineIterator" {
try testing.expect(iter.next() == null);
}
// X
test "Screen: lineIterator soft wrap" {
const testing = std.testing;
const alloc = testing.allocator;
@ -3773,6 +3775,7 @@ test "Screen: lineIterator soft wrap" {
try testing.expect(iter.next() == null);
}
// X - selectLine in new screen
test "Screen: getLine soft wrap" {
const testing = std.testing;
const alloc = testing.allocator;

View File

@ -10,6 +10,7 @@ const point = @import("point.zig");
const pagepkg = @import("page.zig");
const stylepkg = @import("style.zig");
const size = @import("size.zig");
const Selection = @import("Selection.zig");
const OffsetBuf = size.OffsetBuf;
const Capacity = pagepkg.Capacity;
const Page = pagepkg.Page;

View File

@ -1247,7 +1247,7 @@ pub const SelectLine = struct {
/// lines and will omit the leading and trailing whitespace. If the point is
/// over whitespace but the line has non-whitespace characters elsewhere, the
/// line will be selected.
pub fn selectLine(self: *Screen, opts: SelectLine) ?Selection {
pub fn selectLine(self: *const Screen, opts: SelectLine) ?Selection {
_ = self;
// Get the current point semantic prompt state since that determines
@ -1713,6 +1713,35 @@ pub fn selectPrompt(self: *Screen, pin: Pin) ?Selection {
return Selection.init(start, end, false);
}
pub const LineIterator = struct {
screen: *const Screen,
current: ?Pin = null,
pub fn next(self: *LineIterator) ?Selection {
const current = self.current orelse return null;
const result = self.screen.selectLine(.{
.pin = current,
.whitespace = null,
.semantic_prompt_boundary = false,
}) orelse {
self.current = null;
return null;
};
self.current = result.end().down(1);
return result;
}
};
/// Returns an iterator to move through the soft-wrapped lines starting
/// from pin.
pub fn lineIterator(self: *const Screen, start: Pin) LineIterator {
return LineIterator{
.screen = self,
.current = start,
};
}
/// Returns the change in x/y that is needed to reach "to" from "from"
/// within a prompt. If "to" is before or after the prompt bounds then
/// the result will be bounded to the prompt.
@ -6355,3 +6384,54 @@ test "Screen: selectionString, rectangle, more complex w/breaks" {
defer alloc.free(contents);
try testing.expectEqualStrings(expected, contents);
}
test "Screen: lineIterator" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD\n2EFGH";
try s.testWriteString(str);
// Test the line iterator
var iter = s.lineIterator(s.pages.pin(.{ .viewport = .{} }).?);
{
const sel = iter.next().?;
const actual = try s.selectionString(alloc, sel, false);
defer alloc.free(actual);
try testing.expectEqualStrings("1ABCD", actual);
}
{
const sel = iter.next().?;
const actual = try s.selectionString(alloc, sel, false);
defer alloc.free(actual);
try testing.expectEqualStrings("2EFGH", actual);
}
}
test "Screen: lineIterator soft wrap" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD2EFGH\n3ABCD";
try s.testWriteString(str);
// Test the line iterator
var iter = s.lineIterator(s.pages.pin(.{ .viewport = .{} }).?);
{
const sel = iter.next().?;
const actual = try s.selectionString(alloc, sel, false);
defer alloc.free(actual);
try testing.expectEqualStrings("1ABCD2EFGH", actual);
}
{
const sel = iter.next().?;
const actual = try s.selectionString(alloc, sel, false);
defer alloc.free(actual);
try testing.expectEqualStrings("3ABCD", actual);
}
// try testing.expect(iter.next() == null);
}