terminal/new: eraseRows viewport behavior

This commit is contained in:
Mitchell Hashimoto
2024-02-29 10:40:52 -08:00
parent ee6344eac8
commit 07eaedf1fb
3 changed files with 159 additions and 21 deletions

View File

@ -5170,6 +5170,7 @@ test "Screen: promtpPath" {
} }
} }
// X - we don't use this in new terminal
test "Screen: scrollRegionUp single" { test "Screen: scrollRegionUp single" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp same line" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp single with pen" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp multiple" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp multiple count" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp count greater than available lines" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
@ -5278,6 +5284,7 @@ test "Screen: scrollRegionUp count greater than available lines" {
try testing.expectEqualStrings("1ABCD\n\n\n4ABCD", contents); try testing.expectEqualStrings("1ABCD\n\n\n4ABCD", contents);
} }
} }
// X - we don't use this in new terminal
test "Screen: scrollRegionUp fills with pen" { test "Screen: scrollRegionUp fills with pen" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp buffer wrap" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp buffer wrap alternate" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; 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" { test "Screen: scrollRegionUp buffer wrap alternative with extra lines" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -319,6 +319,34 @@ pub fn clonePool(
return result; 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. /// Scroll options.
pub const Scroll = union(enum) { pub const Scroll = union(enum) {
/// Scroll to the active area. This is also sometimes referred to as /// 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) { switch (behavior) {
.active => self.viewport = .{ .active = {} }, .active => self.viewport = .{ .active = {} },
.top => self.viewport = .{ .top = {} }, .top => self.viewport = .{ .top = {} },
.delta_row => |n| delta_row: { .delta_row => |n| {
if (n == 0) return; if (n == 0) return;
const top = self.getTopLeft(.viewport); 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 // But in a terminal when you get to the bottom and back into the
// active area, you usually expect that the viewport will now // active area, you usually expect that the viewport will now
// follow the active area. // follow the active area.
const active = self.getTopLeft(.active); self.viewport = self.viewportForOffset(offset);
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 };
}, },
} }
} }
@ -562,6 +572,23 @@ pub fn eraseRows(
return; 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 /// 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()); 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 testing = std.testing;
const alloc = testing.allocator; 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. // Erase the entire history, we should be back to just our active set.
s.eraseRows(.{ .history = .{} }, null); 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.?); 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" { test "PageList erase active regrows automatically" {

View File

@ -1651,3 +1651,61 @@ test "Screen: clone one line active with extra space" {
try testing.expectEqualStrings("1ABC", contents); 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);
}
}