terminal/new: eraseRows

This commit is contained in:
Mitchell Hashimoto
2024-02-27 17:21:31 -08:00
parent 55b34251ac
commit 6e0df767cf
2 changed files with 51 additions and 21 deletions

View File

@ -488,21 +488,36 @@ pub const RowChunkIterator = struct {
/// ///
/// This is a more efficient way to iterate through the data in a region, /// This is a more efficient way to iterate through the data in a region,
/// since you can do simple pointer math and so on. /// since you can do simple pointer math and so on.
///
/// If bl_pt is non-null, iteration will stop at the bottom left point
/// (inclusive). If bl_pt is null, the entire region specified by the point
/// tag will be iterated over. tl_pt and bl_pt must be the same tag, and
/// bl_pt must be greater than or equal to tl_pt.
pub fn rowChunkIterator( pub fn rowChunkIterator(
self: *const PageList, self: *const PageList,
tl_pt: point.Point, tl_pt: point.Point,
bl_pt: ?point.Point,
) RowChunkIterator { ) RowChunkIterator {
// TODO: bl_pt assertions
const tl = self.getTopLeft(tl_pt); const tl = self.getTopLeft(tl_pt);
const limit: RowChunkIterator.Limit = switch (tl_pt) { const limit: RowChunkIterator.Limit = limit: {
// These always go to the end of the screen. if (bl_pt) |pt| {
.screen, .active => .{ .none = {} }, const bl = self.getTopLeft(pt);
break :limit .{ .row = bl.forward(pt.coord().y).? };
}
// Viewport always is rows long break :limit switch (tl_pt) {
.viewport => .{ .count = self.rows }, // These always go to the end of the screen.
.screen, .active => .{ .none = {} },
// History goes to the top of the active area. This is more expensive // Viewport always is rows long
// to calculate but also more rare of a thing to iterate over. .viewport => .{ .count = self.rows },
.history => .{ .row = self.getTopLeft(.active) },
// History goes to the top of the active area. This is more expensive
// to calculate but also more rare of a thing to iterate over.
.history => .{ .row = self.getTopLeft(.active) },
};
}; };
return .{ .row = tl.forward(tl_pt.coord().y), .limit = limit }; return .{ .row = tl.forward(tl_pt.coord().y), .limit = limit };
@ -1040,7 +1055,7 @@ test "PageList rowChunkIterator single page" {
try testing.expect(s.pages.first.?.next == null); try testing.expect(s.pages.first.?.next == null);
// Iterate the active area // Iterate the active area
var it = s.rowChunkIterator(.{ .active = .{} }); var it = s.rowChunkIterator(.{ .active = .{} }, null);
{ {
const chunk = it.next().?; const chunk = it.next().?;
try testing.expect(chunk.page == s.pages.first.?); try testing.expect(chunk.page == s.pages.first.?);
@ -1068,7 +1083,7 @@ test "PageList rowChunkIterator two pages" {
try testing.expect(try s.grow() != null); try testing.expect(try s.grow() != null);
// Iterate the active area // Iterate the active area
var it = s.rowChunkIterator(.{ .active = .{} }); var it = s.rowChunkIterator(.{ .active = .{} }, null);
{ {
const chunk = it.next().?; const chunk = it.next().?;
try testing.expect(chunk.page == s.pages.first.?); try testing.expect(chunk.page == s.pages.first.?);
@ -1102,7 +1117,7 @@ test "PageList rowChunkIterator history two pages" {
try testing.expect(try s.grow() != null); try testing.expect(try s.grow() != null);
// Iterate the active area // Iterate the active area
var it = s.rowChunkIterator(.{ .history = .{} }); var it = s.rowChunkIterator(.{ .history = .{} }, null);
{ {
const active_tl = s.getTopLeft(.active); const active_tl = s.getTopLeft(.active);
const chunk = it.next().?; const chunk = it.next().?;

View File

@ -277,10 +277,11 @@ pub fn scroll(self: *Screen, behavior: Scroll) void {
} }
} }
/// Erase the active area of the screen from y=0 to rows-1. The cells // Erase the region specified by tl and bl, inclusive. Erased cells are
/// are blanked using the given blank cell. // colored with the current style background color. This will erase all
pub fn eraseActive(self: *Screen) void { // cells in the rows.
var it = self.pages.rowChunkIterator(.{ .active = .{} }); pub fn eraseRows(self: *Screen, tl: point.Point, bl: ?point.Point) void {
var it = self.pages.rowChunkIterator(tl, bl);
while (it.next()) |chunk| { while (it.next()) |chunk| {
for (chunk.rows()) |*row| { for (chunk.rows()) |*row| {
const cells_offset = row.cells; const cells_offset = row.cells;
@ -345,6 +346,20 @@ pub fn eraseCells(
@memset(cells, self.blankCell()); @memset(cells, self.blankCell());
} }
/// Erase cells but only if they are not protected.
pub fn eraseUnprotectedCells(
self: *Screen,
page: *Page,
row: *Row,
cells: []Cell,
) void {
for (cells) |*cell| {
if (cell.protected) continue;
const cell_multi: [*]Cell = @ptrCast(cell);
self.eraseCells(page, row, cell_multi[0..1]);
}
}
/// Returns the blank cell to use when doing terminal operations that /// Returns the blank cell to use when doing terminal operations that
/// require preserving the bg color. /// require preserving the bg color.
fn blankCell(self: *const Screen) Cell { fn blankCell(self: *const Screen) Cell {
@ -743,7 +758,7 @@ test "Screen style reset with unset" {
try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory)); try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory));
} }
test "Screen eraseActive one line" { test "Screen eraseRows active one line" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -751,13 +766,13 @@ test "Screen eraseActive one line" {
defer s.deinit(); defer s.deinit();
try s.testWriteString("hello, world"); try s.testWriteString("hello, world");
s.eraseActive(); s.eraseRows(.{ .active = .{} }, null);
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} }); const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str); defer alloc.free(str);
try testing.expectEqualStrings("", str); try testing.expectEqualStrings("", str);
} }
test "Screen eraseActive multi line" { test "Screen eraseRows active multi line" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -765,13 +780,13 @@ test "Screen eraseActive multi line" {
defer s.deinit(); defer s.deinit();
try s.testWriteString("hello\nworld"); try s.testWriteString("hello\nworld");
s.eraseActive(); s.eraseRows(.{ .active = .{} }, null);
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} }); const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str); defer alloc.free(str);
try testing.expectEqualStrings("", str); try testing.expectEqualStrings("", str);
} }
test "Screen eraseActive styled line" { test "Screen eraseRows active styled line" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -786,7 +801,7 @@ test "Screen eraseActive styled line" {
const page = s.cursor.page_offset.page.data; const page = s.cursor.page_offset.page.data;
try testing.expectEqual(@as(usize, 1), page.styles.count(page.memory)); try testing.expectEqual(@as(usize, 1), page.styles.count(page.memory));
s.eraseActive(); s.eraseRows(.{ .active = .{} }, null);
// We should have none because active cleared it // We should have none because active cleared it
try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory)); try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory));