diff --git a/src/terminal/new/PageList.zig b/src/terminal/new/PageList.zig index b17a460b3..5fbd7c480 100644 --- a/src/terminal/new/PageList.zig +++ b/src/terminal/new/PageList.zig @@ -488,21 +488,36 @@ pub const RowChunkIterator = struct { /// /// This is a more efficient way to iterate through the data in a region, /// 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( self: *const PageList, tl_pt: point.Point, + bl_pt: ?point.Point, ) RowChunkIterator { + // TODO: bl_pt assertions + const tl = self.getTopLeft(tl_pt); - const limit: RowChunkIterator.Limit = switch (tl_pt) { - // These always go to the end of the screen. - .screen, .active => .{ .none = {} }, + const limit: RowChunkIterator.Limit = limit: { + if (bl_pt) |pt| { + const bl = self.getTopLeft(pt); + break :limit .{ .row = bl.forward(pt.coord().y).? }; + } - // Viewport always is rows long - .viewport => .{ .count = self.rows }, + break :limit switch (tl_pt) { + // 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 - // to calculate but also more rare of a thing to iterate over. - .history => .{ .row = self.getTopLeft(.active) }, + // Viewport always is rows long + .viewport => .{ .count = self.rows }, + + // 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 }; @@ -1040,7 +1055,7 @@ test "PageList rowChunkIterator single page" { try testing.expect(s.pages.first.?.next == null); // Iterate the active area - var it = s.rowChunkIterator(.{ .active = .{} }); + var it = s.rowChunkIterator(.{ .active = .{} }, null); { const chunk = it.next().?; try testing.expect(chunk.page == s.pages.first.?); @@ -1068,7 +1083,7 @@ test "PageList rowChunkIterator two pages" { try testing.expect(try s.grow() != null); // Iterate the active area - var it = s.rowChunkIterator(.{ .active = .{} }); + var it = s.rowChunkIterator(.{ .active = .{} }, null); { const chunk = it.next().?; 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); // Iterate the active area - var it = s.rowChunkIterator(.{ .history = .{} }); + var it = s.rowChunkIterator(.{ .history = .{} }, null); { const active_tl = s.getTopLeft(.active); const chunk = it.next().?; diff --git a/src/terminal/new/Screen.zig b/src/terminal/new/Screen.zig index d0efdb5e4..e57d8111c 100644 --- a/src/terminal/new/Screen.zig +++ b/src/terminal/new/Screen.zig @@ -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 -/// are blanked using the given blank cell. -pub fn eraseActive(self: *Screen) void { - var it = self.pages.rowChunkIterator(.{ .active = .{} }); +// Erase the region specified by tl and bl, inclusive. Erased cells are +// colored with the current style background color. This will erase all +// cells in the rows. +pub fn eraseRows(self: *Screen, tl: point.Point, bl: ?point.Point) void { + var it = self.pages.rowChunkIterator(tl, bl); while (it.next()) |chunk| { for (chunk.rows()) |*row| { const cells_offset = row.cells; @@ -345,6 +346,20 @@ pub fn eraseCells( @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 /// require preserving the bg color. 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)); } -test "Screen eraseActive one line" { +test "Screen eraseRows active one line" { const testing = std.testing; const alloc = testing.allocator; @@ -751,13 +766,13 @@ test "Screen eraseActive one line" { defer s.deinit(); try s.testWriteString("hello, world"); - s.eraseActive(); + s.eraseRows(.{ .active = .{} }, null); const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} }); defer alloc.free(str); try testing.expectEqualStrings("", str); } -test "Screen eraseActive multi line" { +test "Screen eraseRows active multi line" { const testing = std.testing; const alloc = testing.allocator; @@ -765,13 +780,13 @@ test "Screen eraseActive multi line" { defer s.deinit(); try s.testWriteString("hello\nworld"); - s.eraseActive(); + s.eraseRows(.{ .active = .{} }, null); const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} }); defer alloc.free(str); try testing.expectEqualStrings("", str); } -test "Screen eraseActive styled line" { +test "Screen eraseRows active styled line" { const testing = std.testing; const alloc = testing.allocator; @@ -786,7 +801,7 @@ test "Screen eraseActive styled line" { const page = s.cursor.page_offset.page.data; 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 try testing.expectEqual(@as(usize, 0), page.styles.count(page.memory));