mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
terminal/new: pagelist resize to 1 col deletes wide chars
This commit is contained in:
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user