mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 20:26:09 +03:00
terminal: moveCell handles graphemes, clears source
This commit is contained in:
@ -335,17 +335,33 @@ pub const Page = struct {
|
|||||||
const src_cells = src_row.cells.ptr(self.memory)[src_left .. src_left + len];
|
const src_cells = src_row.cells.ptr(self.memory)[src_left .. src_left + len];
|
||||||
const dst_cells = dst_row.cells.ptr(self.memory)[dst_left .. dst_left + len];
|
const dst_cells = dst_row.cells.ptr(self.memory)[dst_left .. dst_left + len];
|
||||||
|
|
||||||
// If src has no graphemes, this is very fast.
|
// Clear our destination now matter what
|
||||||
|
self.clearCells(dst_row, dst_left, dst_left + len);
|
||||||
|
|
||||||
|
// If src has no graphemes, this is very fast because we can
|
||||||
|
// just copy the cells directly because every other attribute
|
||||||
|
// is position-independent.
|
||||||
const src_grapheme = src_row.grapheme or grapheme: {
|
const src_grapheme = src_row.grapheme or grapheme: {
|
||||||
for (src_cells) |c| if (c.hasGrapheme()) break :grapheme true;
|
for (src_cells) |c| if (c.hasGrapheme()) break :grapheme true;
|
||||||
break :grapheme false;
|
break :grapheme false;
|
||||||
};
|
};
|
||||||
if (!src_grapheme) {
|
if (!src_grapheme) {
|
||||||
fastmem.copy(Cell, dst_cells, src_cells);
|
fastmem.copy(Cell, dst_cells, src_cells);
|
||||||
return;
|
} else {
|
||||||
|
// Source has graphemes, meaning we have to do a slower
|
||||||
|
// cell by cell copy.
|
||||||
|
for (src_cells, dst_cells) |*src, *dst| {
|
||||||
|
dst.* = src.*;
|
||||||
|
if (!src.hasGrapheme()) continue;
|
||||||
|
|
||||||
|
// Required for moveGrapheme assertions
|
||||||
|
dst.content_tag = .codepoint;
|
||||||
|
self.moveGrapheme(src, dst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@panic("TODO: grapheme move");
|
// Clear our source row now that the copy is complete
|
||||||
|
self.clearCells(src_row, src_left, src_left + len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the cells in the given row. This will reclaim memory used
|
/// Clear the cells in the given row. This will reclaim memory used
|
||||||
@ -453,6 +469,24 @@ pub const Page = struct {
|
|||||||
return slice.offset.ptr(self.memory)[0..slice.len];
|
return slice.offset.ptr(self.memory)[0..slice.len];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move the graphemes from one cell to another. This can't fail
|
||||||
|
/// because we avoid any allocations since we're just moving data.
|
||||||
|
pub fn moveGrapheme(self: *const Page, src: *Cell, dst: *Cell) void {
|
||||||
|
if (comptime std.debug.runtime_safety) {
|
||||||
|
assert(src.hasGrapheme());
|
||||||
|
assert(!dst.hasGrapheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
const src_offset = getOffset(Cell, self.memory, src);
|
||||||
|
const dst_offset = getOffset(Cell, self.memory, dst);
|
||||||
|
var map = self.grapheme_map.map(self.memory);
|
||||||
|
const entry = map.getEntry(src_offset).?;
|
||||||
|
const value = entry.value_ptr.*;
|
||||||
|
map.removeByPtr(entry.key_ptr);
|
||||||
|
map.putAssumeCapacity(dst_offset, value);
|
||||||
|
src.content_tag = .codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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 {
|
||||||
if (comptime std.debug.runtime_safety) assert(cell.hasGrapheme());
|
if (comptime std.debug.runtime_safety) assert(cell.hasGrapheme());
|
||||||
@ -1322,3 +1356,91 @@ test "Page cloneFrom frees dst graphemes" {
|
|||||||
}
|
}
|
||||||
try testing.expectEqual(@as(usize, 0), page2.graphemeCount());
|
try testing.expectEqual(@as(usize, 0), page2.graphemeCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Page moveCells text-only" {
|
||||||
|
var page = try Page.init(.{
|
||||||
|
.cols = 10,
|
||||||
|
.rows = 10,
|
||||||
|
.styles = 8,
|
||||||
|
});
|
||||||
|
defer page.deinit();
|
||||||
|
|
||||||
|
// Write
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 0);
|
||||||
|
rac.cell.* = .{
|
||||||
|
.content_tag = .codepoint,
|
||||||
|
.content = .{ .codepoint = @intCast(x + 1) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = page.getRow(0);
|
||||||
|
const dst = page.getRow(1);
|
||||||
|
page.moveCells(src, 0, dst, 0, page.capacity.cols);
|
||||||
|
|
||||||
|
// New rows should have text
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 1);
|
||||||
|
try testing.expectEqual(
|
||||||
|
@as(u21, @intCast(x + 1)),
|
||||||
|
rac.cell.content.codepoint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old row should be blank
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 0);
|
||||||
|
try testing.expectEqual(
|
||||||
|
@as(u21, 0),
|
||||||
|
rac.cell.content.codepoint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Page moveCells graphemes" {
|
||||||
|
var page = try Page.init(.{
|
||||||
|
.cols = 10,
|
||||||
|
.rows = 10,
|
||||||
|
.styles = 8,
|
||||||
|
});
|
||||||
|
defer page.deinit();
|
||||||
|
|
||||||
|
// Write
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 0);
|
||||||
|
rac.cell.* = .{
|
||||||
|
.content_tag = .codepoint,
|
||||||
|
.content = .{ .codepoint = @intCast(x + 1) },
|
||||||
|
};
|
||||||
|
try page.appendGrapheme(rac.row, rac.cell, 0x0A);
|
||||||
|
}
|
||||||
|
const original_count = page.graphemeCount();
|
||||||
|
|
||||||
|
const src = page.getRow(0);
|
||||||
|
const dst = page.getRow(1);
|
||||||
|
page.moveCells(src, 0, dst, 0, page.capacity.cols);
|
||||||
|
try testing.expectEqual(original_count, page.graphemeCount());
|
||||||
|
|
||||||
|
// New rows should have text
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 1);
|
||||||
|
try testing.expectEqual(
|
||||||
|
@as(u21, @intCast(x + 1)),
|
||||||
|
rac.cell.content.codepoint,
|
||||||
|
);
|
||||||
|
try testing.expectEqualSlices(
|
||||||
|
u21,
|
||||||
|
&.{0x0A},
|
||||||
|
page.lookupGrapheme(rac.cell).?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old row should be blank
|
||||||
|
for (0..page.capacity.cols) |x| {
|
||||||
|
const rac = page.getRowAndCell(x, 0);
|
||||||
|
try testing.expectEqual(
|
||||||
|
@as(u21, 0),
|
||||||
|
rac.cell.content.codepoint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user