terminal/new: scroll clear

This commit is contained in:
Mitchell Hashimoto
2024-02-27 19:22:32 -08:00
parent 116f6264ba
commit 1d30577506
4 changed files with 113 additions and 29 deletions

View File

@ -254,6 +254,36 @@ pub fn scroll(self: *PageList, behavior: Scroll) void {
}
}
/// Clear the screen by scrolling written contents up into the scrollback.
/// This will not update the viewport.
pub fn scrollClear(self: *PageList) !void {
// Go through the active area backwards to find the first non-empty
// row. We use this to determine how many rows to scroll up.
const non_empty: usize = non_empty: {
var page = self.pages.last.?;
var n: usize = 0;
while (true) {
const rows: [*]Row = page.data.rows.ptr(page.data.memory);
for (0..page.data.size.rows) |i| {
const rev_i = page.data.size.rows - i - 1;
const row = rows[rev_i];
const cells = row.cells.ptr(page.data.memory)[0..self.cols];
for (cells) |cell| {
if (!cell.isEmpty()) break :non_empty self.rows - n;
}
n += 1;
if (n > self.rows) break :non_empty 0;
}
page = page.prev orelse break :non_empty 0;
}
};
// Scroll
for (0..non_empty) |_| _ = try self.grow();
}
/// Grow the active area by exactly one row.
///
/// This may allocate, but also may not if our current page has more
@ -958,6 +988,39 @@ test "PageList scroll delta row forward into active" {
}
}
test "PageList scroll clear" {
const testing = std.testing;
const alloc = testing.allocator;
var s = try init(alloc, 80, 24, null);
defer s.deinit();
{
const cell = s.getCell(.{ .active = .{ .x = 0, .y = 0 } }).?;
cell.cell.* = .{
.content_tag = .codepoint,
.content = .{ .codepoint = 'A' },
};
}
{
const cell = s.getCell(.{ .active = .{ .x = 0, .y = 1 } }).?;
cell.cell.* = .{
.content_tag = .codepoint,
.content = .{ .codepoint = 'A' },
};
}
try s.scrollClear();
{
const pt = s.getCell(.{ .viewport = .{} }).?.screenPoint();
try testing.expectEqual(point.Point{ .screen = .{
.x = 0,
.y = 2,
} }, pt);
}
}
test "PageList grow fit in capacity" {
const testing = std.testing;
const alloc = testing.allocator;

View File

@ -277,6 +277,13 @@ pub fn scroll(self: *Screen, behavior: Scroll) void {
}
}
/// See PageList.scrollClear. In addition to that, we reset the cursor
/// to be on top.
pub fn scrollClear(self: *Screen) !void {
try self.pages.scrollClear();
self.cursorAbsolute(0, 0);
}
// Erase the region specified by tl and bl, inclusive. Erased cells are
// colored with the current style background color. This will erase all
// cells in the rows.

View File

@ -1614,19 +1614,20 @@ pub fn eraseDisplay(
const protected = self.screen.protected_mode == .iso or protected_req;
switch (mode) {
// .scroll_complete => {
// self.screen.scroll(.{ .clear = {} }) catch |err| {
// log.warn("scroll clear failed, doing a normal clear err={}", .{err});
// self.eraseDisplay(alloc, .complete, protected_req);
// return;
// };
//
// // Unsets pending wrap state
// self.screen.cursor.pending_wrap = false;
//
// // Clear all Kitty graphics state for this screen
.scroll_complete => {
self.screen.scrollClear() catch |err| {
log.warn("scroll clear failed, doing a normal clear err={}", .{err});
self.eraseDisplay(.complete, protected_req);
return;
};
// Unsets pending wrap state
self.screen.cursor.pending_wrap = false;
// Clear all Kitty graphics state for this screen
// TODO
// self.screen.kitty_images.delete(alloc, self, .{ .all = true });
// },
},
.complete => {
// If we're on the primary screen and our last non-empty row is
@ -7001,22 +7002,22 @@ test "Terminal: eraseDisplay protected below" {
}
}
// test "Terminal: eraseDisplay scroll complete" {
// const alloc = testing.allocator;
// var t = try init(alloc, 10, 5);
// defer t.deinit(alloc);
//
// try t.print('A');
// t.carriageReturn();
// try t.linefeed();
// t.eraseDisplay(.scroll_complete, false);
//
// {
// const str = try t.plainString(testing.allocator);
// defer testing.allocator.free(str);
// try testing.expectEqualStrings("", str);
// }
// }
test "Terminal: eraseDisplay scroll complete" {
const alloc = testing.allocator;
var t = try init(alloc, 10, 5);
defer t.deinit(alloc);
try t.print('A');
t.carriageReturn();
try t.linefeed();
t.eraseDisplay(.scroll_complete, false);
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("", str);
}
}
test "Terminal: eraseDisplay protected above" {
const alloc = testing.allocator;

View File

@ -599,6 +599,19 @@ pub const Cell = packed struct(u64) {
};
}
/// Returns true if the cell has no text or styling.
pub fn isEmpty(self: Cell) bool {
return switch (self.content_tag) {
.codepoint,
.codepoint_grapheme,
=> !self.hasText(),
.bg_color_palette,
.bg_color_rgb,
=> false,
};
}
pub fn hasGrapheme(self: Cell) bool {
return self.content_tag == .codepoint_grapheme;
}