mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 11:35:48 +00:00
terminal: Screen replace adjust with increaseCapacity
This commit is contained in:
@@ -1176,14 +1176,19 @@ inline fn cursorChangePin(self: *Screen, new: Pin) void {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a old style then we need to release it from the old page.
|
||||
// If we have an old style then we need to release it from the old page.
|
||||
const old_style_: ?style.Style = if (self.cursor.style_id == style.default_id)
|
||||
null
|
||||
else
|
||||
self.cursor.style;
|
||||
if (old_style_ != null) {
|
||||
// Release the style directly from the old page instead of going through
|
||||
// manualStyleUpdate, because the cursor position may have already been
|
||||
// updated but the pin has not, which would fail integrity checks.
|
||||
const old_page: *Page = &self.cursor.page_pin.node.data;
|
||||
old_page.styles.release(old_page.memory, self.cursor.style_id);
|
||||
self.cursor.style = .{};
|
||||
self.manualStyleUpdate() catch unreachable; // Removing a style should never fail
|
||||
self.cursor.style_id = style.default_id;
|
||||
}
|
||||
|
||||
// If we have a hyperlink then we need to release it from the old page.
|
||||
@@ -2000,7 +2005,17 @@ pub fn setAttribute(self: *Screen, attr: sgr.Attribute) !void {
|
||||
}
|
||||
|
||||
/// Call this whenever you manually change the cursor style.
|
||||
pub fn manualStyleUpdate(self: *Screen) !void {
|
||||
///
|
||||
/// Note that this can return any PageList capacity error, because it
|
||||
/// is possible for the internal pagelist to not accommodate the new style
|
||||
/// at all. This WILL attempt to resize our internal pages to fit the style
|
||||
/// but it is possible that it cannot be done, in which case upstream callers
|
||||
/// need to split the page or do something else.
|
||||
///
|
||||
/// NOTE(mitchellh): I think in the future we'll do page splitting
|
||||
/// automatically here and remove this failure scenario.
|
||||
pub fn manualStyleUpdate(self: *Screen) PageList.IncreaseCapacityError!void {
|
||||
defer self.assertIntegrity();
|
||||
var page: *Page = &self.cursor.page_pin.node.data;
|
||||
|
||||
// std.log.warn("active styles={}", .{page.styles.count()});
|
||||
@@ -2019,6 +2034,9 @@ pub fn manualStyleUpdate(self: *Screen) !void {
|
||||
// Clear the cursor style ID to prevent weird things from happening
|
||||
// if the page capacity has to be adjusted which would end up calling
|
||||
// manualStyleUpdate again.
|
||||
//
|
||||
// This also ensures that if anything fails below, we fall back to
|
||||
// clearing our style.
|
||||
self.cursor.style_id = style.default_id;
|
||||
|
||||
// After setting the style, we need to update our style map.
|
||||
@@ -2030,30 +2048,50 @@ pub fn manualStyleUpdate(self: *Screen) !void {
|
||||
page.memory,
|
||||
self.cursor.style,
|
||||
) catch |err| id: {
|
||||
// Our style map is full or needs to be rehashed,
|
||||
// so we allocate a new page, which will rehash,
|
||||
// and double the style capacity for it if it was
|
||||
// full.
|
||||
const node = try self.adjustCapacity(
|
||||
// Our style map is full or needs to be rehashed, so we need to
|
||||
// increase style capacity (or rehash).
|
||||
const node = try self.increaseCapacity(
|
||||
self.cursor.page_pin.node,
|
||||
switch (err) {
|
||||
error.OutOfMemory => .{ .styles = page.capacity.styles * 2 },
|
||||
error.NeedsRehash => .{},
|
||||
error.OutOfMemory => .styles,
|
||||
error.NeedsRehash => null,
|
||||
},
|
||||
);
|
||||
|
||||
page = &node.data;
|
||||
break :id try page.styles.add(
|
||||
break :id page.styles.add(
|
||||
page.memory,
|
||||
self.cursor.style,
|
||||
);
|
||||
) catch |err2| switch (err2) {
|
||||
error.OutOfMemory => {
|
||||
// This shouldn't happen because increaseCapacity is
|
||||
// guaranteed to increase our capacity by at least one and
|
||||
// we only need one space, but again, I don't want to crash
|
||||
// here so let's log loudly and reset.
|
||||
log.err("style addition failed after capacity increase", .{});
|
||||
return error.OutOfMemory;
|
||||
},
|
||||
error.NeedsRehash => {
|
||||
// This should be impossible because we rehash above
|
||||
// and rehashing should never result in a duplicate. But
|
||||
// we don't want to simply hard crash so log it and
|
||||
// clear our style.
|
||||
log.err("style rehash resulted in needs rehash", .{});
|
||||
return;
|
||||
},
|
||||
};
|
||||
};
|
||||
errdefer page.styles.release(page.memory, id);
|
||||
|
||||
self.cursor.style_id = id;
|
||||
self.assertIntegrity();
|
||||
}
|
||||
|
||||
/// Append a grapheme to the given cell within the current cursor row.
|
||||
pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void {
|
||||
pub fn appendGrapheme(
|
||||
self: *Screen,
|
||||
cell: *Cell,
|
||||
cp: u21,
|
||||
) PageList.IncreaseCapacityError!void {
|
||||
defer self.cursor.page_pin.node.data.assertIntegrity();
|
||||
self.cursor.page_pin.node.data.appendGrapheme(
|
||||
self.cursor.page_row,
|
||||
@@ -2073,11 +2111,9 @@ pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void {
|
||||
|
||||
// Adjust our capacity. This will update our cursor page pin and
|
||||
// force us to reload.
|
||||
const original_node = self.cursor.page_pin.node;
|
||||
const new_bytes = original_node.data.capacity.grapheme_bytes * 2;
|
||||
_ = try self.adjustCapacity(
|
||||
original_node,
|
||||
.{ .grapheme_bytes = new_bytes },
|
||||
_ = try self.increaseCapacity(
|
||||
self.cursor.page_pin.node,
|
||||
.grapheme_bytes,
|
||||
);
|
||||
|
||||
// The cell pointer is now invalid, so we need to get it from
|
||||
@@ -2088,17 +2124,22 @@ pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void {
|
||||
.gt => self.cursorCellRight(@intCast(cell_idx - self.cursor.x)),
|
||||
};
|
||||
|
||||
try self.cursor.page_pin.node.data.appendGrapheme(
|
||||
self.cursor.page_pin.node.data.appendGrapheme(
|
||||
self.cursor.page_row,
|
||||
reloaded_cell,
|
||||
cp,
|
||||
);
|
||||
) catch |err2| {
|
||||
comptime assert(@TypeOf(err2) == error{OutOfMemory});
|
||||
// This should never happen because we just increased capacity.
|
||||
// Log loudly but still return an error so we don't just
|
||||
// crash.
|
||||
log.err("grapheme append failed after capacity increase", .{});
|
||||
return err2;
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub const StartHyperlinkError = Allocator.Error || PageList.AdjustCapacityError;
|
||||
|
||||
/// Start the hyperlink state. Future cells will be marked as hyperlinks with
|
||||
/// this state. Note that various terminal operations may clear the hyperlink
|
||||
/// state, such as switching screens (alt screen).
|
||||
@@ -2106,7 +2147,7 @@ pub fn startHyperlink(
|
||||
self: *Screen,
|
||||
uri: []const u8,
|
||||
id_: ?[]const u8,
|
||||
) StartHyperlinkError!void {
|
||||
) PageList.IncreaseCapacityError!void {
|
||||
// Create our pending entry.
|
||||
const link: hyperlink.Hyperlink = .{
|
||||
.uri = uri,
|
||||
@@ -2131,21 +2172,21 @@ pub fn startHyperlink(
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
|
||||
// strings table is out of memory, adjust it up
|
||||
error.StringsOutOfMemory => _ = try self.adjustCapacity(
|
||||
error.StringsOutOfMemory => _ = try self.increaseCapacity(
|
||||
self.cursor.page_pin.node,
|
||||
.{ .string_bytes = self.cursor.page_pin.node.data.capacity.string_bytes * 2 },
|
||||
.string_bytes,
|
||||
),
|
||||
|
||||
// hyperlink set is out of memory, adjust it up
|
||||
error.SetOutOfMemory => _ = try self.adjustCapacity(
|
||||
error.SetOutOfMemory => _ = try self.increaseCapacity(
|
||||
self.cursor.page_pin.node,
|
||||
.{ .hyperlink_bytes = self.cursor.page_pin.node.data.capacity.hyperlink_bytes * 2 },
|
||||
.hyperlink_bytes,
|
||||
),
|
||||
|
||||
// hyperlink set is too full, rehash it
|
||||
error.SetNeedsRehash => _ = try self.adjustCapacity(
|
||||
error.SetNeedsRehash => _ = try self.increaseCapacity(
|
||||
self.cursor.page_pin.node,
|
||||
.{},
|
||||
null,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -2038,7 +2038,7 @@ pub const Cell = packed struct(u64) {
|
||||
|
||||
test "Page.layout can take a maxed capacity" {
|
||||
// Our intention is for a maxed-out capacity to always fit
|
||||
// within a page layout without trigering runtime safety on any
|
||||
// within a page layout without triggering runtime safety on any
|
||||
// overflow. This simplifies some of our handling downstream of the
|
||||
// call (relevant to: https://github.com/ghostty-org/ghostty/issues/10258)
|
||||
var cap: Capacity = undefined;
|
||||
|
||||
Reference in New Issue
Block a user