diff --git a/src/terminal/page.zig b/src/terminal/page.zig index 7e7cdbaca..f9ca0ecb8 100644 --- a/src/terminal/page.zig +++ b/src/terminal/page.zig @@ -220,9 +220,6 @@ pub const Page = struct { /// If the other page has more columns, the extra columns will be /// truncated. If the other page has fewer columns, the extra columns /// will be zeroed. - /// - /// The current page is assumed to be empty. We will not clear any - /// existing data in the current page. pub fn cloneFrom( self: *Page, other: *const Page, @@ -232,11 +229,6 @@ pub const Page = struct { assert(y_start <= y_end); assert(y_end <= other.size.rows); assert(y_end - y_start <= self.size.rows); - if (comptime std.debug.runtime_safety) { - // The current page must be empty. - assert(self.styles.count(self.memory) == 0); - assert(self.graphemeCount() == 0); - } const other_rows = other.rows.ptr(other.memory)[y_start..y_end]; const rows = self.rows.ptr(self.memory)[0 .. y_end - y_start]; @@ -254,15 +246,23 @@ pub const Page = struct { dst_row: *Row, src_row: *const Row, ) !void { + const cell_len = @min(self.size.cols, other.size.cols); + const other_cells = src_row.cells.ptr(other.memory)[0..cell_len]; + const cells = dst_row.cells.ptr(self.memory)[0..cell_len]; + + // If our destination has styles or graphemes then we need to + // clear some state. + if (dst_row.grapheme or dst_row.styled) { + self.clearCells(dst_row, 0, cells.len); + assert(!dst_row.grapheme); + assert(!dst_row.styled); + } + // Copy all the row metadata but keep our cells offset const cells_offset = dst_row.cells; dst_row.* = src_row.*; dst_row.cells = cells_offset; - const cell_len = @min(self.size.cols, other.size.cols); - const other_cells = src_row.cells.ptr(other.memory)[0..cell_len]; - const cells = dst_row.cells.ptr(self.memory)[0..cell_len]; - // If we have no managed memory in the row, we can just copy. if (!dst_row.grapheme and !dst_row.styled) { fastmem.copy(Cell, cells, other_cells); @@ -1278,3 +1278,47 @@ test "Page cloneFrom graphemes" { try testing.expectEqual(@as(u21, 0), rac.cell.content.codepoint); } } + +test "Page cloneFrom frees dst graphemes" { + var page = try Page.init(.{ + .cols = 10, + .rows = 10, + .styles = 8, + }); + defer page.deinit(); + for (0..page.capacity.rows) |y| { + const rac = page.getRowAndCell(1, y); + rac.cell.* = .{ + .content_tag = .codepoint, + .content = .{ .codepoint = @intCast(y + 1) }, + }; + } + + // Clone + var page2 = try Page.init(.{ + .cols = 10, + .rows = 10, + .styles = 8, + }); + defer page2.deinit(); + for (0..page2.capacity.rows) |y| { + const rac = page2.getRowAndCell(1, y); + rac.cell.* = .{ + .content_tag = .codepoint, + .content = .{ .codepoint = @intCast(y + 1) }, + }; + try page2.appendGrapheme(rac.row, rac.cell, 0x0A); + } + + // Clone from page which has no graphemes. + try page2.cloneFrom(&page, 0, page.size.rows); + + // Read it again + for (0..page2.capacity.rows) |y| { + const rac = page2.getRowAndCell(1, y); + try testing.expectEqual(@as(u21, @intCast(y + 1)), rac.cell.content.codepoint); + try testing.expect(!rac.row.grapheme); + try testing.expect(!rac.cell.hasGrapheme()); + } + try testing.expectEqual(@as(usize, 0), page2.graphemeCount()); +}