mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
terminal/new: introduce content tags and bg color cells
This commit is contained in:
@ -251,7 +251,7 @@ pub fn dumpString(
|
|||||||
break :cells cells[0..self.pages.cols];
|
break :cells cells[0..self.pages.cols];
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!pagepkg.Cell.hasText(cells)) {
|
if (!pagepkg.Cell.hasTextAny(cells)) {
|
||||||
blank_rows += 1;
|
blank_rows += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -274,7 +274,7 @@ pub fn dumpString(
|
|||||||
// If we have a zero value, then we accumulate a counter. We
|
// If we have a zero value, then we accumulate a counter. We
|
||||||
// only want to turn zero values into spaces if we have a non-zero
|
// only want to turn zero values into spaces if we have a non-zero
|
||||||
// char sometime later.
|
// char sometime later.
|
||||||
if (cell.codepoint == 0) {
|
if (!cell.hasText()) {
|
||||||
blank_cells += 1;
|
blank_cells += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -283,13 +283,20 @@ pub fn dumpString(
|
|||||||
blank_cells = 0;
|
blank_cells = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.print("{u}", .{cell.codepoint});
|
switch (cell.content_tag) {
|
||||||
|
.codepoint => {
|
||||||
|
try writer.print("{u}", .{cell.content.codepoint});
|
||||||
|
},
|
||||||
|
|
||||||
if (cell.grapheme) {
|
.codepoint_grapheme => {
|
||||||
const cps = row_offset.page.data.lookupGrapheme(cell).?;
|
try writer.print("{u}", .{cell.content.codepoint});
|
||||||
for (cps) |cp| {
|
const cps = row_offset.page.data.lookupGrapheme(cell).?;
|
||||||
try writer.print("{u}", .{cp});
|
for (cps) |cp| {
|
||||||
}
|
try writer.print("{u}", .{cp});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,7 +329,8 @@ fn testWriteString(self: *Screen, text: []const u8) !void {
|
|||||||
assert(width == 1 or width == 2);
|
assert(width == 1 or width == 2);
|
||||||
switch (width) {
|
switch (width) {
|
||||||
1 => {
|
1 => {
|
||||||
self.cursor.page_cell.codepoint = c;
|
self.cursor.page_cell.content_tag = .codepoint;
|
||||||
|
self.cursor.page_cell.content = .{ .codepoint = c };
|
||||||
self.cursor.x += 1;
|
self.cursor.x += 1;
|
||||||
if (self.cursor.x < self.pages.cols) {
|
if (self.cursor.x < self.pages.cols) {
|
||||||
const cell: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell);
|
const cell: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell);
|
||||||
|
@ -245,7 +245,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// column. Otherwise, we need to check if there is text to
|
// column. Otherwise, we need to check if there is text to
|
||||||
// figure out if we're attaching to the prev or current.
|
// figure out if we're attaching to the prev or current.
|
||||||
if (self.screen.cursor.x != right_limit - 1) break :left 1;
|
if (self.screen.cursor.x != right_limit - 1) break :left 1;
|
||||||
break :left @intFromBool(self.screen.cursor.page_cell.codepoint == 0);
|
break :left @intFromBool(!self.screen.cursor.page_cell.hasText());
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the previous cell is a wide spacer tail, then we actually
|
// If the previous cell is a wide spacer tail, then we actually
|
||||||
@ -263,12 +263,12 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
|
|
||||||
// If our cell has no content, then this is a new cell and
|
// If our cell has no content, then this is a new cell and
|
||||||
// necessarily a grapheme break.
|
// necessarily a grapheme break.
|
||||||
if (prev.cell.codepoint == 0) break :grapheme;
|
if (!prev.cell.hasText()) break :grapheme;
|
||||||
|
|
||||||
const grapheme_break = brk: {
|
const grapheme_break = brk: {
|
||||||
var state: unicode.GraphemeBreakState = .{};
|
var state: unicode.GraphemeBreakState = .{};
|
||||||
var cp1: u21 = prev.cell.codepoint;
|
var cp1: u21 = prev.cell.content.codepoint;
|
||||||
if (prev.cell.grapheme) {
|
if (prev.cell.hasGrapheme()) {
|
||||||
const cps = self.screen.cursor.page_offset.page.data.lookupGrapheme(prev.cell).?;
|
const cps = self.screen.cursor.page_offset.page.data.lookupGrapheme(prev.cell).?;
|
||||||
for (cps) |cp2| {
|
for (cps) |cp2| {
|
||||||
// log.debug("cp1={x} cp2={x}", .{ cp1, cp2 });
|
// log.debug("cp1={x} cp2={x}", .{ cp1, cp2 });
|
||||||
@ -289,7 +289,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
// VS15 makes it narrow.
|
// VS15 makes it narrow.
|
||||||
if (c == 0xFE0F or c == 0xFE0E) {
|
if (c == 0xFE0F or c == 0xFE0E) {
|
||||||
// This only applies to emoji
|
// This only applies to emoji
|
||||||
const prev_props = unicode.getProperties(prev.cell.codepoint);
|
const prev_props = unicode.getProperties(prev.cell.content.codepoint);
|
||||||
const emoji = prev_props.grapheme_boundary_class == .extended_pictographic;
|
const emoji = prev_props.grapheme_boundary_class == .extended_pictographic;
|
||||||
if (!emoji) return;
|
if (!emoji) return;
|
||||||
|
|
||||||
@ -310,7 +310,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
try self.printWrap();
|
try self.printWrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.printCell(prev.cell.codepoint, .wide);
|
self.printCell(prev.cell.content.codepoint, .wide);
|
||||||
|
|
||||||
// Write our spacer
|
// Write our spacer
|
||||||
self.screen.cursorRight(1);
|
self.screen.cursorRight(1);
|
||||||
@ -385,9 +385,15 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
break :prev self.screen.cursorCellLeft(2);
|
break :prev self.screen.cursorCellLeft(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If our previous cell has no text, just ignore the zero-width character
|
||||||
|
if (!prev.hasText()) {
|
||||||
|
log.warn("zero-width character with no prior character, ignoring", .{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a emoji variation selector, prev must be an emoji
|
// If this is a emoji variation selector, prev must be an emoji
|
||||||
if (c == 0xFE0F or c == 0xFE0E) {
|
if (c == 0xFE0F or c == 0xFE0E) {
|
||||||
const prev_props = unicode.getProperties(prev.codepoint);
|
const prev_props = unicode.getProperties(prev.content.codepoint);
|
||||||
const emoji = prev_props.grapheme_boundary_class == .extended_pictographic;
|
const emoji = prev_props.grapheme_boundary_class == .extended_pictographic;
|
||||||
if (!emoji) return;
|
if (!emoji) return;
|
||||||
}
|
}
|
||||||
@ -513,7 +519,7 @@ fn printCell(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the prior value had graphemes, clear those
|
// If the prior value had graphemes, clear those
|
||||||
if (cell.grapheme) {
|
if (cell.hasGrapheme()) {
|
||||||
self.screen.cursor.page_offset.page.data.clearGrapheme(
|
self.screen.cursor.page_offset.page.data.clearGrapheme(
|
||||||
self.screen.cursor.page_row,
|
self.screen.cursor.page_row,
|
||||||
cell,
|
cell,
|
||||||
@ -522,8 +528,9 @@ fn printCell(
|
|||||||
|
|
||||||
// Write
|
// Write
|
||||||
cell.* = .{
|
cell.* = .{
|
||||||
|
.content_tag = .codepoint,
|
||||||
|
.content = .{ .codepoint = c },
|
||||||
.style_id = self.screen.cursor.style_id,
|
.style_id = self.screen.cursor.style_id,
|
||||||
.codepoint = c,
|
|
||||||
.wide = wide,
|
.wide = wide,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1055,7 +1062,7 @@ test "Terminal: print wide char" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x1F600), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x1F600), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -1077,7 +1084,7 @@ test "Terminal: print wide char in single-width terminal" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, ' '), cell.codepoint);
|
try testing.expectEqual(@as(u21, ' '), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1096,13 +1103,13 @@ test "Terminal: print over wide char at 0,0" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'A'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'A'), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1118,13 +1125,13 @@ test "Terminal: print over wide spacer tail" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'X'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'X'), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,8 +1163,8 @@ test "Terminal: print multicodepoint grapheme, disabled mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x1F468), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x1F468), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1165,16 +1172,16 @@ test "Terminal: print multicodepoint grapheme, disabled mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, ' '), cell.codepoint);
|
try testing.expectEqual(@as(u21, ' '), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
||||||
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 2, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 2, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x1F469), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x1F469), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1182,24 +1189,24 @@ test "Terminal: print multicodepoint grapheme, disabled mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 3, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 3, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, ' '), cell.codepoint);
|
try testing.expectEqual(@as(u21, ' '), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
||||||
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x1F467), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x1F467), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 5, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 5, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, ' '), cell.codepoint);
|
try testing.expectEqual(@as(u21, ' '), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
||||||
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
try testing.expect(list_cell.page.data.lookupGrapheme(cell) == null);
|
||||||
}
|
}
|
||||||
@ -1224,8 +1231,8 @@ test "Terminal: VS16 doesn't make character with 2027 disabled" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x2764), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x2764), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1249,14 +1256,14 @@ test "Terminal: print invalid VS16 non-grapheme" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'x'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'x'), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0), cell.content.codepoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1284,8 +1291,8 @@ test "Terminal: print multicodepoint grapheme, mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x1F468), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x1F468), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 4), cps.len);
|
try testing.expectEqual(@as(usize, 4), cps.len);
|
||||||
@ -1293,8 +1300,8 @@ test "Terminal: print multicodepoint grapheme, mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, ' '), cell.codepoint);
|
try testing.expectEqual(@as(u21, ' '), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
try testing.expectEqual(Cell.Wide.spacer_tail, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1318,8 +1325,8 @@ test "Terminal: VS15 to make narrow character" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x26C8), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x26C8), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1345,8 +1352,8 @@ test "Terminal: VS16 to make wide character with mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x2764), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x2764), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1374,8 +1381,8 @@ test "Terminal: VS16 repeated with mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x2764), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x2764), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1383,8 +1390,8 @@ test "Terminal: VS16 repeated with mode 2027" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 2, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 2, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0x2764), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0x2764), cell.content.codepoint);
|
||||||
try testing.expect(cell.grapheme);
|
try testing.expect(cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
try testing.expectEqual(Cell.Wide.wide, cell.wide);
|
||||||
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
const cps = list_cell.page.data.lookupGrapheme(cell).?;
|
||||||
try testing.expectEqual(@as(usize, 1), cps.len);
|
try testing.expectEqual(@as(usize, 1), cps.len);
|
||||||
@ -1411,14 +1418,14 @@ test "Terminal: print invalid VS16 grapheme" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'x'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'x'), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1444,16 +1451,16 @@ test "Terminal: print invalid VS16 with second char" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'x'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'x'), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 1, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'y'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'y'), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1479,8 +1486,8 @@ test "Terminal: overwrite grapheme should clear grapheme data" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 0, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'A'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'A'), cell.content.codepoint);
|
||||||
try testing.expect(!cell.grapheme);
|
try testing.expect(!cell.hasGrapheme());
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1560,7 +1567,7 @@ test "Terminal: disabled wraparound with wide char and one space" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 0), cell.codepoint);
|
try testing.expectEqual(@as(u21, 0), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1587,7 +1594,7 @@ test "Terminal: disabled wraparound with wide char and no space" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, 'A'), cell.codepoint);
|
try testing.expectEqual(@as(u21, 'A'), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1616,7 +1623,7 @@ test "Terminal: disabled wraparound with wide grapheme and half space" {
|
|||||||
{
|
{
|
||||||
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
const list_cell = t.screen.pages.getCell(.{ .screen = .{ .x = 4, .y = 0 } }).?;
|
||||||
const cell = list_cell.cell;
|
const cell = list_cell.cell;
|
||||||
try testing.expectEqual(@as(u21, '❤'), cell.codepoint);
|
try testing.expectEqual(@as(u21, '❤'), cell.content.codepoint);
|
||||||
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
try testing.expectEqual(Cell.Wide.narrow, cell.wide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,12 +204,14 @@ pub const Page = struct {
|
|||||||
|
|
||||||
/// Append a codepoint to the given cell as a grapheme.
|
/// Append a codepoint to the given cell as a grapheme.
|
||||||
pub fn appendGrapheme(self: *Page, row: *Row, cell: *Cell, cp: u21) !void {
|
pub fn appendGrapheme(self: *Page, row: *Row, cell: *Cell, cp: u21) !void {
|
||||||
|
if (comptime std.debug.runtime_safety) assert(cell.hasText());
|
||||||
|
|
||||||
const cell_offset = getOffset(Cell, self.memory, cell);
|
const cell_offset = getOffset(Cell, self.memory, cell);
|
||||||
var map = self.grapheme_map.map(self.memory);
|
var map = self.grapheme_map.map(self.memory);
|
||||||
|
|
||||||
// If this cell has no graphemes, we can go faster by knowing we
|
// If this cell has no graphemes, we can go faster by knowing we
|
||||||
// need to allocate a new grapheme slice and update the map.
|
// need to allocate a new grapheme slice and update the map.
|
||||||
if (!cell.grapheme) {
|
if (cell.content_tag != .codepoint_grapheme) {
|
||||||
const cps = try self.grapheme_alloc.alloc(u21, self.memory, 1);
|
const cps = try self.grapheme_alloc.alloc(u21, self.memory, 1);
|
||||||
errdefer self.grapheme_alloc.free(self.memory, cps);
|
errdefer self.grapheme_alloc.free(self.memory, cps);
|
||||||
cps[0] = cp;
|
cps[0] = cp;
|
||||||
@ -220,7 +222,7 @@ pub const Page = struct {
|
|||||||
});
|
});
|
||||||
errdefer map.remove(cell_offset);
|
errdefer map.remove(cell_offset);
|
||||||
|
|
||||||
cell.grapheme = true;
|
cell.content_tag = .codepoint_grapheme;
|
||||||
row.grapheme = true;
|
row.grapheme = true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -270,7 +272,7 @@ pub const Page = struct {
|
|||||||
|
|
||||||
/// Clear the graphemes for a given cell.
|
/// Clear the graphemes for a given cell.
|
||||||
pub fn clearGrapheme(self: *Page, row: *Row, cell: *Cell) void {
|
pub fn clearGrapheme(self: *Page, row: *Row, cell: *Cell) void {
|
||||||
assert(cell.grapheme);
|
if (comptime std.debug.runtime_safety) assert(cell.hasGrapheme());
|
||||||
|
|
||||||
// Get our entry in the map, which must exist
|
// Get our entry in the map, which must exist
|
||||||
const cell_offset = getOffset(Cell, self.memory, cell);
|
const cell_offset = getOffset(Cell, self.memory, cell);
|
||||||
@ -286,9 +288,9 @@ pub const Page = struct {
|
|||||||
|
|
||||||
// Mark that we no longer have graphemes, also search the row
|
// Mark that we no longer have graphemes, also search the row
|
||||||
// to make sure its state is correct.
|
// to make sure its state is correct.
|
||||||
cell.grapheme = false;
|
cell.content_tag = .codepoint;
|
||||||
const cells = row.cells.ptr(self.memory)[0..self.size.cols];
|
const cells = row.cells.ptr(self.memory)[0..self.size.cols];
|
||||||
for (cells) |c| if (c.grapheme) return;
|
for (cells) |c| if (c.hasGrapheme()) return;
|
||||||
row.grapheme = false;
|
row.grapheme = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,26 +446,55 @@ pub const Row = packed struct(u64) {
|
|||||||
/// The zero value of this struct must be a valid cell representing empty,
|
/// The zero value of this struct must be a valid cell representing empty,
|
||||||
/// since we zero initialize the backing memory for a page.
|
/// since we zero initialize the backing memory for a page.
|
||||||
pub const Cell = packed struct(u64) {
|
pub const Cell = packed struct(u64) {
|
||||||
/// The codepoint that this cell contains. If `grapheme` is false,
|
/// The content tag dictates the active tag in content and possibly
|
||||||
/// then this is the only codepoint in the cell. If `grapheme` is
|
/// some other behaviors.
|
||||||
/// true, then this is the first codepoint in the grapheme cluster.
|
content_tag: ContentTag = .codepoint,
|
||||||
codepoint: u21 = 0,
|
|
||||||
|
/// The content of the cell. This is a union based on content_tag.
|
||||||
|
content: packed union {
|
||||||
|
/// The codepoint that this cell contains. If `grapheme` is false,
|
||||||
|
/// then this is the only codepoint in the cell. If `grapheme` is
|
||||||
|
/// true, then this is the first codepoint in the grapheme cluster.
|
||||||
|
codepoint: u21,
|
||||||
|
|
||||||
|
/// The content is an empty cell with a background color.
|
||||||
|
color_palette: u8,
|
||||||
|
color_rgb: RGB,
|
||||||
|
} = .{ .codepoint = 0 },
|
||||||
|
|
||||||
/// The style ID to use for this cell within the style map. Zero
|
/// The style ID to use for this cell within the style map. Zero
|
||||||
/// is always the default style so no lookup is required.
|
/// is always the default style so no lookup is required.
|
||||||
style_id: style.Id = 0,
|
style_id: style.Id = 0,
|
||||||
|
|
||||||
/// This is true if there are additional codepoints in the grapheme
|
|
||||||
/// map for this cell to build a multi-codepoint grapheme.
|
|
||||||
grapheme: bool = false,
|
|
||||||
|
|
||||||
/// The wide property of this cell, for wide characters. Characters in
|
/// The wide property of this cell, for wide characters. Characters in
|
||||||
/// a terminal grid can only be 1 or 2 cells wide. A wide character
|
/// a terminal grid can only be 1 or 2 cells wide. A wide character
|
||||||
/// is always next to a spacer. This is used to determine both the width
|
/// is always next to a spacer. This is used to determine both the width
|
||||||
/// and spacer properties of a cell.
|
/// and spacer properties of a cell.
|
||||||
wide: Wide = .narrow,
|
wide: Wide = .narrow,
|
||||||
|
|
||||||
_padding: u24 = 0,
|
_padding: u20 = 0,
|
||||||
|
|
||||||
|
pub const ContentTag = enum(u2) {
|
||||||
|
/// A single codepoint, could be zero to be empty cell.
|
||||||
|
codepoint = 0,
|
||||||
|
|
||||||
|
/// A codepoint that is part of a multi-codepoint grapheme cluster.
|
||||||
|
/// The codepoint tag is active in content, but also expect more
|
||||||
|
/// codepoints in the grapheme data.
|
||||||
|
codepoint_grapheme = 1,
|
||||||
|
|
||||||
|
/// The cell has no text but only a background color. This is an
|
||||||
|
/// optimization so that cells with only backgrounds don't take up
|
||||||
|
/// style map space and also don't require a style map lookup.
|
||||||
|
bg_color_palette = 2,
|
||||||
|
bg_color_rgb = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RGB = packed struct {
|
||||||
|
r: u8,
|
||||||
|
g: u8,
|
||||||
|
b: u8,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Wide = enum(u2) {
|
pub const Wide = enum(u2) {
|
||||||
/// Not a wide character, cell width 1.
|
/// Not a wide character, cell width 1.
|
||||||
@ -480,10 +511,34 @@ pub const Cell = packed struct(u64) {
|
|||||||
spacer_head = 3,
|
spacer_head = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Helper to make a cell that just has a codepoint.
|
||||||
|
pub fn init(codepoint: u21) Cell {
|
||||||
|
return .{
|
||||||
|
.content_tag = .codepoint,
|
||||||
|
.content = .{ .codepoint = codepoint },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hasText(self: Cell) bool {
|
||||||
|
return switch (self.content_tag) {
|
||||||
|
.codepoint,
|
||||||
|
.codepoint_grapheme,
|
||||||
|
=> self.content.codepoint != 0,
|
||||||
|
|
||||||
|
.bg_color_palette,
|
||||||
|
.bg_color_rgb,
|
||||||
|
=> false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hasGrapheme(self: Cell) bool {
|
||||||
|
return self.content_tag == .codepoint_grapheme;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the set of cells has text in it.
|
/// Returns true if the set of cells has text in it.
|
||||||
pub fn hasText(cells: []const Cell) bool {
|
pub fn hasTextAny(cells: []const Cell) bool {
|
||||||
for (cells) |cell| {
|
for (cells) |cell| {
|
||||||
if (cell.codepoint != 0) return true;
|
if (cell.hasText()) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -569,13 +624,16 @@ test "Page read and write cells" {
|
|||||||
|
|
||||||
for (0..page.capacity.rows) |y| {
|
for (0..page.capacity.rows) |y| {
|
||||||
const rac = page.getRowAndCell(1, y);
|
const rac = page.getRowAndCell(1, y);
|
||||||
rac.cell.codepoint = @intCast(y);
|
rac.cell.* = .{
|
||||||
|
.content_tag = .codepoint,
|
||||||
|
.content = .{ .codepoint = @intCast(y) },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read it again
|
// Read it again
|
||||||
for (0..page.capacity.rows) |y| {
|
for (0..page.capacity.rows) |y| {
|
||||||
const rac = page.getRowAndCell(1, y);
|
const rac = page.getRowAndCell(1, y);
|
||||||
try testing.expectEqual(@as(u21, @intCast(y)), rac.cell.codepoint);
|
try testing.expectEqual(@as(u21, @intCast(y)), rac.cell.content.codepoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,24 +646,24 @@ test "Page appendGrapheme small" {
|
|||||||
defer page.deinit();
|
defer page.deinit();
|
||||||
|
|
||||||
const rac = page.getRowAndCell(0, 0);
|
const rac = page.getRowAndCell(0, 0);
|
||||||
rac.cell.codepoint = 0x09;
|
rac.cell.* = Cell.init(0x09);
|
||||||
|
|
||||||
// One
|
// One
|
||||||
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
|
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
|
||||||
try testing.expect(rac.row.grapheme);
|
try testing.expect(rac.row.grapheme);
|
||||||
try testing.expect(rac.cell.grapheme);
|
try testing.expect(rac.cell.hasGrapheme());
|
||||||
try testing.expectEqualSlices(u21, &.{0x0A}, page.lookupGrapheme(rac.cell).?);
|
try testing.expectEqualSlices(u21, &.{0x0A}, page.lookupGrapheme(rac.cell).?);
|
||||||
|
|
||||||
// Two
|
// Two
|
||||||
try page.appendGrapheme(rac.row, rac.cell, 0x0B);
|
try page.appendGrapheme(rac.row, rac.cell, 0x0B);
|
||||||
try testing.expect(rac.row.grapheme);
|
try testing.expect(rac.row.grapheme);
|
||||||
try testing.expect(rac.cell.grapheme);
|
try testing.expect(rac.cell.hasGrapheme());
|
||||||
try testing.expectEqualSlices(u21, &.{ 0x0A, 0x0B }, page.lookupGrapheme(rac.cell).?);
|
try testing.expectEqualSlices(u21, &.{ 0x0A, 0x0B }, page.lookupGrapheme(rac.cell).?);
|
||||||
|
|
||||||
// Clear it
|
// Clear it
|
||||||
page.clearGrapheme(rac.row, rac.cell);
|
page.clearGrapheme(rac.row, rac.cell);
|
||||||
try testing.expect(!rac.row.grapheme);
|
try testing.expect(!rac.row.grapheme);
|
||||||
try testing.expect(!rac.cell.grapheme);
|
try testing.expect(!rac.cell.hasGrapheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Page appendGrapheme larger than chunk" {
|
test "Page appendGrapheme larger than chunk" {
|
||||||
@ -617,7 +675,7 @@ test "Page appendGrapheme larger than chunk" {
|
|||||||
defer page.deinit();
|
defer page.deinit();
|
||||||
|
|
||||||
const rac = page.getRowAndCell(0, 0);
|
const rac = page.getRowAndCell(0, 0);
|
||||||
rac.cell.codepoint = 0x09;
|
rac.cell.* = Cell.init(0x09);
|
||||||
|
|
||||||
const count = grapheme_chunk_len * 10;
|
const count = grapheme_chunk_len * 10;
|
||||||
for (0..count) |i| {
|
for (0..count) |i| {
|
||||||
@ -640,16 +698,16 @@ test "Page clearGrapheme not all cells" {
|
|||||||
defer page.deinit();
|
defer page.deinit();
|
||||||
|
|
||||||
const rac = page.getRowAndCell(0, 0);
|
const rac = page.getRowAndCell(0, 0);
|
||||||
rac.cell.codepoint = 0x09;
|
rac.cell.* = Cell.init(0x09);
|
||||||
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
|
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
|
||||||
|
|
||||||
const rac2 = page.getRowAndCell(1, 0);
|
const rac2 = page.getRowAndCell(1, 0);
|
||||||
rac2.cell.codepoint = 0x09;
|
rac2.cell.* = Cell.init(0x09);
|
||||||
try page.appendGrapheme(rac2.row, rac2.cell, 0x0A);
|
try page.appendGrapheme(rac2.row, rac2.cell, 0x0A);
|
||||||
|
|
||||||
// Clear it
|
// Clear it
|
||||||
page.clearGrapheme(rac.row, rac.cell);
|
page.clearGrapheme(rac.row, rac.cell);
|
||||||
try testing.expect(rac.row.grapheme);
|
try testing.expect(rac.row.grapheme);
|
||||||
try testing.expect(!rac.cell.grapheme);
|
try testing.expect(!rac.cell.hasGrapheme());
|
||||||
try testing.expect(rac2.cell.grapheme);
|
try testing.expect(rac2.cell.hasGrapheme());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user