mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
terminal/new: scroll clear
This commit is contained in:
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
// self.screen.kitty_images.delete(alloc, self, .{ .all = true });
|
||||
// },
|
||||
.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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user