mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal: insertLines/deleteLines handle split across pages
This commit is contained in:
@ -1270,12 +1270,24 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
var it = bot.rowIterator(.left_up, top);
|
var it = bot.rowIterator(.left_up, top);
|
||||||
while (it.next()) |p| {
|
while (it.next()) |p| {
|
||||||
const dst_p = p.down(adjusted_count).?;
|
const dst_p = p.down(adjusted_count).?;
|
||||||
assert(dst_p.page == p.page); // TODO: handle different pages
|
|
||||||
|
|
||||||
const src: *Row = p.rowAndCell().row;
|
const src: *Row = p.rowAndCell().row;
|
||||||
const dst: *Row = dst_p.rowAndCell().row;
|
const dst: *Row = dst_p.rowAndCell().row;
|
||||||
|
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
|
// If the pages are not the same, we need to do a slow copy.
|
||||||
|
if (dst_p.page != p.page) {
|
||||||
|
dst_p.page.data.cloneRowFrom(
|
||||||
|
&p.page.data,
|
||||||
|
dst,
|
||||||
|
src,
|
||||||
|
) catch |err| {
|
||||||
|
std.log.warn("TODO: insertLines handle clone error err={}", .{err});
|
||||||
|
unreachable;
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Swap the src/dst cells. This ensures that our dst gets the proper
|
// Swap the src/dst cells. This ensures that our dst gets the proper
|
||||||
// shifted rows and src gets non-garbage cell data that we can clear.
|
// shifted rows and src gets non-garbage cell data that we can clear.
|
||||||
const dst_row = dst.*;
|
const dst_row = dst.*;
|
||||||
@ -1284,6 +1296,8 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(dst_p.page == p.page); // TODO: handle different pages for left/right
|
||||||
|
|
||||||
// Left/right scroll margins we have to copy cells, which is much slower...
|
// Left/right scroll margins we have to copy cells, which is much slower...
|
||||||
var page = &self.screen.cursor.page_pin.page.data;
|
var page = &self.screen.cursor.page_pin.page.data;
|
||||||
page.moveCells(
|
page.moveCells(
|
||||||
@ -1369,12 +1383,24 @@ pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
|||||||
var it = top.rowIterator(.right_down, bot);
|
var it = top.rowIterator(.right_down, bot);
|
||||||
while (it.next()) |p| {
|
while (it.next()) |p| {
|
||||||
const src_p = p.down(count).?;
|
const src_p = p.down(count).?;
|
||||||
assert(src_p.page == p.page); // TODO: handle different pages
|
|
||||||
|
|
||||||
const src: *Row = src_p.rowAndCell().row;
|
const src: *Row = src_p.rowAndCell().row;
|
||||||
const dst: *Row = p.rowAndCell().row;
|
const dst: *Row = p.rowAndCell().row;
|
||||||
|
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
|
// If the pages are not the same, we need to do a slow copy.
|
||||||
|
if (src_p.page != p.page) {
|
||||||
|
p.page.data.cloneRowFrom(
|
||||||
|
&src_p.page.data,
|
||||||
|
dst,
|
||||||
|
src,
|
||||||
|
) catch |err| {
|
||||||
|
std.log.warn("TODO: deleteLines handle clone error err={}", .{err});
|
||||||
|
unreachable;
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Swap the src/dst cells. This ensures that our dst gets the proper
|
// Swap the src/dst cells. This ensures that our dst gets the proper
|
||||||
// shifted rows and src gets non-garbage cell data that we can clear.
|
// shifted rows and src gets non-garbage cell data that we can clear.
|
||||||
const dst_row = dst.*;
|
const dst_row = dst.*;
|
||||||
@ -1383,6 +1409,8 @@ pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(src_p.page == p.page); // TODO: handle different pages for left/right
|
||||||
|
|
||||||
// Left/right scroll margins we have to copy cells, which is much slower...
|
// Left/right scroll margins we have to copy cells, which is much slower...
|
||||||
var page = &self.screen.cursor.page_pin.page.data;
|
var page = &self.screen.cursor.page_pin.page.data;
|
||||||
page.moveCells(
|
page.moveCells(
|
||||||
|
@ -240,37 +240,49 @@ pub const Page = struct {
|
|||||||
|
|
||||||
const other_rows = other.rows.ptr(other.memory)[y_start..y_end];
|
const other_rows = other.rows.ptr(other.memory)[y_start..y_end];
|
||||||
const rows = self.rows.ptr(self.memory)[0 .. y_end - y_start];
|
const rows = self.rows.ptr(self.memory)[0 .. y_end - y_start];
|
||||||
for (rows, other_rows) |*dst_row, *src_row| {
|
for (rows, other_rows) |*dst_row, *src_row| try self.cloneRowFrom(
|
||||||
// Copy all the row metadata but keep our cells offset
|
other,
|
||||||
const cells_offset = dst_row.cells;
|
dst_row,
|
||||||
dst_row.* = src_row.*;
|
src_row,
|
||||||
dst_row.cells = cells_offset;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const cell_len = @min(self.size.cols, other.size.cols);
|
/// Clone a single row from another page into this page.
|
||||||
const other_cells = src_row.cells.ptr(other.memory)[0..cell_len];
|
pub fn cloneRowFrom(
|
||||||
const cells = dst_row.cells.ptr(self.memory)[0..cell_len];
|
self: *Page,
|
||||||
|
other: *const Page,
|
||||||
|
dst_row: *Row,
|
||||||
|
src_row: *const Row,
|
||||||
|
) !void {
|
||||||
|
// Copy all the row metadata but keep our cells offset
|
||||||
|
const cells_offset = dst_row.cells;
|
||||||
|
dst_row.* = src_row.*;
|
||||||
|
dst_row.cells = cells_offset;
|
||||||
|
|
||||||
// If we have no managed memory in the row, we can just copy.
|
const cell_len = @min(self.size.cols, other.size.cols);
|
||||||
if (!dst_row.grapheme and !dst_row.styled) {
|
const other_cells = src_row.cells.ptr(other.memory)[0..cell_len];
|
||||||
fastmem.copy(Cell, cells, other_cells);
|
const cells = dst_row.cells.ptr(self.memory)[0..cell_len];
|
||||||
continue;
|
|
||||||
|
// If we have no managed memory in the row, we can just copy.
|
||||||
|
if (!dst_row.grapheme and !dst_row.styled) {
|
||||||
|
fastmem.copy(Cell, cells, other_cells);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have managed memory, so we have to do a slower copy to
|
||||||
|
// get all of that right.
|
||||||
|
for (cells, other_cells) |*dst_cell, *src_cell| {
|
||||||
|
dst_cell.* = src_cell.*;
|
||||||
|
if (src_cell.hasGrapheme()) {
|
||||||
|
dst_cell.content_tag = .codepoint; // required for appendGrapheme
|
||||||
|
const cps = other.lookupGrapheme(src_cell).?;
|
||||||
|
for (cps) |cp| try self.appendGrapheme(dst_row, dst_cell, cp);
|
||||||
}
|
}
|
||||||
|
if (src_cell.style_id != style.default_id) {
|
||||||
// We have managed memory, so we have to do a slower copy to
|
const other_style = other.styles.lookupId(other.memory, src_cell.style_id).?.*;
|
||||||
// get all of that right.
|
const md = try self.styles.upsert(self.memory, other_style);
|
||||||
for (cells, other_cells) |*dst_cell, *src_cell| {
|
md.ref += 1;
|
||||||
dst_cell.* = src_cell.*;
|
dst_cell.style_id = md.id;
|
||||||
if (src_cell.hasGrapheme()) {
|
|
||||||
dst_cell.content_tag = .codepoint; // required for appendGrapheme
|
|
||||||
const cps = other.lookupGrapheme(src_cell).?;
|
|
||||||
for (cps) |cp| try self.appendGrapheme(dst_row, dst_cell, cp);
|
|
||||||
}
|
|
||||||
if (src_cell.style_id != style.default_id) {
|
|
||||||
const other_style = other.styles.lookupId(other.memory, src_cell.style_id).?.*;
|
|
||||||
const md = try self.styles.upsert(self.memory, other_style);
|
|
||||||
md.ref += 1;
|
|
||||||
dst_cell.style_id = md.id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,21 +614,9 @@ pub const Capacity = struct {
|
|||||||
// for rows & cells (which will allow us to calculate the number of
|
// for rows & cells (which will allow us to calculate the number of
|
||||||
// rows we can fit at a certain column width) we need to layout the
|
// rows we can fit at a certain column width) we need to layout the
|
||||||
// "meta" members of the page (i.e. everything else) from the end.
|
// "meta" members of the page (i.e. everything else) from the end.
|
||||||
const grapheme_map_start = alignBackward(
|
const grapheme_map_start = alignBackward(usize, layout.total_size - layout.grapheme_map_layout.total_size, GraphemeMap.base_align);
|
||||||
usize,
|
const grapheme_alloc_start = alignBackward(usize, grapheme_map_start - layout.grapheme_alloc_layout.total_size, GraphemeAlloc.base_align);
|
||||||
layout.total_size - layout.grapheme_map_layout.total_size,
|
const styles_start = alignBackward(usize, grapheme_alloc_start - layout.styles_layout.total_size, style.Set.base_align);
|
||||||
GraphemeMap.base_align
|
|
||||||
);
|
|
||||||
const grapheme_alloc_start = alignBackward(
|
|
||||||
usize,
|
|
||||||
grapheme_map_start - layout.grapheme_alloc_layout.total_size,
|
|
||||||
GraphemeAlloc.base_align
|
|
||||||
);
|
|
||||||
const styles_start = alignBackward(
|
|
||||||
usize,
|
|
||||||
grapheme_alloc_start - layout.styles_layout.total_size,
|
|
||||||
style.Set.base_align
|
|
||||||
);
|
|
||||||
|
|
||||||
const available_size = styles_start;
|
const available_size = styles_start;
|
||||||
const size_per_row = @sizeOf(Row) + (@sizeOf(Cell) * @as(usize, @intCast(cols)));
|
const size_per_row = @sizeOf(Row) + (@sizeOf(Cell) * @as(usize, @intCast(cols)));
|
||||||
@ -914,7 +914,7 @@ test "Page capacity adjust cols sweep" {
|
|||||||
var cap = std_capacity;
|
var cap = std_capacity;
|
||||||
const original_cols = cap.cols;
|
const original_cols = cap.cols;
|
||||||
const original_size = Page.layout(cap).total_size;
|
const original_size = Page.layout(cap).total_size;
|
||||||
for (1..original_cols*2) |c| {
|
for (1..original_cols * 2) |c| {
|
||||||
cap = try cap.adjust(.{ .cols = @as(u16, @intCast(c)) });
|
cap = try cap.adjust(.{ .cols = @as(u16, @intCast(c)) });
|
||||||
const adjusted_size = Page.layout(cap).total_size;
|
const adjusted_size = Page.layout(cap).total_size;
|
||||||
try testing.expectEqual(original_size, adjusted_size);
|
try testing.expectEqual(original_size, adjusted_size);
|
||||||
|
Reference in New Issue
Block a user