mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
terminal: allow growing beyond max size for active area to fit
This commit is contained in:
@ -158,6 +158,12 @@ pub const Viewport = union(enum) {
|
|||||||
/// such that we can fit the active area AND at least two pages. Note we
|
/// such that we can fit the active area AND at least two pages. Note we
|
||||||
/// need the two pages for algorithms to work properly (such as grow) but
|
/// need the two pages for algorithms to work properly (such as grow) but
|
||||||
/// we don't need to fit double the active area.
|
/// we don't need to fit double the active area.
|
||||||
|
///
|
||||||
|
/// This min size may not be totally correct in the case that a large
|
||||||
|
/// number of other dimensions makes our row size in a page very small.
|
||||||
|
/// But this gives us a nice fast heuristic for determining min/max size.
|
||||||
|
/// Therefore, if the page size is violated you should always also verify
|
||||||
|
/// that we have enough space for the active area.
|
||||||
fn minMaxSize(cols: size.CellCountInt, rows: size.CellCountInt) !usize {
|
fn minMaxSize(cols: size.CellCountInt, rows: size.CellCountInt) !usize {
|
||||||
// Get our capacity to fit our rows. If the cols are too big, it may
|
// Get our capacity to fit our rows. If the cols are too big, it may
|
||||||
// force less rows than we want meaning we need more than one page to
|
// force less rows than we want meaning we need more than one page to
|
||||||
@ -850,7 +856,6 @@ fn reflowPage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.erasePage(initial_node);
|
self.erasePage(initial_node);
|
||||||
assert(self.page_size <= self.maxSize());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1696,10 +1701,27 @@ pub fn scrollClear(self: *PageList) !void {
|
|||||||
|
|
||||||
/// Returns the actual max size. This may be greater than the explicit
|
/// Returns the actual max size. This may be greater than the explicit
|
||||||
/// value if the explicit value is less than the min_max_size.
|
/// value if the explicit value is less than the min_max_size.
|
||||||
|
///
|
||||||
|
/// This value is a HEURISTIC. You cannot assert on this value. We may
|
||||||
|
/// exceed this value if required to fit the active area. This may be
|
||||||
|
/// required in some cases if the active area has a large number of
|
||||||
|
/// graphemes, styles, etc.
|
||||||
pub fn maxSize(self: *const PageList) usize {
|
pub fn maxSize(self: *const PageList) usize {
|
||||||
return @max(self.explicit_max_size, self.min_max_size);
|
return @max(self.explicit_max_size, self.min_max_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if we need to grow into our active area.
|
||||||
|
fn growRequiredForActive(self: *const PageList) bool {
|
||||||
|
var rows: usize = 0;
|
||||||
|
var page = self.pages.last;
|
||||||
|
while (page) |p| : (page = p.prev) {
|
||||||
|
rows += p.data.size.rows;
|
||||||
|
if (rows >= self.rows) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@ -1721,7 +1743,11 @@ pub fn grow(self: *PageList) !?*List.Node {
|
|||||||
// If allocation would exceed our max size, we prune the first page.
|
// If allocation would exceed our max size, we prune the first page.
|
||||||
// We don't need to reallocate because we can simply reuse that first
|
// We don't need to reallocate because we can simply reuse that first
|
||||||
// page.
|
// page.
|
||||||
if (self.page_size + PagePool.item_size > self.maxSize()) {
|
if (self.page_size + PagePool.item_size > self.maxSize()) prune: {
|
||||||
|
// If we need to add more memory to ensure our active area is
|
||||||
|
// satisfied then we do not prune.
|
||||||
|
if (self.growRequiredForActive()) break :prune;
|
||||||
|
|
||||||
const layout = Page.layout(try std_capacity.adjust(.{ .cols = self.cols }));
|
const layout = Page.layout(try std_capacity.adjust(.{ .cols = self.cols }));
|
||||||
|
|
||||||
// Get our first page and reset it to prepare for reuse.
|
// Get our first page and reset it to prepare for reuse.
|
||||||
@ -1762,7 +1788,6 @@ pub fn grow(self: *PageList) !?*List.Node {
|
|||||||
|
|
||||||
// We should never be more than our max size here because we've
|
// We should never be more than our max size here because we've
|
||||||
// verified the case above.
|
// verified the case above.
|
||||||
assert(self.page_size <= self.maxSize());
|
|
||||||
next_page.data.assertIntegrity();
|
next_page.data.assertIntegrity();
|
||||||
|
|
||||||
return next_page;
|
return next_page;
|
||||||
@ -3159,6 +3184,37 @@ test "PageList active after grow" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "PageList grow allows exceeding max size for active area" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
// Setup our initial page so that we fully take up one page.
|
||||||
|
const cap = try std_capacity.adjust(.{ .cols = 5 });
|
||||||
|
var s = try init(alloc, 5, cap.rows, 0);
|
||||||
|
defer s.deinit();
|
||||||
|
try testing.expectEqual(@as(usize, s.rows), s.totalRows());
|
||||||
|
|
||||||
|
// Grow once because we guarantee at least two pages of
|
||||||
|
// capacity so we want to get to that.
|
||||||
|
_ = try s.grow();
|
||||||
|
const start_pages = s.totalPages();
|
||||||
|
try testing.expect(start_pages >= 2);
|
||||||
|
|
||||||
|
// Surgically modify our pages so that they have a smaller size.
|
||||||
|
{
|
||||||
|
var it = s.pages.first;
|
||||||
|
while (it) |page| : (it = page.next) {
|
||||||
|
page.data.size.rows = 1;
|
||||||
|
page.data.capacity.rows = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow our row and ensure we don't prune pages because we need
|
||||||
|
// enough for the active area.
|
||||||
|
_ = try s.grow();
|
||||||
|
try testing.expectEqual(start_pages + 1, s.totalPages());
|
||||||
|
}
|
||||||
|
|
||||||
test "PageList scroll top" {
|
test "PageList scroll top" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user