terminal/new: clear graphemes on overwrite

This commit is contained in:
Mitchell Hashimoto
2024-02-23 22:26:59 -08:00
parent 73f07725da
commit eb3afae57e
2 changed files with 86 additions and 2 deletions

View File

@ -513,10 +513,15 @@ fn printCell(
}
// If the prior value had graphemes, clear those
if (cell.grapheme) @panic("TODO: clear graphemes");
if (cell.grapheme) {
self.screen.cursor.page_offset.page.data.clearGrapheme(
self.screen.cursor.page_row,
cell,
);
}
// Write
self.screen.cursor.page_cell.* = .{
cell.* = .{
.style_id = self.screen.cursor.style_id,
.codepoint = c,
.wide = wide,
@ -1362,6 +1367,33 @@ test "Terminal: print invalid VS16 with second char" {
}
}
test "Terminal: overwrite grapheme should clear grapheme data" {
var t = try init(testing.allocator, 5, 5);
defer t.deinit(testing.allocator);
// Enable grapheme clustering
t.modes.set(.grapheme_cluster, true);
try t.print(0x26C8); // Thunder cloud and rain
try t.print(0xFE0E); // VS15 to make narrow
t.setCursorPos(1, 1);
try t.print('A');
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("A", str);
}
{
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
const cell = list_cell.cell;
try testing.expectEqual(@as(u21, 'A'), cell.codepoint);
try testing.expect(!cell.grapheme);
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
}
}
test "Terminal: soft wrap" {
var t = try init(testing.allocator, 3, 80);
defer t.deinit(testing.allocator);

View File

@ -268,6 +268,30 @@ pub const Page = struct {
return slice.offset.ptr(self.memory)[0..slice.len];
}
/// Clear the graphemes for a given cell.
pub fn clearGrapheme(self: *Page, row: *Row, cell: *Cell) void {
assert(cell.grapheme);
// Get our entry in the map, which must exist
const cell_offset = getOffset(Cell, self.memory, cell);
var map = self.grapheme_map.map(self.memory);
const entry = map.getEntry(cell_offset).?;
// Free our grapheme data
const cps = entry.value_ptr.offset.ptr(self.memory)[0..entry.value_ptr.len];
self.grapheme_alloc.free(self.memory, cps);
// Remove the entry
map.removeByPtr(entry.key_ptr);
// Mark that we no longer have graphemes, also search the row
// to make sure its state is correct.
cell.grapheme = false;
const cells = row.cells.ptr(self.memory)[0..self.size.cols];
for (cells) |c| if (c.grapheme) return;
row.grapheme = false;
}
pub const Layout = struct {
total_size: usize,
rows_start: usize,
@ -577,6 +601,11 @@ test "Page appendGrapheme small" {
try testing.expect(rac.row.grapheme);
try testing.expect(rac.cell.grapheme);
try testing.expectEqualSlices(u21, &.{ 0x0A, 0x0B }, page.lookupGrapheme(rac.cell).?);
// Clear it
page.clearGrapheme(rac.row, rac.cell);
try testing.expect(!rac.row.grapheme);
try testing.expect(!rac.cell.grapheme);
}
test "Page appendGrapheme larger than chunk" {
@ -601,3 +630,26 @@ test "Page appendGrapheme larger than chunk" {
try testing.expectEqual(@as(u21, @intCast(0x0A + i)), cps[i]);
}
}
test "Page clearGrapheme not all cells" {
var page = try Page.init(.{
.cols = 10,
.rows = 10,
.styles = 8,
});
defer page.deinit();
const rac = page.getRowAndCell(0, 0);
rac.cell.codepoint = 0x09;
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
const rac2 = page.getRowAndCell(1, 0);
rac2.cell.codepoint = 0x09;
try page.appendGrapheme(rac2.row, rac2.cell, 0x0A);
// Clear it
page.clearGrapheme(rac.row, rac.cell);
try testing.expect(rac.row.grapheme);
try testing.expect(!rac.cell.grapheme);
try testing.expect(rac2.cell.grapheme);
}