diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 77773b1..2a8953a 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -167,7 +167,7 @@ fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadE return binding_set; } -const LoadError = (error{ NotFound, NotAnObject } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError); +pub const LoadError = (error{ NotFound, NotAnObject } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError); ///A collection of modes that represent a switchable editor emulation const Namespace = struct { diff --git a/src/location_history.zig b/src/location_history.zig index 98d5864..98c4816 100644 --- a/src/location_history.zig +++ b/src/location_history.zig @@ -16,7 +16,7 @@ pub const Selection = struct { end: Cursor = Cursor{}, }; -pub fn create() !Self { +pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!Self { return .{ .pid = try Process.create() }; } @@ -45,7 +45,7 @@ const Process = struct { selection: ?Selection = null, }; - pub fn create() !tp.pid { + pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!tp.pid { const self = try outer_a.create(Process); self.* = .{ .arena = std.heap.ArenaAllocator.init(outer_a), diff --git a/src/main.zig b/src/main.zig index 18c4ee9..65ce937 100644 --- a/src/main.zig +++ b/src/main.zig @@ -551,7 +551,9 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu }; } -pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void { +pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed }; + +pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void { config_mutex.lock(); defer config_mutex.unlock(); _ = allocator; @@ -560,14 +562,20 @@ pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void { // return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf)))); } -fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) !void { - var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true }); +fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) ConfigWriteError!void { + var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch |e| { + std.log.err("createFileAbsolute failed with {any} for: {s}", .{ e, file_name }); + return error.CreateConfigFileFailed; + }; defer file.close(); const writer = file.writer(); - return write_config_to_writer(T, data, writer); + write_config_to_writer(T, data, writer) catch |e| { + std.log.err("write file failed with {any} for: {s}", .{ e, file_name }); + return error.WriteConfigFileFailed; + }; } -pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) !void { +pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) @TypeOf(writer).Error!void { const default: T = .{}; inline for (@typeInfo(T).@"struct".fields) |field_info| { if (config_eql( @@ -650,7 +658,15 @@ pub fn get_config_dir() ![]const u8 { return get_app_config_dir(application_name); } -fn get_app_config_dir(appname: []const u8) ![]const u8 { +pub const ConfigDirError = error{ + NoSpaceLeft, + MakeConfigDirFailed, + MakeHomeConfigDirFailed, + MakeAppConfigDirFailed, + AppConfigDirUnavailable, +}; + +fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { const a = std.heap.c_allocator; const local = struct { var config_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; @@ -666,7 +682,7 @@ fn get_app_config_dir(appname: []const u8) ![]const u8 { const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home}); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return e, + else => return error.MakeHomeConfigDirFailed, }; break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname }); } else if (builtin.os.tag == .windows) ret: { @@ -675,7 +691,7 @@ fn get_app_config_dir(appname: []const u8) ![]const u8 { const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname }); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return e, + else => return error.MakeAppConfigDirFailed, }; break :ret dir; } else return error.AppConfigDirUnavailable; @@ -684,7 +700,7 @@ fn get_app_config_dir(appname: []const u8) ![]const u8 { local.config_dir = config_dir; std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return e, + else => return error.MakeConfigDirFailed, }; var keybind_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; @@ -784,11 +800,11 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 { return state_dir; } -fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ![]const u8 { +fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 { return get_app_config_dir_file_name(appname, base_name ++ ".json"); } -fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ![]const u8 { +fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 { const local = struct { var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index adea572..32af142 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -41,7 +41,20 @@ logger: log.Logger, loop: Loop, -pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) !Self { +pub const Error = error{ + UnexpectedRendererEvent, + OutOfMemory, + IntegerTooLarge, + IntegerTooSmall, + InvalidType, + TooShort, + Utf8CannotEncodeSurrogateHalf, + CodepointTooLarge, + TtyInitError, + TtyWriteError, +} || std.Thread.SpawnError; + +pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) Error!Self { const opts: vaxis.Vaxis.Options = .{ .kitty_keyboard_flags = .{ .disambiguate = true, @@ -54,7 +67,7 @@ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: }; return .{ .allocator = allocator, - .tty = try vaxis.Tty.init(), + .tty = vaxis.Tty.init() catch return error.TtyInitError, .vx = try vaxis.init(allocator, opts), .no_alternate = no_alternate, .event_buffer = std.ArrayList(u8).init(allocator), @@ -93,19 +106,19 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_ return std.debug.defaultPanic(msg, ret_addr orelse @returnAddress()); } -pub fn run(self: *Self) !void { +pub fn run(self: *Self) Error!void { self.vx.sgr = .legacy; self.vx.conpty_hacks = true; panic_cleanup = .{ .allocator = self.allocator, .tty = &self.tty, .vx = &self.vx }; - if (!self.no_alternate) try self.vx.enterAltScreen(self.tty.anyWriter()); + if (!self.no_alternate) self.vx.enterAltScreen(self.tty.anyWriter()) catch return error.TtyWriteError; if (builtin.os.tag == .windows) { try self.resize(.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }); // dummy resize to fully init vaxis } else { - try self.sigwinch(); + self.sigwinch() catch return error.TtyWriteError; } - try self.vx.setBracketedPaste(self.tty.anyWriter(), true); - try self.vx.queryTerminalSend(self.tty.anyWriter()); + self.vx.setBracketedPaste(self.tty.anyWriter(), true) catch return error.TtyWriteError; + self.vx.queryTerminalSend(self.tty.anyWriter()) catch return error.TtyWriteError; self.loop = Loop.init(&self.tty, &self.vx); try self.loop.start(); @@ -122,8 +135,8 @@ pub fn sigwinch(self: *Self) !void { try self.resize(try vaxis.Tty.getWinsize(self.input_fd_blocking())); } -fn resize(self: *Self, ws: vaxis.Winsize) !void { - try self.vx.resize(self.allocator, self.tty.anyWriter(), ws); +fn resize(self: *Self, ws: vaxis.Winsize) error{ TtyWriteError, OutOfMemory }!void { + self.vx.resize(self.allocator, self.tty.anyWriter(), ws) catch return error.TtyWriteError; self.vx.queueRefresh(); if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"})); } @@ -147,7 +160,7 @@ pub fn input_fd_blocking(self: Self) i32 { return self.tty.fd; } -pub fn process_renderer_event(self: *Self, msg: []const u8) !void { +pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { var input_: []const u8 = undefined; var text_: []const u8 = undefined; if (!try cbor.match(msg, .{ "RDR", cbor.extract(&input_), cbor.extract(&text_) })) @@ -268,7 +281,7 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void { .cap_da1 => { self.queries_done = true; self.vx.enableDetectedFeatures(self.tty.anyWriter()) catch |e| self.logger.err("enable features", e); - try self.vx.setMouseMode(self.tty.anyWriter(), true); + self.vx.setMouseMode(self.tty.anyWriter(), true) catch return error.TtyWriteError; }, .cap_kitty_keyboard => { self.logger.print("kitty keyboard capability detected", .{}); @@ -287,7 +300,7 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void { } } -fn fmtmsg(self: *Self, value: anytype) ![]const u8 { +fn fmtmsg(self: *Self, value: anytype) std.ArrayList(u8).Writer.Error![]const u8 { self.event_buffer.clearRetainingCapacity(); try cbor.writeValue(self.event_buffer.writer(), value); return self.event_buffer.items; @@ -329,7 +342,7 @@ fn handle_bracketed_paste_end(self: *Self) !void { if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items })); } -fn handle_bracketed_paste_error(self: *Self, e: anytype) !void { +fn handle_bracketed_paste_error(self: *Self, e: Error) !void { self.logger.err("bracketed paste", e); self.bracketed_paste_buffer.clearAndFree(); self.bracketed_paste = false; @@ -508,7 +521,7 @@ const Loop = struct { } /// spawns the input thread to read input from the tty - pub fn start(self: *Loop) !void { + pub fn start(self: *Loop) std.Thread.SpawnError!void { if (self.thread) |_| return; self.thread = try std.Thread.spawn(.{}, Loop.ttyRun, .{self}); } diff --git a/src/renderer/win32/renderer.zig b/src/renderer/win32/renderer.zig index 192f279..95815c4 100644 --- a/src/renderer/win32/renderer.zig +++ b/src/renderer/win32/renderer.zig @@ -20,6 +20,18 @@ const DropWriter = gui.DropWriter; pub const style = StyleBits; pub const styles = @import("tuirenderer").styles; +pub const Error = error{ + UnexpectedRendererEvent, + OutOfMemory, + IntegerTooLarge, + IntegerTooSmall, + InvalidType, + TooShort, + Utf8CannotEncodeSurrogateHalf, + CodepointTooLarge, + VaxisResizeError, +} || std.Thread.SpawnError; + pub const panic = messageBoxThenPanic(.{ .title = "Flow Panic" }); threadlocal var thread_is_panicing = false; @@ -80,7 +92,7 @@ pub fn init( handler_ctx: *anyopaque, no_alternate: bool, dispatch_initialized: *const fn (ctx: *anyopaque) void, -) !Self { +) Error!Self { std.debug.assert(!global.init_called); global.init_called = true; @@ -116,16 +128,16 @@ pub fn deinit(self: *Self) void { self.title_buf.deinit(); } -pub fn run(self: *Self) !void { +pub fn run(self: *Self) Error!void { if (self.thread) |_| return; // dummy resize to fully init vaxis const drop_writer = DropWriter{}; - try self.vx.resize( + self.vx.resize( self.allocator, drop_writer.writer().any(), .{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }, - ); + ) catch return error.VaxisResizeError; self.thread = try gui.start(); } @@ -164,7 +176,7 @@ pub fn stdplane(self: *Self) Plane { return plane; } -pub fn process_renderer_event(self: *Self, msg: []const u8) !void { +pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { const Input = struct { kind: u8, codepoint: u21, diff --git a/src/tui/WidgetList.zig b/src/tui/WidgetList.zig index c7c1e47..98808a9 100644 --- a/src/tui/WidgetList.zig +++ b/src/tui/WidgetList.zig @@ -31,7 +31,7 @@ on_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_re after_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_render_default, on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on_resize_default, -pub fn createH(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) !*Self { +pub fn createH(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) error{OutOfMemory}!*Self { const self: *Self = try allocator.create(Self); self.* = try init(allocator, parent, name, .horizontal, layout_, Box{}); self.plane.hide(); diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 7db301e..0a5bb94 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -60,7 +60,9 @@ const FileListType = enum { find_in_files, }; -pub fn create(allocator: std.mem.Allocator) !Widget { +pub const CreateError = error{ OutOfMemory, ThespianSpawnFailed }; + +pub fn create(allocator: std.mem.Allocator) CreateError!Widget { const self = try allocator.create(Self); self.* = .{ .allocator = allocator, diff --git a/src/tui/status/bar.zig b/src/tui/status/bar.zig index d7b891d..98d4c25 100644 --- a/src/tui/status/bar.zig +++ b/src/tui/status/bar.zig @@ -8,13 +8,17 @@ const Plane = @import("renderer").Plane; pub const Style = enum { none, grip }; -pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, style: Style, event_handler: ?EventHandler) !Widget { +pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, style: Style, event_handler: ?EventHandler) error{OutOfMemory}!Widget { var w = try WidgetList.createH(allocator, parent, "statusbar", .{ .static = 1 }); if (style == .grip) w.after_render = render_grip; w.ctx = w; var it = std.mem.splitScalar(u8, config, ' '); - while (it.next()) |widget_name| - try w.add(try status_widget.create(widget_name, allocator, w.plane, event_handler) orelse continue); + while (it.next()) |widget_name| { + try w.add(status_widget.create(widget_name, allocator, w.plane, event_handler) catch |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + error.WidgetInitFailed => null, + } orelse continue); + } return w.widget(); } diff --git a/src/tui/status/clock.zig b/src/tui/status/clock.zig index 4ef96b6..f1d6d38 100644 --- a/src/tui/status/clock.zig +++ b/src/tui/status/clock.zig @@ -19,14 +19,20 @@ tz: zeit.timezone.TimeZone, const Self = @This(); pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget { - var env = std.process.getEnvMap(allocator) catch |e| return tp.exit_error(e, @errorReturnTrace()); + var env = std.process.getEnvMap(allocator) catch |e| { + std.log.err("clock: std.process.getEnvMap failed with {any}", .{e}); + return error.WidgetInitFailed; + }; defer env.deinit(); const self: *Self = try allocator.create(Self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .on_event = event_handler, - .tz = zeit.local(allocator, &env) catch |e| return tp.exit_error(e, @errorReturnTrace()), + .tz = zeit.local(allocator, &env) catch |e| { + std.log.err("clock: zeit.local failed with {any}", .{e}); + return error.WidgetInitFailed; + }, }; try tui.message_filters().add(MessageFilter.bind(self, receive_tick)); self.update_tick_timer(.init); diff --git a/src/tui/status/minilog.zig b/src/tui/status/minilog.zig index 340c241..676103c 100644 --- a/src/tui/status/minilog.zig +++ b/src/tui/status/minilog.zig @@ -35,7 +35,7 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event }; logview.init(allocator); try tui.message_filters().add(MessageFilter.bind(self, receive_log)); - try log.subscribe(); + log.subscribe() catch return error.WidgetInitFailed; return Widget.to(self); } diff --git a/src/tui/status/widget.zig b/src/tui/status/widget.zig index f0e1c02..4b87b06 100644 --- a/src/tui/status/widget.zig +++ b/src/tui/status/widget.zig @@ -20,7 +20,7 @@ const widgets = std.static_string_map.StaticStringMap(CreateFunction).initCompti .{ "keybind", @import("keybindstate.zig").create }, .{ "tabs", @import("tabs.zig").create }, }); -pub const CreateError = error{ OutOfMemory, Exit }; +pub const CreateError = error{ OutOfMemory, WidgetInitFailed }; pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget; pub fn create(name: []const u8, allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!?Widget { diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 51386b3..5e047ab 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -84,11 +84,24 @@ fn start(args: StartArgs) tp.result { tp.receive(&self.receiver); } -fn init(allocator: Allocator) !*Self { +const InitError = error{ + OutOfMemory, + UnknownTheme, + ThespianMetronomeInitFailed, + ThespianMetronomeStartFailed, + ThespianTimeoutInitFailed, + ThespianSignalInitFailed, + ThespianSpawnFailed, +} || renderer.Error || + root.ConfigDirError || + root.ConfigWriteError || + keybind.LoadError; + +fn init(allocator: Allocator) InitError!*Self { var conf, const conf_bufs = root.read_config(@import("config"), allocator); defer root.free_config(allocator, conf_bufs); - const theme_ = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return tp.exit("unknown theme"); + const theme_ = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return error.UnknownTheme; conf.theme = theme_.name; conf.whitespace_mode = try allocator.dupe(u8, conf.whitespace_mode); conf.input_mode = try allocator.dupe(u8, conf.input_mode); @@ -161,7 +174,7 @@ fn init(allocator: Allocator) !*Self { return self; } -fn init_input_namespace(self: *Self) !void { +fn init_input_namespace(self: *Self) InitError!void { var mode_parts = std.mem.splitScalar(u8, self.config_.input_mode, '/'); const namespace_name = mode_parts.first(); keybind.set_namespace(namespace_name) catch { @@ -172,7 +185,7 @@ fn init_input_namespace(self: *Self) !void { }; } -fn init_delayed(self: *Self) !void { +fn init_delayed(self: *Self) command.Result { self.delayed_init_done = true; if (self.input_mode_) |_| {} else { if (self.delayed_init_input_mode) |delayed_init_input_mode| { @@ -218,9 +231,9 @@ fn deinit(self: *Self) void { self.allocator.destroy(self); } -fn listen_sigwinch(self: *Self) tp.result { +fn listen_sigwinch(self: *Self) error{ThespianSignalInitFailed}!void { if (self.sigwinch_signal) |old| old.deinit(); - self.sigwinch_signal = tp.signal.init(std.posix.SIG.WINCH, tp.message.fmt(.{"sigwinch"})) catch |e| return tp.exit_error(e, @errorReturnTrace()); + self.sigwinch_signal = try tp.signal.init(std.posix.SIG.WINCH, tp.message.fmt(.{"sigwinch"})); } fn update_mouse_idle_timer(self: *Self) void { @@ -465,7 +478,7 @@ fn active_event_handler(self: *Self) ?EventHandler { return mode.event_handler orelse mode.input_handler; } -fn dispatch_flush_input_event(self: *Self) !void { +fn dispatch_flush_input_event(self: *Self) error{ Exit, NoSpaceLeft }!void { var buf: [32]u8 = undefined; const mode = self.input_mode_ orelse return; try mode.input_handler.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"})); @@ -627,7 +640,7 @@ pub fn refresh_hover() void { _ = self.update_hover(self.last_hover_y, self.last_hover_x) catch {}; } -pub fn save_config() !void { +pub fn save_config() (root.ConfigDirError || root.ConfigWriteError)!void { const self = current(); try root.write_config(self.config_, self.allocator); }