terminal/new: eraseDisplay history

This commit is contained in:
Mitchell Hashimoto
2024-02-27 21:21:51 -08:00
parent d21d7f0426
commit 5ad6228822
3 changed files with 136 additions and 9 deletions

View File

@ -364,7 +364,7 @@ fn createPage(self: *PageList) !*List.Node {
/// If the top or bottom point is in the middle of a page, the other
/// contents in the page will be preserved but the page itself will be
/// underutilized (size < capacity).
pub fn erase(
pub fn eraseRows(
self: *PageList,
tl_pt: point.Point,
bl_pt: ?point.Point,
@ -388,7 +388,7 @@ pub fn erase(
const rows = chunk.page.data.rows.ptr(chunk.page.data.memory);
const scroll_amount = chunk.page.data.size.rows - chunk.end;
for (0..scroll_amount) |i| {
const src: *Row = &rows[i + scroll_amount];
const src: *Row = &rows[i + chunk.end];
const dst: *Row = &rows[i];
const old_dst = dst.*;
dst.* = src.*;
@ -400,6 +400,17 @@ pub fn erase(
// be written to again (its in the past) or it will grow and the
// terminal erase will automatically erase the data.
// If our viewport is on this page and the offset is beyond
// our new end, shift it.
switch (self.viewport) {
.top, .active => {},
.exact => |*offset| exact: {
if (offset.page != chunk.page) break :exact;
offset.row_offset -|= scroll_amount;
},
}
// Our new size is the amount we scrolled
chunk.page.data.size.rows = @intCast(scroll_amount);
}
}
@ -407,6 +418,20 @@ pub fn erase(
/// Erase a single page, freeing all its resources. The page can be
/// anywhere in the linked list.
fn erasePage(self: *PageList, page: *List.Node) void {
// If our viewport is pinned to this page, then we need to update it.
switch (self.viewport) {
.top, .active => {},
.exact => |*offset| {
if (offset.page == page) {
if (page.next) |next| {
offset.page = next;
} else {
self.viewport = .{ .active = {} };
}
}
},
}
// Remove the page from the linked list
self.pages.remove(page);
@ -1276,6 +1301,28 @@ test "PageList erase" {
try testing.expect(s.totalRows() > s.rows);
// Erase the entire history, we should be back to just our active set.
s.erase(.{ .history = .{} }, null);
s.eraseRows(.{ .history = .{} }, null);
try testing.expectEqual(s.rows, s.totalRows());
}
test "PageList erase resets viewport if inside erased page" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 80, 24, null);
defer s.deinit();
// Grow so we take up at least 5 pages.
const page = &s.pages.last.?.data;
for (0..page.capacity.rows * 5) |_| {
_ = try s.grow();
}
// Move our viewport to the top
s.scroll(.{ .delta_row = -@as(isize, @intCast(s.totalRows())) });
try testing.expect(s.viewport.exact.page == s.pages.first.?);
// Erase the entire history, we should be back to just our active set.
s.eraseRows(.{ .history = .{} }, null);
try testing.expect(s.viewport.exact.page == s.pages.first.?);
}

View File

@ -299,6 +299,23 @@ pub fn scrollClear(self: *Screen) !void {
self.kitty_images.dirty = true;
}
/// Erase the region specified by tl and br, inclusive. This will physically
/// erase the rows meaning the memory will be reclaimed (if the underlying
/// page is empty) and other rows will be shifted up.
pub fn eraseRows(
self: *Screen,
tl: point.Point,
bl: ?point.Point,
) void {
// Erase the rows
self.pages.eraseRows(tl, bl);
// Just to be safe, reset our cursor since it is possible depending
// on the points that our active area shifted so our pointers are
// invalid.
//self.cursorAbsolute(self.cursor.x, self.cursor.y);
}
// Clear the region specified by tl and bl, inclusive. Cleared cells are
// colored with the current style background color. This will clear all
// cells in the rows.
@ -844,3 +861,71 @@ test "Screen clearRows active styled line" {
defer alloc.free(str);
try testing.expectEqualStrings("", str);
}
test "Terminal: eraseRows history" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try Screen.init(alloc, 5, 5, 1000);
defer s.deinit();
try s.testWriteString("1\n2\n3\n4\n5\n6");
{
const str = try s.dumpStringAlloc(alloc, .{ .active = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
{
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("1\n2\n3\n4\n5\n6", str);
}
s.eraseRows(.{ .history = .{} }, null);
{
const str = try s.dumpStringAlloc(alloc, .{ .active = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
{
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
}
test "Terminal: eraseRows history with more lines" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try Screen.init(alloc, 5, 5, 1000);
defer s.deinit();
try s.testWriteString("A\nB\nC\n1\n2\n3\n4\n5\n6");
{
const str = try s.dumpStringAlloc(alloc, .{ .active = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
{
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("A\nB\nC\n1\n2\n3\n4\n5\n6", str);
}
s.eraseRows(.{ .history = .{} }, null);
{
const str = try s.dumpStringAlloc(alloc, .{ .active = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
{
const str = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
defer alloc.free(str);
try testing.expectEqualStrings("2\n3\n4\n5\n6", str);
}
}

View File

@ -1714,13 +1714,8 @@ pub fn eraseDisplay(
// Unsets pending wrap state
assert(!self.screen.cursor.pending_wrap);
},
//
// .scrollback => self.screen.clear(.history) catch |err| {
// // This isn't a huge issue, so just log it.
// log.err("failed to clear scrollback: {}", .{err});
// },
else => @panic("TODO"),
.scrollback => self.screen.eraseRows(.{ .history = .{} }, null),
}
}