mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-20 10:46:07 +03:00
terminal: handle clonePartialRowFrom errors in insert/deleteLines
Handle `clonePartialRowFrom` errors in `insertLines` and `deleteLines` by adjusting page capacity. To do this, I've rewritten both functions with a new way of iterating rows by moving a tracked pin up/down. Benchmarks seem to indicate that this has no effect on performance.
This commit is contained in:
@ -1404,6 +1404,11 @@ fn rowWillBeShifted(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(qwerasd): `insertLines` and `deleteLines` are 99% identical,
|
||||||
|
// the majority of their logic can (and should) be abstracted in to
|
||||||
|
// a single shared helper function, probably on `Screen` not here.
|
||||||
|
// I'm just too lazy to do that rn :p
|
||||||
|
|
||||||
/// Insert amount lines at the current cursor row. The contents of the line
|
/// Insert amount lines at the current cursor row. The contents of the line
|
||||||
/// at the current cursor row and below (to the bottom-most line in the
|
/// at the current cursor row and below (to the bottom-most line in the
|
||||||
/// scrolling region) are shifted down by amount lines. The contents of the
|
/// scrolling region) are shifted down by amount lines. The contents of the
|
||||||
@ -1435,6 +1440,18 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
// Scrolling dirties the images because it updates their placements pins.
|
// Scrolling dirties the images because it updates their placements pins.
|
||||||
self.screen.kitty_images.dirty = true;
|
self.screen.kitty_images.dirty = true;
|
||||||
|
|
||||||
|
// At the end we need to return the cursor to the row it started on.
|
||||||
|
const start_y = self.screen.cursor.y;
|
||||||
|
defer {
|
||||||
|
self.screen.cursorAbsolute(self.scrolling_region.left, start_y);
|
||||||
|
// Always unset pending wrap
|
||||||
|
self.screen.cursor.pending_wrap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a slower path if we have left or right scroll margins.
|
||||||
|
const left_right = self.scrolling_region.left > 0 or
|
||||||
|
self.scrolling_region.right < self.cols - 1;
|
||||||
|
|
||||||
// Remaining rows from our cursor to the bottom of the scroll region.
|
// Remaining rows from our cursor to the bottom of the scroll region.
|
||||||
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
||||||
|
|
||||||
@ -1442,121 +1459,135 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
// region. So we take whichever is smaller.
|
// region. So we take whichever is smaller.
|
||||||
const adjusted_count = @min(count, rem);
|
const adjusted_count = @min(count, rem);
|
||||||
|
|
||||||
// top is just the cursor position. insertLines starts at the cursor
|
// Create a new tracked pin which we'll use to navigate the page list
|
||||||
// so this is our top. We want to shift lines down, down to the bottom
|
// so that if we need to adjust capacity it will be properly tracked.
|
||||||
// of the scroll region.
|
var cur_p = self.screen.pages.trackPin(
|
||||||
const top = self.screen.cursor.page_pin.*;
|
self.screen.cursor.page_pin.down(rem - 1).?,
|
||||||
|
) catch |err| {
|
||||||
|
std.log.err("TODO: insertLines handle trackPin error err={}", .{err});
|
||||||
|
@panic("TODO: insertLines handle trackPin error");
|
||||||
|
};
|
||||||
|
defer self.screen.pages.untrackPin(cur_p);
|
||||||
|
|
||||||
// This is the amount of space at the bottom of the scroll region
|
// Our current y position relative to the cursor
|
||||||
// that will NOT be blank, so we need to shift the correct lines down.
|
var y: usize = rem;
|
||||||
// "scroll_amount" is the number of such lines.
|
|
||||||
const scroll_amount = rem - adjusted_count;
|
|
||||||
if (scroll_amount > 0) {
|
|
||||||
// If we have left/right scroll margins we have a slower path.
|
|
||||||
const left_right = self.scrolling_region.left > 0 or
|
|
||||||
self.scrolling_region.right < self.cols - 1;
|
|
||||||
|
|
||||||
const bot = top.down(scroll_amount - 1).?;
|
// Traverse from the bottom up
|
||||||
var it = bot.rowIterator(.left_up, top);
|
while (y > 0) {
|
||||||
while (it.next()) |p| {
|
const cur_rac = cur_p.rowAndCell();
|
||||||
const dst_p = p.down(adjusted_count).?;
|
const cur_row: *Row = cur_rac.row;
|
||||||
const src_rac = p.rowAndCell();
|
|
||||||
const dst_rac = dst_p.rowAndCell();
|
|
||||||
const src: *Row = src_rac.row;
|
|
||||||
const dst: *Row = dst_rac.row;
|
|
||||||
|
|
||||||
self.rowWillBeShifted(&p.page.data, src);
|
// Mark the row as dirty
|
||||||
self.rowWillBeShifted(&dst_p.page.data, dst);
|
cur_p.markDirty();
|
||||||
|
|
||||||
// Mark both our src/dst as dirty
|
// If this is one of the lines we need to shift, do so
|
||||||
p.markDirty();
|
if (y > adjusted_count) {
|
||||||
dst_p.markDirty();
|
const off_p = cur_p.up(adjusted_count).?;
|
||||||
|
const off_rac = off_p.rowAndCell();
|
||||||
|
const off_row: *Row = off_rac.row;
|
||||||
|
|
||||||
|
self.rowWillBeShifted(&cur_p.page.data, cur_row);
|
||||||
|
self.rowWillBeShifted(&off_p.page.data, off_row);
|
||||||
|
|
||||||
// If our scrolling region is full width, then we unset wrap.
|
// If our scrolling region is full width, then we unset wrap.
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
dst.wrap = false;
|
off_row.wrap = false;
|
||||||
src.wrap = false;
|
cur_row.wrap = false;
|
||||||
dst.wrap_continuation = false;
|
off_row.wrap_continuation = false;
|
||||||
src.wrap_continuation = false;
|
cur_row.wrap_continuation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const src_p = off_p;
|
||||||
|
const src_row = off_row;
|
||||||
|
const dst_p = cur_p;
|
||||||
|
const dst_row = cur_row;
|
||||||
|
|
||||||
// If our page doesn't match, then we need to do a copy from
|
// If our page doesn't match, then we need to do a copy from
|
||||||
// one page to another. This is the slow path.
|
// one page to another. This is the slow path.
|
||||||
if (dst_p.page != p.page) {
|
if (src_p.page != dst_p.page) {
|
||||||
dst_p.page.data.clonePartialRowFrom(
|
dst_p.page.data.clonePartialRowFrom(
|
||||||
&p.page.data,
|
&src_p.page.data,
|
||||||
dst,
|
dst_row,
|
||||||
src,
|
src_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
self.scrolling_region.right + 1,
|
self.scrolling_region.right + 1,
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
std.log.warn("TODO: insertLines handle clone error err={}", .{err});
|
const cap = dst_p.page.data.capacity;
|
||||||
@panic("TODO");
|
// Adjust our page capacity to make
|
||||||
|
// room for we didn't have space for
|
||||||
|
_ = self.screen.adjustCapacity(
|
||||||
|
dst_p.page,
|
||||||
|
switch (err) {
|
||||||
|
// Rehash the sets
|
||||||
|
error.StyleSetNeedsRehash,
|
||||||
|
error.HyperlinkSetNeedsRehash,
|
||||||
|
=> .{},
|
||||||
|
|
||||||
|
// Increase style memory
|
||||||
|
error.StyleSetOutOfMemory,
|
||||||
|
=> .{.styles = cap.styles * 2 },
|
||||||
|
|
||||||
|
// Increase string memory
|
||||||
|
error.StringAllocOutOfMemory,
|
||||||
|
=> .{ .string_bytes = cap.string_bytes * 2 },
|
||||||
|
|
||||||
|
// Increase hyperlink memory
|
||||||
|
error.HyperlinkSetOutOfMemory,
|
||||||
|
error.HyperlinkMapOutOfMemory,
|
||||||
|
=> .{ .hyperlink_bytes = cap.hyperlink_bytes * 2 },
|
||||||
|
|
||||||
|
// Increase grapheme memory
|
||||||
|
error.GraphemeMapOutOfMemory,
|
||||||
|
error.GraphemeAllocOutOfMemory,
|
||||||
|
=> .{ .grapheme_bytes = cap.grapheme_bytes * 2 },
|
||||||
|
},
|
||||||
|
) catch |e| {
|
||||||
|
std.log.err("TODO: insertLines handle adjustCapacity error err={}", .{e});
|
||||||
|
@panic("TODO: insertLines handle adjustCapacity error");
|
||||||
};
|
};
|
||||||
|
// Continue the loop to try handling this row again.
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
|
} else {
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
// 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
|
||||||
// shifted rows and src gets non-garbage cell data that we can clear.
|
// proper shifted rows and src gets non-garbage cell data that
|
||||||
const dst_row = dst.*;
|
// we can clear.
|
||||||
dst.* = src.*;
|
const dst = dst_row.*;
|
||||||
src.* = dst_row;
|
dst_row.* = src_row.*;
|
||||||
|
src_row.* = dst;
|
||||||
|
|
||||||
// Ensure what we did didn't corrupt the page
|
// Ensure what we did didn't corrupt the page
|
||||||
p.page.data.assertIntegrity();
|
cur_p.page.data.assertIntegrity();
|
||||||
continue;
|
} else {
|
||||||
}
|
// 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...
|
const page = &cur_p.page.data;
|
||||||
const page = &p.page.data;
|
|
||||||
page.moveCells(
|
page.moveCells(
|
||||||
src,
|
src_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
dst,
|
dst_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
(self.scrolling_region.right - self.scrolling_region.left) + 1,
|
(self.scrolling_region.right - self.scrolling_region.left) + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The operations above can prune our cursor style so we need to
|
|
||||||
// update. This should never fail because the above can only FREE
|
|
||||||
// memory.
|
|
||||||
self.screen.manualStyleUpdate() catch |err| {
|
|
||||||
std.log.warn("deleteLines manualStyleUpdate err={}", .{err});
|
|
||||||
self.screen.cursor.style = .{};
|
|
||||||
self.screen.manualStyleUpdate() catch unreachable;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Inserted lines should keep our bg color
|
// Clear the cells for this row, it has been shifted.
|
||||||
const bot = top.down(adjusted_count - 1).?;
|
const page = &cur_p.page.data;
|
||||||
var it = top.rowIterator(.right_down, bot);
|
const cells = page.getCells(cur_row);
|
||||||
while (it.next()) |p| {
|
self.screen.clearCells(
|
||||||
const row: *Row = p.rowAndCell().row;
|
page,
|
||||||
|
cur_row,
|
||||||
// This row is now dirty
|
cells[self.scrolling_region.left .. self.scrolling_region.right + 1],
|
||||||
p.markDirty();
|
|
||||||
|
|
||||||
// Clear the src row.
|
|
||||||
const page = &p.page.data;
|
|
||||||
const cells = page.getCells(row);
|
|
||||||
const cells_write = cells[self.scrolling_region.left .. self.scrolling_region.right + 1];
|
|
||||||
self.screen.clearCells(page, row, cells_write);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the cursor to the left margin. But importantly this also
|
|
||||||
// forces screen.cursor.page_cell to reload because the rows above
|
|
||||||
// shifted cell ofsets so this will ensure the cursor is pointing
|
|
||||||
// to the correct cell.
|
|
||||||
self.screen.cursorAbsolute(
|
|
||||||
self.scrolling_region.left,
|
|
||||||
self.screen.cursor.y,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Always unset pending wrap
|
// We have successfully processed a line.
|
||||||
self.screen.cursor.pending_wrap = false;
|
y -= 1;
|
||||||
|
// Move our pin up to the next row.
|
||||||
|
if (cur_p.up(1)) |p| cur_p.* = p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes amount lines from the current cursor row down. The remaining lines
|
/// Removes amount lines from the current cursor row down. The remaining lines
|
||||||
@ -1575,9 +1606,9 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
/// cleared space is colored according to the current SGR state.
|
/// cleared space is colored according to the current SGR state.
|
||||||
///
|
///
|
||||||
/// Moves the cursor to the left margin.
|
/// Moves the cursor to the left margin.
|
||||||
pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
pub fn deleteLines(self: *Terminal, count: usize) void {
|
||||||
// Rare, but happens
|
// Rare, but happens
|
||||||
if (count_req == 0) return;
|
if (count == 0) return;
|
||||||
|
|
||||||
// If the cursor is outside the scroll region we do nothing.
|
// If the cursor is outside the scroll region we do nothing.
|
||||||
if (self.screen.cursor.y < self.scrolling_region.top or
|
if (self.screen.cursor.y < self.scrolling_region.top or
|
||||||
@ -1588,125 +1619,154 @@ pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
|||||||
// Scrolling dirties the images because it updates their placements pins.
|
// Scrolling dirties the images because it updates their placements pins.
|
||||||
self.screen.kitty_images.dirty = true;
|
self.screen.kitty_images.dirty = true;
|
||||||
|
|
||||||
// top is just the cursor position. insertLines starts at the cursor
|
// At the end we need to return the cursor to the row it started on.
|
||||||
// so this is our top. We want to shift lines down, down to the bottom
|
const start_y = self.screen.cursor.y;
|
||||||
// of the scroll region.
|
defer {
|
||||||
const top = self.screen.cursor.page_pin.*;
|
self.screen.cursorAbsolute(self.scrolling_region.left, start_y);
|
||||||
|
// Always unset pending wrap
|
||||||
|
self.screen.cursor.pending_wrap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a slower path if we have left or right scroll margins.
|
||||||
|
const left_right = self.scrolling_region.left > 0 or
|
||||||
|
self.scrolling_region.right < self.cols - 1;
|
||||||
|
|
||||||
// Remaining rows from our cursor to the bottom of the scroll region.
|
// Remaining rows from our cursor to the bottom of the scroll region.
|
||||||
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
const rem = self.scrolling_region.bottom - self.screen.cursor.y + 1;
|
||||||
|
|
||||||
// The maximum we can delete is the remaining lines in the scroll region.
|
// We can only insert lines up to our remaining lines in the scroll
|
||||||
const count = @min(count_req, rem);
|
// region. So we take whichever is smaller.
|
||||||
|
const adjusted_count = @min(count, rem);
|
||||||
|
|
||||||
// This is the amount of space at the bottom of the scroll region
|
// Create a new tracked pin which we'll use to navigate the page list
|
||||||
// that will NOT be blank, so we need to shift the correct lines down.
|
// so that if we need to adjust capacity it will be properly tracked.
|
||||||
// "scroll_amount" is the number of such lines.
|
var cur_p = self.screen.pages.trackPin(
|
||||||
const scroll_amount = rem - count;
|
self.screen.cursor.page_pin.*,
|
||||||
if (scroll_amount > 0) {
|
) catch |err| {
|
||||||
// If we have left/right scroll margins we have a slower path.
|
std.log.err("TODO: deleteLines handle trackPin error err={}", .{err});
|
||||||
const left_right = self.scrolling_region.left > 0 or
|
@panic("TODO: deleteLines handle trackPin error");
|
||||||
self.scrolling_region.right < self.cols - 1;
|
};
|
||||||
|
defer self.screen.pages.untrackPin(cur_p);
|
||||||
|
|
||||||
const bot = top.down(scroll_amount - 1).?;
|
// Our current y position relative to the cursor
|
||||||
var it = top.rowIterator(.right_down, bot);
|
var y: usize = 0;
|
||||||
while (it.next()) |p| {
|
|
||||||
const src_p = p.down(count).?;
|
|
||||||
const src_rac = src_p.rowAndCell();
|
|
||||||
const dst_rac = p.rowAndCell();
|
|
||||||
const src: *Row = src_rac.row;
|
|
||||||
const dst: *Row = dst_rac.row;
|
|
||||||
|
|
||||||
// Mark both our src/dst as dirty
|
// Traverse from the top down
|
||||||
p.markDirty();
|
while (y < rem) {
|
||||||
src_p.markDirty();
|
const cur_rac = cur_p.rowAndCell();
|
||||||
|
const cur_row: *Row = cur_rac.row;
|
||||||
|
|
||||||
self.rowWillBeShifted(&src_p.page.data, src);
|
// Mark the row as dirty
|
||||||
self.rowWillBeShifted(&p.page.data, dst);
|
cur_p.markDirty();
|
||||||
|
|
||||||
|
// If this is one of the lines we need to shift, do so
|
||||||
|
if (y < rem - adjusted_count) {
|
||||||
|
const off_p = cur_p.down(adjusted_count).?;
|
||||||
|
const off_rac = off_p.rowAndCell();
|
||||||
|
const off_row: *Row = off_rac.row;
|
||||||
|
|
||||||
|
self.rowWillBeShifted(&cur_p.page.data, cur_row);
|
||||||
|
self.rowWillBeShifted(&off_p.page.data, off_row);
|
||||||
|
|
||||||
// If our scrolling region is full width, then we unset wrap.
|
// If our scrolling region is full width, then we unset wrap.
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
dst.wrap = false;
|
off_row.wrap = false;
|
||||||
src.wrap = false;
|
cur_row.wrap = false;
|
||||||
dst.wrap_continuation = false;
|
off_row.wrap_continuation = false;
|
||||||
src.wrap_continuation = false;
|
cur_row.wrap_continuation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_p.page != p.page) {
|
const src_p = off_p;
|
||||||
p.page.data.clonePartialRowFrom(
|
const src_row = off_row;
|
||||||
|
const dst_p = cur_p;
|
||||||
|
const dst_row = cur_row;
|
||||||
|
|
||||||
|
// If our page doesn't match, then we need to do a copy from
|
||||||
|
// one page to another. This is the slow path.
|
||||||
|
if (src_p.page != dst_p.page) {
|
||||||
|
dst_p.page.data.clonePartialRowFrom(
|
||||||
&src_p.page.data,
|
&src_p.page.data,
|
||||||
dst,
|
dst_row,
|
||||||
src,
|
src_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
self.scrolling_region.right + 1,
|
self.scrolling_region.right + 1,
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
std.log.warn("TODO: deleteLines handle clone error err={}", .{err});
|
const cap = dst_p.page.data.capacity;
|
||||||
@panic("TODO");
|
// Adjust our page capacity to make
|
||||||
|
// room for we didn't have space for
|
||||||
|
_ = self.screen.adjustCapacity(
|
||||||
|
dst_p.page,
|
||||||
|
switch (err) {
|
||||||
|
// Rehash the sets
|
||||||
|
error.StyleSetNeedsRehash,
|
||||||
|
error.HyperlinkSetNeedsRehash,
|
||||||
|
=> .{},
|
||||||
|
|
||||||
|
// Increase style memory
|
||||||
|
error.StyleSetOutOfMemory,
|
||||||
|
=> .{.styles = cap.styles * 2 },
|
||||||
|
|
||||||
|
// Increase string memory
|
||||||
|
error.StringAllocOutOfMemory,
|
||||||
|
=> .{ .string_bytes = cap.string_bytes * 2 },
|
||||||
|
|
||||||
|
// Increase hyperlink memory
|
||||||
|
error.HyperlinkSetOutOfMemory,
|
||||||
|
error.HyperlinkMapOutOfMemory,
|
||||||
|
=> .{ .hyperlink_bytes = cap.hyperlink_bytes * 2 },
|
||||||
|
|
||||||
|
// Increase grapheme memory
|
||||||
|
error.GraphemeMapOutOfMemory,
|
||||||
|
error.GraphemeAllocOutOfMemory,
|
||||||
|
=> .{ .grapheme_bytes = cap.grapheme_bytes * 2 },
|
||||||
|
},
|
||||||
|
) catch |e| {
|
||||||
|
std.log.err("TODO: deleteLines handle adjustCapacity error err={}", .{e});
|
||||||
|
@panic("TODO: deleteLines handle adjustCapacity error");
|
||||||
};
|
};
|
||||||
|
// Continue the loop to try handling this row again.
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
|
} else {
|
||||||
if (!left_right) {
|
if (!left_right) {
|
||||||
// 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
|
||||||
// shifted rows and src gets non-garbage cell data that we can clear.
|
// proper shifted rows and src gets non-garbage cell data that
|
||||||
const dst_row = dst.*;
|
// we can clear.
|
||||||
dst.* = src.*;
|
const dst = dst_row.*;
|
||||||
src.* = dst_row;
|
dst_row.* = src_row.*;
|
||||||
|
src_row.* = dst;
|
||||||
|
|
||||||
// Ensure what we did didn't corrupt the page
|
// Ensure what we did didn't corrupt the page
|
||||||
p.page.data.assertIntegrity();
|
cur_p.page.data.assertIntegrity();
|
||||||
continue;
|
} else {
|
||||||
}
|
// 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...
|
const page = &cur_p.page.data;
|
||||||
const page = &p.page.data;
|
|
||||||
page.moveCells(
|
page.moveCells(
|
||||||
src,
|
src_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
dst,
|
dst_row,
|
||||||
self.scrolling_region.left,
|
self.scrolling_region.left,
|
||||||
(self.scrolling_region.right - self.scrolling_region.left) + 1,
|
(self.scrolling_region.right - self.scrolling_region.left) + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The operations above can prune our cursor style so we need to
|
|
||||||
// update. This should never fail because the above can only FREE
|
|
||||||
// memory.
|
|
||||||
self.screen.manualStyleUpdate() catch |err| {
|
|
||||||
std.log.warn("deleteLines manualStyleUpdate err={}", .{err});
|
|
||||||
self.screen.cursor.style = .{};
|
|
||||||
self.screen.manualStyleUpdate() catch unreachable;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
const clear_top = top.down(scroll_amount).?;
|
// Clear the cells for this row, it's from out of bounds.
|
||||||
const bot = top.down(rem - 1).?;
|
const page = &cur_p.page.data;
|
||||||
var it = clear_top.rowIterator(.right_down, bot);
|
const cells = page.getCells(cur_row);
|
||||||
while (it.next()) |p| {
|
self.screen.clearCells(
|
||||||
const row: *Row = p.rowAndCell().row;
|
page,
|
||||||
|
cur_row,
|
||||||
// This row is now dirty
|
cells[self.scrolling_region.left .. self.scrolling_region.right + 1],
|
||||||
p.markDirty();
|
|
||||||
|
|
||||||
// Clear the src row.
|
|
||||||
const page = &p.page.data;
|
|
||||||
const cells = page.getCells(row);
|
|
||||||
const cells_write = cells[self.scrolling_region.left .. self.scrolling_region.right + 1];
|
|
||||||
self.screen.clearCells(page, row, cells_write);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the cursor to the left margin. But importantly this also
|
|
||||||
// forces screen.cursor.page_cell to reload because the rows above
|
|
||||||
// shifted cell ofsets so this will ensure the cursor is pointing
|
|
||||||
// to the correct cell.
|
|
||||||
self.screen.cursorAbsolute(
|
|
||||||
self.scrolling_region.left,
|
|
||||||
self.screen.cursor.y,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Always unset pending wrap
|
// We have successfully processed a line.
|
||||||
self.screen.cursor.pending_wrap = false;
|
y += 1;
|
||||||
|
// Move our pin down to the next row.
|
||||||
|
if (cur_p.down(1)) |p| cur_p.* = p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts spaces at current cursor position moving existing cell contents
|
/// Inserts spaces at current cursor position moving existing cell contents
|
||||||
|
Reference in New Issue
Block a user