terminal: handle case grow allocates but cursor is multiple pages back

This commit is contained in:
Mitchell Hashimoto
2024-08-18 10:20:23 -07:00
parent 602fea52ec
commit 994514981f

View File

@ -757,45 +757,8 @@ pub fn cursorScrollAbove(self: *Screen) !void {
assert(self.cursor.y < self.pages.rows - 1); assert(self.cursor.y < self.pages.rows - 1);
const old_pin = self.cursor.page_pin.*; const old_pin = self.cursor.page_pin.*;
if (try self.pages.grow()) |new_page_node| { if (try self.pages.grow()) |_| {
// We allocated a new page and went to it. In this case, our new try self.cursorScrollAboveRotate();
// empty line is at the top of this page.
// Prev is never null because pagelist asserts that we always have
// memory for at least two pages. This is an assertion.
assert(new_page_node.prev.? == old_pin.page);
const prev_page = &old_pin.page.data;
const new_page = &new_page_node.data;
const prev_rows = prev_page.rows.ptr(prev_page.memory.ptr);
const new_rows = new_page.rows.ptr(new_page.memory.ptr);
const prev_last_row = &prev_rows[prev_page.size.rows - 1];
// First, copy the last row of the previous page to the top
// of our current page.
try new_page.cloneRowFrom(
prev_page,
&new_rows[0],
prev_last_row,
);
// Update our cursor metadata now. We call methods below that assert
// integrity in debug modes so we want to put ourselves in a
// consistent state first.
self.cursor.page_pin.* = self.cursor.page_pin.down(1).?;
self.cursorChangePin(self.cursor.page_pin.*);
const page_rac = self.cursor.page_pin.rowAndCell();
self.cursor.page_row = page_rac.row;
self.cursor.page_cell = page_rac.cell;
// Third, clear the last row of the previous page.
self.clearCells(
prev_page,
prev_last_row,
prev_page.getCells(prev_last_row),
);
var dirty = prev_page.dirtyBitSet();
dirty.set(prev_page.size.rows - 1);
} else { } else {
// In this case, it means grow() didn't allocate a new page. // In this case, it means grow() didn't allocate a new page.
@ -847,7 +810,22 @@ pub fn cursorScrollAbove(self: *Screen) !void {
// 1 |5E00000000| | 4 // 1 |5E00000000| | 4
// +----------+ : // +----------+ :
// +-------------+ // +-------------+
try self.cursorScrollAboveRotate();
}
}
if (self.cursor.style_id != style.default_id) {
// The newly created line needs to be styled according to
// the bg color if it is set.
if (self.cursor.style.bgCell()) |blank_cell| {
const cell_current: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell);
const cells = cell_current - self.cursor.x;
@memset(cells[0..self.pages.cols], blank_cell);
}
}
}
fn cursorScrollAboveRotate(self: *Screen) !void {
self.cursor.page_pin.* = self.cursor.page_pin.down(1).?; self.cursor.page_pin.* = self.cursor.page_pin.down(1).?;
// Go through each of the pages following our pin, shift all rows // Go through each of the pages following our pin, shift all rows
@ -899,18 +877,6 @@ pub fn cursorScrollAbove(self: *Screen) !void {
const page_rac = self.cursor.page_pin.rowAndCell(); const page_rac = self.cursor.page_pin.rowAndCell();
self.cursor.page_row = page_rac.row; self.cursor.page_row = page_rac.row;
self.cursor.page_cell = page_rac.cell; self.cursor.page_cell = page_rac.cell;
}
}
if (self.cursor.style_id != style.default_id) {
// The newly created line needs to be styled according to
// the bg color if it is set.
if (self.cursor.style.bgCell()) |blank_cell| {
const cell_current: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell);
const cells = cell_current - self.cursor.x;
@memset(cells[0..self.pages.cols], blank_cell);
}
}
} }
/// Move the cursor down if we're not at the bottom of the screen. Otherwise /// Move the cursor down if we're not at the bottom of the screen. Otherwise
@ -4196,7 +4162,7 @@ test "Screen: scroll above creates new page" {
// Only y=1 is dirty because they are the ones that CHANGED contents // Only y=1 is dirty because they are the ones that CHANGED contents
try testing.expect(!s.pages.isDirty(.{ .active = .{ .x = 0, .y = 0 } })); try testing.expect(!s.pages.isDirty(.{ .active = .{ .x = 0, .y = 0 } }));
try testing.expect(s.pages.isDirty(.{ .active = .{ .x = 0, .y = 1 } })); try testing.expect(s.pages.isDirty(.{ .active = .{ .x = 0, .y = 1 } }));
try testing.expect(!s.pages.isDirty(.{ .active = .{ .x = 0, .y = 2 } })); try testing.expect(s.pages.isDirty(.{ .active = .{ .x = 0, .y = 2 } }));
} }
test "Screen: scroll above no scrollback bottom of page" { test "Screen: scroll above no scrollback bottom of page" {