mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 11:46:11 +03:00
terminal/new: eraseRows viewport behavior
This commit is contained in:
@ -5170,6 +5170,7 @@ test "Screen: promtpPath" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp single" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5187,6 +5188,7 @@ test "Screen: scrollRegionUp single" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp same line" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5204,6 +5206,7 @@ test "Screen: scrollRegionUp same line" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp single with pen" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5228,6 +5231,7 @@ test "Screen: scrollRegionUp single with pen" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp multiple" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5245,6 +5249,7 @@ test "Screen: scrollRegionUp multiple" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp multiple count" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5262,6 +5267,7 @@ test "Screen: scrollRegionUp multiple count" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp count greater than available lines" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5278,6 +5284,7 @@ test "Screen: scrollRegionUp count greater than available lines" {
|
||||
try testing.expectEqualStrings("1ABCD\n\n\n4ABCD", contents);
|
||||
}
|
||||
}
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp fills with pen" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5302,6 +5309,7 @@ test "Screen: scrollRegionUp fills with pen" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp buffer wrap" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5334,6 +5342,7 @@ test "Screen: scrollRegionUp buffer wrap" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp buffer wrap alternate" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -5366,6 +5375,7 @@ test "Screen: scrollRegionUp buffer wrap alternate" {
|
||||
}
|
||||
}
|
||||
|
||||
// X - we don't use this in new terminal
|
||||
test "Screen: scrollRegionUp buffer wrap alternative with extra lines" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
@ -319,6 +319,34 @@ pub fn clonePool(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the viewport for the given offset, prefering to pin to
|
||||
/// "active" if the offset is within the active area.
|
||||
fn viewportForOffset(self: *const PageList, offset: RowOffset) Viewport {
|
||||
// If the offset is on the active page, then we pin to active
|
||||
// if our row idx is beyond the active row idx.
|
||||
const active = self.getTopLeft(.active);
|
||||
if (offset.page == active.page) {
|
||||
if (offset.row_offset >= active.row_offset) {
|
||||
return .{ .active = {} };
|
||||
}
|
||||
} else {
|
||||
var page_ = active.page.next;
|
||||
while (page_) |page| {
|
||||
// This loop is pretty fast because the active area is
|
||||
// never that large so this is at most one, two pages for
|
||||
// reasonable terminals (including very large real world
|
||||
// ones).
|
||||
|
||||
// A page forward in the active area is our page, so we're
|
||||
// definitely in the active area.
|
||||
if (page == offset.page) return .{ .active = {} };
|
||||
page_ = page.next;
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .exact = offset };
|
||||
}
|
||||
|
||||
/// Scroll options.
|
||||
pub const Scroll = union(enum) {
|
||||
/// Scroll to the active area. This is also sometimes referred to as
|
||||
@ -343,7 +371,7 @@ pub fn scroll(self: *PageList, behavior: Scroll) void {
|
||||
switch (behavior) {
|
||||
.active => self.viewport = .{ .active = {} },
|
||||
.top => self.viewport = .{ .top = {} },
|
||||
.delta_row => |n| delta_row: {
|
||||
.delta_row => |n| {
|
||||
if (n == 0) return;
|
||||
|
||||
const top = self.getTopLeft(.viewport);
|
||||
@ -362,25 +390,7 @@ pub fn scroll(self: *PageList, behavior: Scroll) void {
|
||||
// But in a terminal when you get to the bottom and back into the
|
||||
// active area, you usually expect that the viewport will now
|
||||
// follow the active area.
|
||||
const active = self.getTopLeft(.active);
|
||||
if (offset.page == active.page) {
|
||||
if (offset.row_offset >= active.row_offset) {
|
||||
self.viewport = .{ .active = {} };
|
||||
break :delta_row;
|
||||
}
|
||||
} else active: {
|
||||
// Check forward pages too.
|
||||
var page = active.page.next orelse break :active;
|
||||
while (true) {
|
||||
if (page == offset.page) {
|
||||
self.viewport = .{ .active = {} };
|
||||
break :delta_row;
|
||||
}
|
||||
page = page.next orelse break :active;
|
||||
}
|
||||
}
|
||||
|
||||
self.viewport = .{ .exact = offset };
|
||||
self.viewport = self.viewportForOffset(offset);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -562,6 +572,23 @@ pub fn eraseRows(
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
// If we have an exact viewport, we need to adjust for active area.
|
||||
switch (self.viewport) {
|
||||
.active => {},
|
||||
|
||||
.exact => |offset| self.viewport = self.viewportForOffset(offset),
|
||||
|
||||
// For top, we move back to active if our erasing moved our
|
||||
// top page into the active area.
|
||||
.top => {
|
||||
const vp = self.viewportForOffset(.{
|
||||
.page = self.pages.first.?,
|
||||
.row_offset = 0,
|
||||
});
|
||||
if (vp == .active) self.viewport = vp;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Erase a single page, freeing all its resources. The page can be
|
||||
@ -1479,7 +1506,7 @@ test "PageList erase" {
|
||||
try testing.expectEqual(s.rows, s.totalRows());
|
||||
}
|
||||
|
||||
test "PageList erase resets viewport if inside erased page" {
|
||||
test "PageList erase resets viewport to active if moves within active" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
@ -1498,7 +1525,50 @@ test "PageList erase resets viewport if inside erased page" {
|
||||
|
||||
// Erase the entire history, we should be back to just our active set.
|
||||
s.eraseRows(.{ .history = .{} }, null);
|
||||
try testing.expect(s.viewport == .active);
|
||||
}
|
||||
|
||||
test "PageList erase resets viewport if inside erased page but not active" {
|
||||
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 = .{} }, .{ .history = .{ .y = 2 } });
|
||||
try testing.expect(s.viewport.exact.page == s.pages.first.?);
|
||||
}
|
||||
|
||||
test "PageList erase resets viewport to active if top is inside active" {
|
||||
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(.{ .top = {} });
|
||||
|
||||
// Erase the entire history, we should be back to just our active set.
|
||||
s.eraseRows(.{ .history = .{} }, null);
|
||||
try testing.expect(s.viewport == .active);
|
||||
}
|
||||
|
||||
test "PageList erase active regrows automatically" {
|
||||
|
@ -1651,3 +1651,61 @@ test "Screen: clone one line active with extra space" {
|
||||
try testing.expectEqualStrings("1ABC", contents);
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: clear history with no history" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, 10, 3, 3);
|
||||
defer s.deinit();
|
||||
try s.testWriteString("4ABCD\n5EFGH\n6IJKL");
|
||||
try testing.expect(s.pages.viewport == .active);
|
||||
s.eraseRows(.{ .history = .{} }, null);
|
||||
try testing.expect(s.pages.viewport == .active);
|
||||
{
|
||||
// Test our contents rotated
|
||||
const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} });
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("4ABCD\n5EFGH\n6IJKL", contents);
|
||||
}
|
||||
{
|
||||
// Test our contents rotated
|
||||
const contents = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("4ABCD\n5EFGH\n6IJKL", contents);
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: clear history" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, 10, 3, 3);
|
||||
defer s.deinit();
|
||||
try s.testWriteString("1ABCD\n2EFGH\n3IJKL\n4ABCD\n5EFGH\n6IJKL");
|
||||
try testing.expect(s.pages.viewport == .active);
|
||||
|
||||
// Scroll to top
|
||||
s.scroll(.{ .top = {} });
|
||||
{
|
||||
// Test our contents rotated
|
||||
const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} });
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("1ABCD\n2EFGH\n3IJKL", contents);
|
||||
}
|
||||
|
||||
s.eraseRows(.{ .history = .{} }, null);
|
||||
try testing.expect(s.pages.viewport == .active);
|
||||
{
|
||||
// Test our contents rotated
|
||||
const contents = try s.dumpStringAlloc(alloc, .{ .viewport = .{} });
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("4ABCD\n5EFGH\n6IJKL", contents);
|
||||
}
|
||||
{
|
||||
// Test our contents rotated
|
||||
const contents = try s.dumpStringAlloc(alloc, .{ .screen = .{} });
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("4ABCD\n5EFGH\n6IJKL", contents);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user