terminal/new: grapheme tests

This commit is contained in:
Mitchell Hashimoto
2024-02-26 19:55:14 -08:00
parent 23b0c1fad9
commit 7a4d2817f8
2 changed files with 91 additions and 4 deletions

View File

@ -1299,6 +1299,7 @@ pub fn insertBlanks(self: *Terminal, count: usize) void {
// If the original source (now copied to dst) had graphemes, // If the original source (now copied to dst) had graphemes,
// we have to move them since they're stored by cell offset. // we have to move them since they're stored by cell offset.
if (dst.hasGrapheme()) { if (dst.hasGrapheme()) {
assert(!src.hasGrapheme());
page.moveGraphemeWithinRow(src, dst); page.moveGraphemeWithinRow(src, dst);
} }
} }
@ -1377,7 +1378,6 @@ fn blankCells(
for (cells) |*cell| { for (cells) |*cell| {
if (cell.hasGrapheme()) page.clearGrapheme(row, cell); if (cell.hasGrapheme()) page.clearGrapheme(row, cell);
} }
assert(!row.grapheme);
} }
if (row.styled) { if (row.styled) {
@ -4988,6 +4988,74 @@ test "Terminal: insertBlanks left/right scroll region large count" {
} }
} }
test "Terminal: insertBlanks deleting graphemes" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
// Disable grapheme clustering
t.modes.set(.grapheme_cluster, true);
try t.printString("ABC");
// This is: 👨👩👧 (which may or may not render correctly)
try t.print(0x1F468);
try t.print(0x200D);
try t.print(0x1F469);
try t.print(0x200D);
try t.print(0x1F467);
// We should have one cell with graphemes
const page = t.screen.cursor.page_offset.page.data;
try testing.expectEqual(@as(usize, 1), page.graphemeCount());
t.setCursorPos(1, 1);
t.insertBlanks(4);
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings(" A", str);
}
// We should have no graphemes
try testing.expectEqual(@as(usize, 0), page.graphemeCount());
}
test "Terminal: insertBlanks shift graphemes" {
const alloc = testing.allocator;
var t = try init(alloc, 5, 5);
defer t.deinit(alloc);
// Disable grapheme clustering
t.modes.set(.grapheme_cluster, true);
try t.printString("A");
// This is: 👨👩👧 (which may or may not render correctly)
try t.print(0x1F468);
try t.print(0x200D);
try t.print(0x1F469);
try t.print(0x200D);
try t.print(0x1F467);
// We should have one cell with graphemes
const page = t.screen.cursor.page_offset.page.data;
try testing.expectEqual(@as(usize, 1), page.graphemeCount());
t.setCursorPos(1, 1);
t.insertBlanks(1);
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings(" A👨👩👧", str);
}
// We should have no graphemes
try testing.expectEqual(@as(usize, 1), page.graphemeCount());
}
test "Terminal: insert mode with space" { test "Terminal: insert mode with space" {
const alloc = testing.allocator; const alloc = testing.allocator;
var t = try init(alloc, 10, 2); var t = try init(alloc, 10, 2);

View File

@ -321,11 +321,30 @@ pub const Page = struct {
row.grapheme = false; row.grapheme = false;
} }
/// Returns the number of graphemes in the page. This isn't the byte
/// size but the total number of unique cells that have grapheme data.
pub fn graphemeCount(self: *const Page) usize {
return self.grapheme_map.map(self.memory).count();
}
/// Move graphemes to another cell in the same row. /// Move graphemes to another cell in the same row.
pub fn moveGraphemeWithinRow(self: *Page, src: *Cell, dst: *Cell) void { pub fn moveGraphemeWithinRow(self: *Page, src: *Cell, dst: *Cell) void {
_ = self; // Note: we don't assert src has graphemes here because one of
_ = src; // the places we call this is from insertBlanks where the cells have
_ = dst; // already swapped cell data but not grapheme data.
// Get our entry in the map, which must exist
const src_offset = getOffset(Cell, self.memory, src);
var map = self.grapheme_map.map(self.memory);
const entry = map.getEntry(src_offset).?;
const value = entry.value_ptr.*;
// Remove the entry so we know we have space
map.removeByPtr(entry.key_ptr);
// Add the entry for the new cell
const dst_offset = getOffset(Cell, self.memory, dst);
map.putAssumeCapacity(dst_offset, value);
} }
pub const Layout = struct { pub const Layout = struct {