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. /// Grow the active area by exactly one row.
/// ///
/// This may allocate, but also may not if our current page has more /// 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" { test "PageList grow fit in capacity" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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 // Erase the region specified by tl and bl, inclusive. Erased cells are
// colored with the current style background color. This will erase all // colored with the current style background color. This will erase all
// cells in the rows. // cells in the rows.

View File

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