mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal: resize with less cols preserves zero width codepoints
This commit is contained in:
@ -2349,7 +2349,8 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
|||||||
errdefer self.storage.deinit(self.alloc);
|
errdefer self.storage.deinit(self.alloc);
|
||||||
defer old.storage.deinit(self.alloc);
|
defer old.storage.deinit(self.alloc);
|
||||||
|
|
||||||
// Copy grapheme map
|
// Create empty grapheme map. Cell IDs change so we can't just copy it,
|
||||||
|
// we'll rebuild it.
|
||||||
self.graphemes = .{};
|
self.graphemes = .{};
|
||||||
errdefer self.deinitGraphemes();
|
errdefer self.deinitGraphemes();
|
||||||
defer old.deinitGraphemes();
|
defer old.deinitGraphemes();
|
||||||
@ -2388,9 +2389,12 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fast path: our old row is not wrapped AND our old row fits
|
// Fast path: our old row is not wrapped AND our old row fits
|
||||||
// into our new smaller size. In this case, we just do a fast
|
// into our new smaller size AND this row has no grapheme clusters.
|
||||||
// copy and move on.
|
// In this case, we just do a fast copy and move on.
|
||||||
if (!old_row_wrapped and trimmed_row.len <= self.cols) {
|
if (!old_row_wrapped and
|
||||||
|
trimmed_row.len <= self.cols and
|
||||||
|
!old_row.header().flags.grapheme)
|
||||||
|
{
|
||||||
// If our cursor is on this line, then set the new cursor.
|
// If our cursor is on this line, then set the new cursor.
|
||||||
if (cursor_pos.y == old_y) {
|
if (cursor_pos.y == old_y) {
|
||||||
assert(new_cursor == null);
|
assert(new_cursor == null);
|
||||||
@ -2473,6 +2477,14 @@ pub fn resize(self: *Screen, rows: usize, cols: usize) !void {
|
|||||||
// Write the cell
|
// Write the cell
|
||||||
var new_cell = row.getCellPtr(x);
|
var new_cell = row.getCellPtr(x);
|
||||||
new_cell.* = cell.cell;
|
new_cell.* = cell.cell;
|
||||||
|
|
||||||
|
// If the old cell is a multi-codepoint grapheme then we
|
||||||
|
// need to also attach the graphemes.
|
||||||
|
if (cell.cell.attrs.grapheme) {
|
||||||
|
var it = cur_old_row.codepointIterator(old_x);
|
||||||
|
while (it.next()) |cp| try row.attachGrapheme(x, cp);
|
||||||
|
}
|
||||||
|
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5625,14 +5637,16 @@ test "Screen: resize less cols with graphemes" {
|
|||||||
try testing.expectEqual(cursor, s.cursor);
|
try testing.expectEqual(cursor, s.cursor);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
const expected = "1️A️B️\n2️E️F️\n3️I️J️";
|
||||||
var contents = try s.testString(alloc, .viewport);
|
var contents = try s.testString(alloc, .viewport);
|
||||||
defer alloc.free(contents);
|
defer alloc.free(contents);
|
||||||
try testing.expectEqualStrings(str, contents);
|
try testing.expectEqualStrings(expected, contents);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
const expected = "1️A️B️\n2️E️F️\n3️I️J️";
|
||||||
var contents = try s.testString(alloc, .screen);
|
var contents = try s.testString(alloc, .screen);
|
||||||
defer alloc.free(contents);
|
defer alloc.free(contents);
|
||||||
try testing.expectEqualStrings(str, contents);
|
try testing.expectEqualStrings(expected, contents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6105,6 +6119,36 @@ test "Screen: resize more cols with wide spacer head" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Screen: resize less cols preserves grapheme cluster" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var s = try init(alloc, 1, 5, 0);
|
||||||
|
defer s.deinit();
|
||||||
|
const str: []const u8 = &.{ 0x43, 0xE2, 0x83, 0x90 }; // C⃐ (C with combining left arrow)
|
||||||
|
try s.testWriteString(str);
|
||||||
|
|
||||||
|
// We should have a single cell with all the codepoints
|
||||||
|
{
|
||||||
|
const row = s.getRow(.{ .screen = 0 });
|
||||||
|
try testing.expectEqual(@as(usize, 2), row.codepointLen(0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .screen);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings(str, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize to less columns. No wrapping, but we should still have
|
||||||
|
// the same grapheme cluster.
|
||||||
|
try s.resize(1, 4);
|
||||||
|
{
|
||||||
|
var contents = try s.testString(alloc, .screen);
|
||||||
|
defer alloc.free(contents);
|
||||||
|
try testing.expectEqualStrings(str, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Screen: resize more cols with wide spacer head multiple lines" {
|
test "Screen: resize more cols with wide spacer head multiple lines" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user