terminal/new: pagelist resize to 1 col deletes wide chars

This commit is contained in:
Mitchell Hashimoto
2024-03-04 11:33:06 -08:00
parent 93e63d5356
commit 6b90b6f2b0

View File

@ -593,10 +593,6 @@ const ReflowCursor = struct {
self.y = y; self.y = y;
} }
fn copyRowMetadata(self: *ReflowCursor, other: *const Row) void {
self.page_row.semantic_prompt = other.semantic_prompt;
}
fn countTrailingEmptyCells(self: *const ReflowCursor) usize { fn countTrailingEmptyCells(self: *const ReflowCursor) usize {
// If the row is wrapped, all empty cells are meaningful. // If the row is wrapped, all empty cells are meaningful.
if (self.page_row.wrap) return 0; if (self.page_row.wrap) return 0;
@ -614,6 +610,10 @@ const ReflowCursor = struct {
return len; return len;
} }
fn copyRowMetadata(self: *ReflowCursor, other: *const Row) void {
self.page_row.semantic_prompt = other.semantic_prompt;
}
}; };
/// Reflow the given page into the new capacity. The new capacity can have /// Reflow the given page into the new capacity. The new capacity can have
@ -728,53 +728,78 @@ fn reflowPage(
dst_cursor.copyRowMetadata(src_cursor.page_row); dst_cursor.copyRowMetadata(src_cursor.page_row);
} }
switch (src_cursor.page_cell.content_tag) { // A rare edge case. If we're resizing down to 1 column
// These are guaranteed to have no styling data and no // and the source is a non-narrow character, we reset the
// graphemes, a fast path. // cell to a narrow blank and we skip to the next cell.
.bg_color_palette, if (cap.cols == 1 and src_cursor.page_cell.wide != .narrow) {
.bg_color_rgb, switch (src_cursor.page_cell.wide) {
=> { .narrow => unreachable,
assert(!src_cursor.page_cell.hasStyling());
assert(!src_cursor.page_cell.hasGrapheme());
dst_cursor.page_cell.* = src_cursor.page_cell.*;
},
.codepoint => { // Wide char, we delete it, reset it to narrow,
dst_cursor.page_cell.* = src_cursor.page_cell.*; // and skip forward.
}, .wide => {
dst_cursor.page_cell.content.codepoint = 0;
dst_cursor.page_cell.wide = .narrow;
src_cursor.cursorForward();
continue;
},
.codepoint_grapheme => { // Skip spacer tails since we should've already
// We copy the cell like normal but we have to reset the // handled them in the previous cell.
// tag because this is used for fast-path detection in .spacer_tail => {},
// appendGrapheme.
dst_cursor.page_cell.* = src_cursor.page_cell.*;
dst_cursor.page_cell.content_tag = .codepoint;
// Copy the graphemes // TODO: test?
const src_cps = src_cursor.page.lookupGrapheme(src_cursor.page_cell).?; .spacer_head => {},
for (src_cps) |cp| { }
try dst_cursor.page.appendGrapheme( } else {
dst_cursor.page_row, switch (src_cursor.page_cell.content_tag) {
dst_cursor.page_cell, // These are guaranteed to have no styling data and no
cp, // graphemes, a fast path.
); .bg_color_palette,
} .bg_color_rgb,
}, => {
} assert(!src_cursor.page_cell.hasStyling());
assert(!src_cursor.page_cell.hasGrapheme());
dst_cursor.page_cell.* = src_cursor.page_cell.*;
},
// If the source cell has a style, we need to copy it. .codepoint => {
if (src_cursor.page_cell.style_id != stylepkg.default_id) { dst_cursor.page_cell.* = src_cursor.page_cell.*;
const src_style = src_cursor.page.styles.lookupId( },
src_cursor.page.memory,
src_cursor.page_cell.style_id,
).?.*;
const dst_md = try dst_cursor.page.styles.upsert( .codepoint_grapheme => {
dst_cursor.page.memory, // We copy the cell like normal but we have to reset the
src_style, // tag because this is used for fast-path detection in
); // appendGrapheme.
dst_md.ref += 1; dst_cursor.page_cell.* = src_cursor.page_cell.*;
dst_cursor.page_cell.style_id = dst_md.id; dst_cursor.page_cell.content_tag = .codepoint;
// Copy the graphemes
const src_cps = src_cursor.page.lookupGrapheme(src_cursor.page_cell).?;
for (src_cps) |cp| {
try dst_cursor.page.appendGrapheme(
dst_cursor.page_row,
dst_cursor.page_cell,
cp,
);
}
},
}
// If the source cell has a style, we need to copy it.
if (src_cursor.page_cell.style_id != stylepkg.default_id) {
const src_style = src_cursor.page.styles.lookupId(
src_cursor.page.memory,
src_cursor.page_cell.style_id,
).?.*;
const dst_md = try dst_cursor.page.styles.upsert(
dst_cursor.page.memory,
src_style,
);
dst_md.ref += 1;
dst_cursor.page_cell.style_id = dst_md.id;
}
} }
// If our original cursor was on this page, this x/y then // If our original cursor was on this page, this x/y then
@ -3787,3 +3812,48 @@ test "PageList resize reflow less cols copy style" {
} }
} }
} }
test "PageList resize reflow less cols to eliminate a wide char" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 2, 1, 0);
defer s.deinit();
{
try testing.expect(s.pages.first == s.pages.last);
const page = &s.pages.first.?.data;
{
const rac = page.getRowAndCell(0, 0);
rac.cell.* = .{
.content_tag = .codepoint,
.content = .{ .codepoint = '😀' },
.wide = .wide,
};
}
{
const rac = page.getRowAndCell(1, 0);
rac.cell.* = .{
.content_tag = .codepoint,
.content = .{ .codepoint = ' ' },
.wide = .spacer_tail,
};
}
}
// Resize
try s.resize(.{ .cols = 1, .reflow = true });
try testing.expectEqual(@as(usize, 1), s.cols);
try testing.expectEqual(@as(usize, 1), s.totalRows());
{
try testing.expect(s.pages.first == s.pages.last);
const page = &s.pages.first.?.data;
{
const rac = page.getRowAndCell(0, 0);
try testing.expectEqual(@as(u21, 0), rac.cell.content.codepoint);
try testing.expectEqual(pagepkg.Cell.Wide.narrow, rac.cell.wide);
}
}
}