diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig index 5161224f8..f18c9d143 100644 --- a/src/terminal/PageList.zig +++ b/src/terminal/PageList.zig @@ -1573,6 +1573,18 @@ pub fn adjustCapacity( return new_page; } +/// Compact a page, reallocating to minimize the amount of memory +/// required for the page. This is useful when we've overflowed ID +/// spaces, are archiving a page, etc. +/// +/// Note today: this doesn't minimize the memory usage, but it does +/// fix style ID overflow. A future update can shrink the memory +/// allocation. +pub fn compact(self: *PageList, page: *List.Node) !*List.Node { + // Adjusting capacity with no adjustments forces a reallocation. + return try self.adjustCapacity(page, .{}); +} + /// Create a new page node. This does not add it to the list and this /// does not do any memory size accounting with max_size/page_size. fn createPage(self: *PageList, cap: Capacity) !*List.Node { diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 7773f5081..de7517307 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -971,26 +971,36 @@ pub fn manualStyleUpdate(self: *Screen) !void { const md = page.styles.upsert( page.memory, self.cursor.style, - ) catch |err| switch (err) { - // Our style map is full. Let's allocate a new page by doubling - // the size and then try again. - error.OutOfMemory => md: { - const node = try self.pages.adjustCapacity( - self.cursor.page_pin.page, - .{ .styles = page.capacity.styles * 2 }, - ); + ) catch |err| md: { + switch (err) { + // Our style map is full. Let's allocate a new page by doubling + // the size and then try again. + error.OutOfMemory => { + const node = try self.pages.adjustCapacity( + self.cursor.page_pin.page, + .{ .styles = page.capacity.styles * 2 }, + ); - // Since this modifies our cursor page, we need to reload - self.cursorReload(); + page = &node.data; + }, - page = &node.data; - break :md try page.styles.upsert( - page.memory, - self.cursor.style, - ); - }, + // We've run out of style IDs. This is fixed by doing a page + // compaction. + error.Overflow => { + const node = try self.pages.compact( + self.cursor.page_pin.page, + ); + page = &node.data; + }, + } - error.Overflow => return err, // TODO + // Since this modifies our cursor page, we need to reload + self.cursorReload(); + + break :md try page.styles.upsert( + page.memory, + self.cursor.style, + ); }; self.cursor.style_id = md.id; self.cursor.style_ref = &md.ref;