mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-07 04:14:26 +00:00
Misc error handling improvements (#10392)
See individual commits. An overview: * Added explicit error sets in more places * Removed `!` from functions that can't ever fail * `renderer.cell.Contents.resize` `errdefer` now does proper cleanup * `renderer.rebuildCells` can now only fail due to allocator OOM * If shaping fails during rendering, that row is skipped (previously it'd halt rendering there) * Failed image rendering setup in the renderer now skips that image (previously would halt the full frame) * GPU texture cleanup for failed image setup now works properly
This commit is contained in:
@@ -196,8 +196,18 @@ pub fn getFace(self: *Collection, index: Index) !*Face {
|
||||
return try self.getFaceFromEntry(try self.getEntry(index));
|
||||
}
|
||||
|
||||
pub const EntryError = error{
|
||||
/// Index represents a special font (built-in) and these don't
|
||||
/// have an associated face. This should be caught upstream and use
|
||||
/// alternate logic.
|
||||
SpecialHasNoFace,
|
||||
|
||||
/// Invalid index.
|
||||
IndexOutOfBounds,
|
||||
};
|
||||
|
||||
/// Get the unaliased entry from an index
|
||||
pub fn getEntry(self: *Collection, index: Index) !*Entry {
|
||||
pub fn getEntry(self: *Collection, index: Index) EntryError!*Entry {
|
||||
if (index.special() != null) return error.SpecialHasNoFace;
|
||||
const list = self.faces.getPtr(index.style);
|
||||
if (index.idx >= list.len) return error.IndexOutOfBounds;
|
||||
|
||||
@@ -98,7 +98,7 @@ pub const Shaper = struct {
|
||||
self.unichars.deinit(alloc);
|
||||
}
|
||||
|
||||
fn reset(self: *RunState) !void {
|
||||
fn reset(self: *RunState) void {
|
||||
self.codepoints.clearRetainingCapacity();
|
||||
self.unichars.clearRetainingCapacity();
|
||||
}
|
||||
@@ -644,8 +644,8 @@ pub const Shaper = struct {
|
||||
pub const RunIteratorHook = struct {
|
||||
shaper: *Shaper,
|
||||
|
||||
pub fn prepare(self: *RunIteratorHook) !void {
|
||||
try self.shaper.run_state.reset();
|
||||
pub fn prepare(self: *RunIteratorHook) void {
|
||||
self.shaper.run_state.reset();
|
||||
// log.warn("----------- run reset -------------", .{});
|
||||
}
|
||||
|
||||
@@ -681,7 +681,7 @@ pub const Shaper = struct {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn finalize(self: RunIteratorHook) !void {
|
||||
pub fn finalize(self: RunIteratorHook) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -175,7 +175,7 @@ pub const Shaper = struct {
|
||||
pub const RunIteratorHook = struct {
|
||||
shaper: *Shaper,
|
||||
|
||||
pub fn prepare(self: RunIteratorHook) !void {
|
||||
pub fn prepare(self: RunIteratorHook) void {
|
||||
// Reset the buffer for our current run
|
||||
self.shaper.hb_buf.reset();
|
||||
self.shaper.hb_buf.setContentType(.unicode);
|
||||
@@ -191,7 +191,7 @@ pub const Shaper = struct {
|
||||
self.shaper.hb_buf.add(cp, cluster);
|
||||
}
|
||||
|
||||
pub fn finalize(self: RunIteratorHook) !void {
|
||||
pub fn finalize(self: RunIteratorHook) void {
|
||||
self.shaper.hb_buf.guessSegmentProperties();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ pub const RunIterator = struct {
|
||||
var current_font: font.Collection.Index = .{};
|
||||
|
||||
// Allow the hook to prepare
|
||||
try self.hooks.prepare();
|
||||
self.hooks.prepare();
|
||||
|
||||
// Initialize our hash for this run.
|
||||
var hasher = Hasher.init(0);
|
||||
@@ -283,7 +283,7 @@ pub const RunIterator = struct {
|
||||
}
|
||||
|
||||
// Finalize our buffer
|
||||
try self.hooks.finalize();
|
||||
self.hooks.finalize();
|
||||
|
||||
// Add our length to the hash as an additional mechanism to avoid collisions
|
||||
autoHash(&hasher, j - self.i);
|
||||
|
||||
@@ -90,7 +90,6 @@ pub const Contents = struct {
|
||||
|
||||
const bg_cells = try alloc.alloc(shaderpkg.CellBg, cell_count);
|
||||
errdefer alloc.free(bg_cells);
|
||||
|
||||
@memset(bg_cells, .{ 0, 0, 0, 0 });
|
||||
|
||||
// The foreground lists can hold 3 types of items:
|
||||
@@ -106,32 +105,28 @@ pub const Contents = struct {
|
||||
// We have size.rows + 2 lists because indexes 0 and size.rows - 1 are
|
||||
// used for special lists containing the cursor cell which need to
|
||||
// be first and last in the buffer, respectively.
|
||||
var fg_rows = try ArrayListCollection(shaderpkg.CellText).init(
|
||||
var fg_rows: ArrayListCollection(shaderpkg.CellText) = try .init(
|
||||
alloc,
|
||||
size.rows + 2,
|
||||
size.columns * 3,
|
||||
);
|
||||
errdefer fg_rows.deinit(alloc);
|
||||
|
||||
alloc.free(self.bg_cells);
|
||||
self.fg_rows.deinit(alloc);
|
||||
|
||||
self.bg_cells = bg_cells;
|
||||
self.fg_rows = fg_rows;
|
||||
|
||||
// We don't need 3*cols worth of cells for the cursor lists, so we can
|
||||
// replace them with smaller lists. This is technically a tiny bit of
|
||||
// extra work but resize is not a hot function so it's worth it to not
|
||||
// waste the memory.
|
||||
self.fg_rows.lists[0].deinit(alloc);
|
||||
self.fg_rows.lists[0] = try std.ArrayListUnmanaged(
|
||||
shaderpkg.CellText,
|
||||
).initCapacity(alloc, 1);
|
||||
fg_rows.lists[0].deinit(alloc);
|
||||
fg_rows.lists[0] = try .initCapacity(alloc, 1);
|
||||
fg_rows.lists[size.rows + 1].deinit(alloc);
|
||||
fg_rows.lists[size.rows + 1] = try .initCapacity(alloc, 1);
|
||||
|
||||
self.fg_rows.lists[size.rows + 1].deinit(alloc);
|
||||
self.fg_rows.lists[size.rows + 1] = try std.ArrayListUnmanaged(
|
||||
shaderpkg.CellText,
|
||||
).initCapacity(alloc, 1);
|
||||
// Perform the swap, no going back from here.
|
||||
errdefer comptime unreachable;
|
||||
alloc.free(self.bg_cells);
|
||||
self.fg_rows.deinit(alloc);
|
||||
self.bg_cells = bg_cells;
|
||||
self.fg_rows = fg_rows;
|
||||
}
|
||||
|
||||
/// Reset the cell contents to an empty state without resizing.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -146,7 +146,7 @@ pub const Image = union(enum) {
|
||||
/// Mark the current image to be replaced with a pending one. This will
|
||||
/// attempt to update the existing texture if we have one, otherwise it
|
||||
/// will act like a new upload.
|
||||
pub fn markForReplace(self: *Image, alloc: Allocator, img: Image) !void {
|
||||
pub fn markForReplace(self: *Image, alloc: Allocator, img: Image) void {
|
||||
assert(img.isPending());
|
||||
|
||||
// If we have pending data right now, free it.
|
||||
@@ -216,9 +216,8 @@ pub const Image = union(enum) {
|
||||
|
||||
/// Prepare the pending image data for upload to the GPU.
|
||||
/// This doesn't need GPU access so is safe to call any time.
|
||||
pub fn prepForUpload(self: *Image, alloc: Allocator) !void {
|
||||
pub fn prepForUpload(self: *Image, alloc: Allocator) wuffs.Error!void {
|
||||
assert(self.isPending());
|
||||
|
||||
try self.convert(alloc);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user