diff --git a/src/EventHandler.zig b/src/EventHandler.zig index 879ba12..0d5f1d7 100644 --- a/src/EventHandler.zig +++ b/src/EventHandler.zig @@ -118,6 +118,7 @@ pub fn send(self: Self, from_: tp.pid_ref, m: tp.message) tp.result { pub fn empty(allocator: Allocator) !Self { const child: type = struct {}; const widget = try allocator.create(child); + errdefer allocator.destroy(widget); widget.* = .{}; return .{ .ptr = widget, diff --git a/src/LSP.zig b/src/LSP.zig index b224f4f..58012a3 100644 --- a/src/LSP.zig +++ b/src/LSP.zig @@ -17,22 +17,34 @@ const OutOfMemoryError = error{OutOfMemory}; const SendError = error{SendFailed}; const SpawnError = error{ThespianSpawnFailed}; -pub fn open(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidLspCommand } || cbor.Error)!Self { - return .{ .allocator = allocator, .pid = try Process.create(allocator, project, cmd) }; +pub fn open( + allocator: std.mem.Allocator, + project: []const u8, + cmd: tp.message, +) (error{ ThespianSpawnFailed, InvalidLspCommand } || cbor.Error)!*const Self { + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + self.* = .{ + .allocator = allocator, + .pid = try Process.create(allocator, project, cmd), + }; + return self; } -pub fn deinit(self: Self) void { +pub fn deinit(self: *const Self) void { self.pid.send(.{"close"}) catch {}; self.pid.deinit(); + self.allocator.destroy(self); } -pub fn term(self: Self) void { +pub fn term(self: *const Self) void { self.pid.send(.{"term"}) catch {}; self.pid.deinit(); + self.allocator.destroy(self); } pub fn send_request( - self: Self, + self: *const Self, allocator: std.mem.Allocator, method: []const u8, m: anytype, @@ -44,17 +56,56 @@ pub fn send_request( return RequestContext(@TypeOf(ctx)).send(allocator, self.pid.ref(), ctx, tp.message.fmt(.{ "REQ", method, cb.items })); } -pub fn send_notification(self: Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void { +pub fn send_notification(self: *const Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void { var cb = std.ArrayList(u8).init(self.allocator); defer cb.deinit(); try cbor.writeValue(cb.writer(), m); return self.send_notification_raw(method, cb.items); } -pub fn send_notification_raw(self: Self, method: []const u8, cb: []const u8) SendError!void { +pub fn send_notification_raw(self: *const Self, method: []const u8, cb: []const u8) SendError!void { self.pid.send(.{ "NTFY", method, cb }) catch return error.SendFailed; } +pub const ErrorCode = enum(i32) { + + // Defined by JSON-RPC + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + + // Defined by LSP + RequestFailed = -32803, + ServerCancelled = -32802, + ContentModified = -32801, + RequestCancelled = -32800, +}; + +pub fn send_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, result: anytype) (SendError || OutOfMemoryError)!void { + var cb = std.ArrayList(u8).init(allocator); + defer cb.deinit(); + const writer = cb.writer(); + try cbor.writeArrayHeader(writer, 3); + try cbor.writeValue(writer, "RSP"); + try writer.writeAll(cbor_id); + try cbor.writeValue(cb.writer(), result); + to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed; +} + +pub fn send_error_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, code: ErrorCode, message: []const u8) (SendError || OutOfMemoryError)!void { + var cb = std.ArrayList(u8).init(allocator); + defer cb.deinit(); + const writer = cb.writer(); + try cbor.writeArrayHeader(writer, 4); + try cbor.writeValue(writer, "ERR"); + try writer.writeAll(cbor_id); + try cbor.writeValue(cb.writer(), code); + try cbor.writeValue(cb.writer(), message); + to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed; +} + pub fn close(self: *Self) void { self.deinit(); } @@ -144,6 +195,7 @@ const Process = struct { return error.InvalidLspCommand; } const self = try allocator.create(Process); + errdefer allocator.destroy(self); var sp_tag_ = std.ArrayList(u8).init(allocator); defer sp_tag_.deinit(); try sp_tag_.appendSlice(tag); @@ -239,6 +291,8 @@ const Process = struct { var err: []const u8 = ""; var code: u32 = 0; var cbor_id: []const u8 = ""; + var error_code: ErrorCode = undefined; + var message: []const u8 = ""; if (try cbor.match(m.buf, .{ "REQ", "initialize", tp.extract(&bytes) })) { try self.send_request(from, "initialize", bytes); @@ -249,6 +303,8 @@ const Process = struct { } } else if (try cbor.match(m.buf, .{ "RSP", tp.extract_cbor(&cbor_id), tp.extract_cbor(&bytes) })) { try self.send_response(cbor_id, bytes); + } else if (try cbor.match(m.buf, .{ "ERR", tp.extract_cbor(&cbor_id), tp.extract(&error_code), tp.extract(&message) })) { + try self.send_error_response(cbor_id, error_code, message); } else if (try cbor.match(m.buf, .{ "NTFY", "initialized", tp.extract(&bytes) })) { self.state = .running; try self.send_notification("initialized", bytes); @@ -469,6 +525,39 @@ const Process = struct { self.write_log("### SEND response:\n{s}\n###\n", .{output.items}); } + fn send_error_response(self: *Process, cbor_id: []const u8, error_code: ErrorCode, message: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { + const sp = if (self.sp) |*sp| sp else return error.Closed; + + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const msg_writer = msg.writer(); + try cbor.writeMapHeader(msg_writer, 3); + try cbor.writeValue(msg_writer, "jsonrpc"); + try cbor.writeValue(msg_writer, "2.0"); + try cbor.writeValue(msg_writer, "id"); + try msg_writer.writeAll(cbor_id); + try cbor.writeValue(msg_writer, "error"); + try cbor.writeMapHeader(msg_writer, 2); + try cbor.writeValue(msg_writer, "code"); + try cbor.writeValue(msg_writer, @intFromEnum(error_code)); + try cbor.writeValue(msg_writer, "message"); + try cbor.writeValue(msg_writer, message); + + const json = try cbor.toJsonAlloc(self.allocator, msg.items); + defer self.allocator.free(json); + var output = std.ArrayList(u8).init(self.allocator); + defer output.deinit(); + const writer = output.writer(); + const terminator = "\r\n"; + const content_length = json.len + terminator.len; + try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length}); + _ = try writer.write(json); + _ = try writer.write(terminator); + + sp.send(output.items) catch return error.SendFailed; + self.write_log("### SEND error response:\n{s}\n###\n", .{output.items}); + } + fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) Error!void { const sp = if (self.sp) |*sp| sp else return error.Closed; diff --git a/src/Project.zig b/src/Project.zig index 19e4399..f9da66c 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -19,8 +19,8 @@ files: std.ArrayListUnmanaged(File) = .empty, pending: std.ArrayListUnmanaged(File) = .empty, longest_file_path: usize = 0, open_time: i64, -language_servers: std.StringHashMap(LSP), -file_language_server: std.StringHashMap(LSP), +language_servers: std.StringHashMap(*const LSP), +file_language_server: std.StringHashMap(*const LSP), tasks: std.ArrayList(Task), persistent: bool = false, logger: log.Logger, @@ -74,8 +74,8 @@ pub fn init(allocator: std.mem.Allocator, name: []const u8) OutOfMemoryError!Sel .allocator = allocator, .name = try allocator.dupe(u8, name), .open_time = std.time.milliTimestamp(), - .language_servers = std.StringHashMap(LSP).init(allocator), - .file_language_server = std.StringHashMap(LSP).init(allocator), + .language_servers = std.StringHashMap(*const LSP).init(allocator), + .file_language_server = std.StringHashMap(*const LSP).init(allocator), .tasks = std.ArrayList(Task).init(allocator), .logger = log.logger("project"), .logger_lsp = log.logger("lsp"), @@ -261,11 +261,14 @@ pub fn restore_state_v0(self: *Self, data: []const u8) error{ } } -fn get_language_server_instance(self: *Self, language_server: []const u8) StartLspError!LSP { +fn get_language_server_instance(self: *Self, language_server: []const u8) StartLspError!*const LSP { if (self.language_servers.get(language_server)) |lsp| { - if (!lsp.pid.expired()) return lsp; - lsp.deinit(); - _ = self.language_servers.remove(language_server); + if (lsp.pid.expired()) { + _ = self.language_servers.remove(language_server); + lsp.deinit(); + } else { + return lsp; + } } const lsp = try LSP.open(self.allocator, self.name, .{ .buf = language_server }); errdefer lsp.deinit(); @@ -279,7 +282,7 @@ fn get_language_server_instance(self: *Self, language_server: []const u8) StartL return lsp; } -fn get_or_start_language_server(self: *Self, file_path: []const u8, language_server: []const u8) StartLspError!LSP { +fn get_or_start_language_server(self: *Self, file_path: []const u8, language_server: []const u8) StartLspError!*const LSP { const lsp = self.file_language_server.get(file_path) orelse blk: { const new_lsp = try self.get_language_server_instance(language_server); const key = try self.allocator.dupe(u8, file_path); @@ -289,7 +292,7 @@ fn get_or_start_language_server(self: *Self, file_path: []const u8, language_ser return lsp; } -fn get_language_server(self: *Self, file_path: []const u8) LspError!LSP { +fn get_language_server(self: *Self, file_path: []const u8) LspError!*const LSP { const lsp = self.file_language_server.get(file_path) orelse return error.NoLsp; if (lsp.pid.expired()) { if (self.file_language_server.fetchRemove(file_path)) |kv| @@ -1491,26 +1494,19 @@ pub fn show_message(self: *Self, _: tp.pid_ref, params_cb: []const u8) !void { pub fn register_capability(self: *Self, from: tp.pid_ref, cbor_id: []const u8, params_cb: []const u8) ClientError!void { _ = params_cb; - return self.send_lsp_response(from, cbor_id, null); + return LSP.send_response(self.allocator, from, cbor_id, null) catch error.ClientFailed; } pub fn workDoneProgress_create(self: *Self, from: tp.pid_ref, cbor_id: []const u8, params_cb: []const u8) ClientError!void { _ = params_cb; - return self.send_lsp_response(from, cbor_id, null); + return LSP.send_response(self.allocator, from, cbor_id, null) catch error.ClientFailed; } -pub fn send_lsp_response(self: *Self, from: tp.pid_ref, cbor_id: []const u8, result: anytype) ClientError!void { - var cb = std.ArrayList(u8).init(self.allocator); - defer cb.deinit(); - const writer = cb.writer(); - try cbor.writeArrayHeader(writer, 3); - try cbor.writeValue(writer, "RSP"); - try writer.writeAll(cbor_id); - try cbor.writeValue(cb.writer(), result); - from.send_raw(.{ .buf = cb.items }) catch return error.ClientFailed; +pub fn unsupported_lsp_request(self: *Self, from: tp.pid_ref, cbor_id: []const u8, method: []const u8) ClientError!void { + return LSP.send_error_response(self.allocator, from, cbor_id, LSP.ErrorCode.MethodNotFound, method) catch error.ClientFailed; } -fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8, language_server: []const u8) !void { +fn send_lsp_init_request(self: *Self, lsp: *const LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8, language_server: []const u8) !void { const handler: struct { language_server: []const u8, lsp: LSP, diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 50bd950..195ac98 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -159,6 +159,7 @@ pub const Leaf = struct { if (piece.len == 0) return if (!bol and !eol) &empty_leaf else if (bol and !eol) &empty_bol_leaf else if (!bol and eol) &empty_eol_leaf else &empty_line_leaf; const node = try allocator.create(Node); + errdefer allocator.destroy(node); node.* = .{ .leaf = .{ .buf = piece, .bol = bol, .eol = eol } }; return node; } @@ -267,6 +268,7 @@ const Node = union(enum) { fn new(allocator: Allocator, l: *const Node, r: *const Node) !*const Node { const node = try allocator.create(Node); + errdefer allocator.destroy(node); const l_weights_sum = l.weights_sum(); var weights_sum_ = Weights{}; weights_sum_.add(l_weights_sum); @@ -1065,6 +1067,7 @@ const Node = union(enum) { pub fn create(allocator: Allocator) error{OutOfMemory}!*Self { const self = try allocator.create(Self); + errdefer allocator.destroy(self); const arena_a = if (builtin.is_test) allocator else std.heap.page_allocator; self.* = .{ .arena = std.heap.ArenaAllocator.init(arena_a), diff --git a/src/diff.zig b/src/diff.zig index abb2bd6..6c2a970 100644 --- a/src/diff.zig +++ b/src/diff.zig @@ -66,6 +66,7 @@ const Process = struct { pub fn create() !tp.pid { const self = try allocator.create(Process); + errdefer allocator.destroy(self); self.* = .{ .receiver = Receiver.init(Process.receive, self), }; diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 2a6641b..03f7a9f 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -89,8 +89,8 @@ ["ctrl+shift+d", "dupe_down"], ["ctrl+shift+z", "redo"], ["ctrl+shift+w", "close_file_without_saving"], - ["ctrl+shift+l", "run_async", "add_cursor_all_matches"], - ["ctrl+shift+i", "run_async", "toggle_inspector_view"], + ["ctrl+shift+l", "add_cursor_all_matches"], + ["ctrl+shift+i", "toggle_inspector_view"], ["ctrl+shift+m", "show_diagnostics"], ["ctrl+shift+enter", "smart_insert_line_before"], ["ctrl+shift+end", "select_buffer_end"], diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 9ffb43b..f45ef52 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -65,7 +65,7 @@ const Handler = struct { bindings: *const BindingSet, fn create(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode { - const self: *@This() = try allocator.create(@This()); + const self = try allocator.create(@This()); errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, diff --git a/src/project_manager.zig b/src/project_manager.zig index 78dde81..a179c1e 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -263,6 +263,7 @@ const Process = struct { fn create() SpawnError!tp.pid { const allocator = std.heap.c_allocator; const self = try allocator.create(Process); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .parent = tp.self_pid().clone(), @@ -619,11 +620,11 @@ const Process = struct { project.register_capability(from, cbor_id, params_cb) else if (std.mem.eql(u8, method, "window/workDoneProgress/create")) project.workDoneProgress_create(from, cbor_id, params_cb) - else blk: { + else { const params = try cbor.toJsonAlloc(self.allocator, params_cb); defer self.allocator.free(params); - self.logger.print_err("lsp", "unsupported LSP request: {s} -> {s}", .{ method, params }); - break :blk error.Unsupported; + self.logger.print("unsupported LSP request: {s} -> {s}", .{ method, params }); + project.unsupported_lsp_request(from, cbor_id, method) catch {}; }; } @@ -755,6 +756,7 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_ fn spawn_link(allocator: std.mem.Allocator, parent: tp.pid_ref, project: *Project, max: usize, path: []const u8) (SpawnError || std.fs.Dir.OpenError)!void { const self = try allocator.create(path_files); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .project_name = try allocator.dupe(u8, project.name), diff --git a/src/ripgrep.zig b/src/ripgrep.zig index c6c14a3..4ea7bd6 100644 --- a/src/ripgrep.zig +++ b/src/ripgrep.zig @@ -85,6 +85,7 @@ const Process = struct { pub fn create(allocator: std.mem.Allocator, query: []const u8, tag: [:0]const u8, stdin_behavior: std.process.Child.StdIo) !tp.pid { const self = try allocator.create(Process); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .query = try allocator.dupe(u8, query), diff --git a/src/service_template.zig b/src/service_template.zig index 17f0a83..92cc265 100644 --- a/src/service_template.zig +++ b/src/service_template.zig @@ -45,6 +45,7 @@ const Process = struct { pub fn create(allocator: std.mem.Allocator) Error!tp.pid { const self = try allocator.create(Process); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .parent = tp.self_pid().clone(), diff --git a/src/shell.zig b/src/shell.zig index cb27221..5869a3d 100644 --- a/src/shell.zig +++ b/src/shell.zig @@ -156,6 +156,7 @@ const Process = struct { return error.InvalidShellArg0; const self = try allocator.create(Process); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .argv = argv, diff --git a/src/syntax/src/QueryCache.zig b/src/syntax/src/QueryCache.zig index 816d8bc..7b011d5 100644 --- a/src/syntax/src/QueryCache.zig +++ b/src/syntax/src/QueryCache.zig @@ -62,6 +62,7 @@ pub const Error = CacheError || QueryParseError || QuerySerializeError; pub fn create(allocator: std.mem.Allocator, opts: struct { lock: bool = false }) !*Self { const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .mutex = if (opts.lock) .{} else null, diff --git a/src/syntax/src/syntax.zig b/src/syntax/src/syntax.zig index cb54b08..b07f6d6 100644 --- a/src/syntax/src/syntax.zig +++ b/src/syntax/src/syntax.zig @@ -29,9 +29,13 @@ tree: ?*treez.Tree = null, pub fn create(file_type: FileType, allocator: std.mem.Allocator, query_cache: *QueryCache) !*Self { const query = try query_cache.get(file_type, .highlights); + errdefer query_cache.release(query, .highlights); const errors_query = try query_cache.get(file_type, .errors); + errdefer query_cache.release(errors_query, .highlights); const injections = try query_cache.get(file_type, .injections); + errdefer if (injections) |injections_| query_cache.release(injections_, .injections); const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .lang = file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{file_type.name}), @@ -40,7 +44,6 @@ pub fn create(file_type: FileType, allocator: std.mem.Allocator, query_cache: *Q .errors_query = errors_query, .injections = injections, }; - errdefer self.destroy(query_cache); try self.parser.setLanguage(self.lang); return self; } @@ -58,6 +61,7 @@ pub fn create_guess_file_type_static(allocator: std.mem.Allocator, content: []co pub fn destroy(self: *Self, query_cache: *QueryCache) void { if (self.tree) |tree| tree.destroy(); query_cache.release(self.query, .highlights); + query_cache.release(self.errors_query, .highlights); if (self.injections) |injections| query_cache.release(injections, .injections); self.parser.destroy(); self.allocator.destroy(self); diff --git a/src/syntax/src/ts_serializer.zig b/src/syntax/src/ts_serializer.zig index 90c5865..928f60b 100644 --- a/src/syntax/src/ts_serializer.zig +++ b/src/syntax/src/ts_serializer.zig @@ -285,6 +285,7 @@ pub const DeserializeError = error{ pub fn fromCbor(cb: []const u8, allocator: std.mem.Allocator) DeserializeError!struct { *TSQuery, *std.heap.ArenaAllocator } { var arena = try allocator.create(std.heap.ArenaAllocator); + errdefer allocator.destroy(arena); arena.* = std.heap.ArenaAllocator.init(allocator); errdefer arena.deinit(); const query = try arena.allocator().create(TSQuery); diff --git a/src/tui/Button.zig b/src/tui/Button.zig index 60fbc41..1ca9704 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -48,6 +48,7 @@ pub fn Options(context: type) type { pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) error{OutOfMemory}!*State(ctx_type) { const Self = State(ctx_type); const self = try allocator.create(Self); + errdefer allocator.destroy(self); var n = try Plane.init(&opts.pos.opts(@typeName(Self)), parent); errdefer n.deinit(); self.* = .{ @@ -57,6 +58,7 @@ pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: .opts = opts, }; self.opts.label = try self.allocator.dupe(u8, opts.label); + errdefer allocator.free(self.opts.label); try self.init(); return self; } diff --git a/src/tui/InputBox.zig b/src/tui/InputBox.zig index 71d79b7..1efd3d2 100644 --- a/src/tui/InputBox.zig +++ b/src/tui/InputBox.zig @@ -58,9 +58,10 @@ pub fn Options(context: type) type { pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) !Widget { const Self = State(ctx_type); - const self = try allocator.create(Self); var n = try Plane.init(&opts.pos.opts(@typeName(Self)), parent); errdefer n.deinit(); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .parent = parent, .plane = n, diff --git a/src/tui/Menu.zig b/src/tui/Menu.zig index 14f68f1..284dc03 100644 --- a/src/tui/Menu.zig +++ b/src/tui/Menu.zig @@ -56,6 +56,7 @@ pub fn Options(context: type) type { pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) !*State(ctx_type) { const self = try allocator.create(State(ctx_type)); + errdefer allocator.destroy(self); const container = try WidgetList.createH(allocator, parent, @typeName(@This()), .dynamic); self.* = .{ .allocator = allocator, diff --git a/src/tui/ModalBackground.zig b/src/tui/ModalBackground.zig index 63444c0..4bc1630 100644 --- a/src/tui/ModalBackground.zig +++ b/src/tui/ModalBackground.zig @@ -59,6 +59,7 @@ pub fn Options(context: type) type { pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Widget, opts: Options(ctx_type)) !*State(ctx_type) { const self = try allocator.create(State(ctx_type)); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = parent.plane.*, diff --git a/src/tui/Widget.zig b/src/tui/Widget.zig index 40060f6..0f0c96f 100644 --- a/src/tui/Widget.zig +++ b/src/tui/Widget.zig @@ -224,6 +224,7 @@ pub fn hover(self: *Self) bool { pub fn empty(allocator: Allocator, parent: Plane, layout_: Layout) !Self { const child: type = struct { plane: Plane, layout: Layout }; const widget = try allocator.create(child); + errdefer allocator.destroy(widget); const n = try Plane.init(&(Box{}).opts("empty"), parent); widget.* = .{ .plane = n, .layout = layout_ }; return .{ diff --git a/src/tui/WidgetList.zig b/src/tui/WidgetList.zig index 98808a9..1024ece 100644 --- a/src/tui/WidgetList.zig +++ b/src/tui/WidgetList.zig @@ -32,21 +32,24 @@ after_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on 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) error{OutOfMemory}!*Self { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = try init(allocator, parent, name, .horizontal, layout_, Box{}); self.plane.hide(); return self; } pub fn createV(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) !*Self { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = try init(allocator, parent, name, .vertical, layout_, Box{}); self.plane.hide(); return self; } pub fn createBox(allocator: Allocator, parent: Plane, name: [:0]const u8, dir: Direction, layout_: Layout, box: Box) !*Self { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = try init(allocator, parent, name, dir, layout_, box); self.plane.hide(); return self; diff --git a/src/tui/editor.zig b/src/tui/editor.zig index c4bd75b..dd4c0b4 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -5941,7 +5941,8 @@ pub const EditorWidget = struct { fn create(allocator: Allocator, parent: Plane, buffer_manager: *Buffer.Manager) !Widget { const container = try WidgetList.createH(allocator, parent, "editor.container", .dynamic); - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); try self.init(allocator, container.plane, buffer_manager); try self.commands.init(&self.editor); const editorWidget = Widget.to(self); diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index f8045d8..77ed19b 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -43,7 +43,8 @@ const Kind = enum { insert, modified, delete }; const Symbol = struct { kind: Kind, line: usize }; pub fn create(allocator: Allocator, parent: Widget, event_source: Widget, editor: *ed.Editor) !Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*), diff --git a/src/tui/filelist_view.zig b/src/tui/filelist_view.zig index 4465e8a..6f4e824 100644 --- a/src/tui/filelist_view.zig +++ b/src/tui/filelist_view.zig @@ -47,7 +47,8 @@ const Entry = struct { }; pub fn create(allocator: Allocator, parent: Plane) !Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(name), parent), diff --git a/src/tui/home.zig b/src/tui/home.zig index 42406d0..e00a16a 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -81,7 +81,8 @@ const Self = @This(); pub fn create(allocator: std.mem.Allocator, parent: Widget) !Widget { const logger = log.logger("home"); - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*); errdefer n.deinit(); @@ -303,6 +304,8 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { self.position_menu(self.v_center(5, self.menu_len, 5), self.center(x, self.menu_w)); } + if (self.plane.dim_y() < 3 or self.plane.dim_x() < root.version.len + 4) return false; + self.plane.cursor_move_yx( @intCast(self.plane.dim_y() - 2), @intCast(@max(self.plane.dim_x(), root.version.len + 3) - root.version.len - 3), @@ -311,6 +314,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { _ = self.plane.print("{s}", .{root.version}) catch return false; if (builtin.mode == .Debug) { const debug_warning_text = "debug build"; + if (self.plane.dim_y() < 4 or self.plane.dim_x() < debug_warning_text.len + 4) return false; self.plane.cursor_move_yx( @intCast(self.plane.dim_y() - 3), @intCast(@max(self.plane.dim_x(), debug_warning_text.len + 3) - debug_warning_text.len - 3), diff --git a/src/tui/info_view.zig b/src/tui/info_view.zig index de43f50..f67e2df 100644 --- a/src/tui/info_view.zig +++ b/src/tui/info_view.zig @@ -14,7 +14,8 @@ view_rows: usize = 0, lines: std.ArrayList([]const u8), pub fn create(allocator: Allocator, parent: Plane) !Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(name), parent), diff --git a/src/tui/inputview.zig b/src/tui/inputview.zig index a8be882..a955189 100644 --- a/src/tui/inputview.zig +++ b/src/tui/inputview.zig @@ -30,9 +30,10 @@ const Entry = struct { const Buffer = ArrayList(Entry); pub fn create(allocator: Allocator, parent: Plane) !Widget { - const self: *Self = try allocator.create(Self); var n = try Plane.init(&(Widget.Box{}).opts_vscroll(@typeName(Self)), parent); errdefer n.deinit(); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .parent = parent, diff --git a/src/tui/inspector_view.zig b/src/tui/inspector_view.zig index 967648e..6d314d7 100644 --- a/src/tui/inspector_view.zig +++ b/src/tui/inspector_view.zig @@ -26,7 +26,8 @@ const Self = @This(); pub fn create(allocator: Allocator, parent: Plane) !Widget { const editor = tui.get_active_editor() orelse return error.NotFound; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts_vscroll(name), parent), .editor = editor, diff --git a/src/tui/logview.zig b/src/tui/logview.zig index 02776e0..4c70457 100644 --- a/src/tui/logview.zig +++ b/src/tui/logview.zig @@ -38,7 +38,8 @@ const Level = enum { }; pub fn create(allocator: Allocator, parent: Plane) !Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(name), parent) }; return Widget.to(self); } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 7fafc9d..f32b90a 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -66,6 +66,7 @@ pub const CreateError = error{ OutOfMemory, ThespianSpawnFailed }; pub fn create(allocator: std.mem.Allocator) CreateError!Widget { const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = tui.plane(), diff --git a/src/tui/mode/mini/buffer.zig b/src/tui/mode/mini/buffer.zig index 35af902..3875d1a 100644 --- a/src/tui/mode/mini/buffer.zig +++ b/src/tui/mode/mini/buffer.zig @@ -21,7 +21,8 @@ pub fn Create(options: type) type { const Self = @This(); pub fn create(allocator: std.mem.Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .input = std.ArrayList(u8).init(allocator), diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index 4b5da15..ea6fee7 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -35,7 +35,8 @@ pub fn Create(options: type) type { }; pub fn create(allocator: std.mem.Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .file_path = std.ArrayList(u8).init(allocator), diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index 30b4976..e04adc0 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -28,7 +28,8 @@ commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { const editor = tui.get_active_editor() orelse return error.NotFound; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .input_ = ArrayList(u8).init(allocator), diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index 4680d22..839cd1b 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -25,7 +25,8 @@ last_input: []u8 = "", commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator }; try self.commands.init(self); if (tui.get_active_selection(self.allocator)) |text| { diff --git a/src/tui/mode/mini/goto.zig b/src/tui/mode/mini/goto.zig index c71a0bd..94d5c6d 100644 --- a/src/tui/mode/mini/goto.zig +++ b/src/tui/mode/mini/goto.zig @@ -24,8 +24,9 @@ start: usize, commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { - const self: *Self = try allocator.create(Self); const editor = tui.get_active_editor() orelse return error.NotFound; + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .start = editor.get_primary().cursor.row + 1, diff --git a/src/tui/mode/mini/move_to_char.zig b/src/tui/mode/mini/move_to_char.zig index 950b35e..d32f00c 100644 --- a/src/tui/mode/mini/move_to_char.zig +++ b/src/tui/mode/mini/move_to_char.zig @@ -38,7 +38,8 @@ pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tu const direction: Direction = if (std.mem.indexOf(u8, operation_command, "_left")) |_| .left else .right; const operation: Operation = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| .select else .move else .move; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .direction = direction, diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index e6a22e9..0c5d687 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -38,7 +38,8 @@ buffer_manager: ?*BufferManager, pub fn create(allocator: std.mem.Allocator) !tui.Mode { const mv = tui.mainview() orelse return error.NotFound; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ .ctx = self }), diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index bde1217..926dc65 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -47,7 +47,8 @@ pub fn Create(options: type) type { pub fn create(allocator: std.mem.Allocator) !tui.Mode { const mv = tui.mainview() orelse return error.NotFound; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ diff --git a/src/tui/scrollbar_v.zig b/src/tui/scrollbar_v.zig index 4f83b1b..8f0911a 100644 --- a/src/tui/scrollbar_v.zig +++ b/src/tui/scrollbar_v.zig @@ -27,7 +27,8 @@ style_factory: ?*const fn (self: *Self, theme: *const Widget.Theme) Widget.Theme const Self = @This(); pub fn create(allocator: Allocator, parent: Plane, event_source: ?Widget, event_sink: EventHandler) !Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .event_sink = event_sink, diff --git a/src/tui/status/blank.zig b/src/tui/status/blank.zig index 8901fd0..50e143b 100644 --- a/src/tui/status/blank.zig +++ b/src/tui/status/blank.zig @@ -20,7 +20,8 @@ pub fn Create(comptime layout_: Widget.Layout) @import("widget.zig").CreateFunct break :blk Widget.Layout{ .static = size }; } else break :blk layout_; } else layout_; - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .layout_ = layout__, diff --git a/src/tui/status/clock.zig b/src/tui/status/clock.zig index d6e8391..fe5b6cd 100644 --- a/src/tui/status/clock.zig +++ b/src/tui/status/clock.zig @@ -30,7 +30,8 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event return error.WidgetInitFailed; }; defer env.deinit(); - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), diff --git a/src/tui/status/keybindstate.zig b/src/tui/status/keybindstate.zig index 3253c19..ea6e709 100644 --- a/src/tui/status/keybindstate.zig +++ b/src/tui/status/keybindstate.zig @@ -12,7 +12,8 @@ plane: Plane, const Self = @This(); pub fn create(allocator: std.mem.Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), diff --git a/src/tui/status/keystate.zig b/src/tui/status/keystate.zig index ca674a8..afa9ff3 100644 --- a/src/tui/status/keystate.zig +++ b/src/tui/status/keystate.zig @@ -31,7 +31,8 @@ pub const width = idle_msg.len + 20; pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { const frame_rate = tp.env.get().num("frame-rate"); - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .wipe_after_frames = @divTrunc(frame_rate, 2), diff --git a/src/tui/status/minilog.zig b/src/tui/status/minilog.zig index a2644fb..400ac60 100644 --- a/src/tui/status/minilog.zig +++ b/src/tui/status/minilog.zig @@ -27,7 +27,8 @@ const Level = enum { }; pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .msg = std.ArrayList(u8).init(allocator), diff --git a/src/tui/status/modstate.zig b/src/tui/status/modstate.zig index 4555cef..2136e7c 100644 --- a/src/tui/status/modstate.zig +++ b/src/tui/status/modstate.zig @@ -20,7 +20,8 @@ const Self = @This(); pub const width = 8; pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), }; diff --git a/src/tui/status/selectionstate.zig b/src/tui/status/selectionstate.zig index f606a5c..6e0fec9 100644 --- a/src/tui/status/selectionstate.zig +++ b/src/tui/status/selectionstate.zig @@ -19,7 +19,8 @@ on_event: ?EventHandler, const Self = @This(); pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { - const self: *Self = try allocator.create(Self); + const self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .on_event = event_handler, diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 45a2b69..f3af3d1 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -54,6 +54,7 @@ pub const Style = @"style.config"; pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { const self = try allocator.create(TabBar); + errdefer allocator.destroy(self); self.* = try TabBar.init(allocator, parent, event_handler); return Widget.to(self); } @@ -447,6 +448,7 @@ const spacer = struct { event_handler: ?EventHandler, ) @import("widget.zig").CreateError!Widget { const self: *Self = try allocator.create(Self); + errdefer allocator.destroy(self); self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .layout_ = .{ .static = self.plane.egc_chunk_width(content, 0, 1) }, diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 6472742..9b82887 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -120,6 +120,9 @@ fn init(allocator: Allocator) InitError!*Self { const frame_clock = try tp.metronome.init(frame_time); var self = try allocator.create(Self); + // don't destroy + // if tui fails it is catastrophic anyway and we don't want to cause nock-on errors + // errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .config_ = conf, diff --git a/src/walk_tree.zig b/src/walk_tree.zig index 5b9a264..ee10d22 100644 --- a/src/walk_tree.zig +++ b/src/walk_tree.zig @@ -21,6 +21,7 @@ pub fn start(a_: std.mem.Allocator, root_path_: []const u8) (SpawnError || std.f fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8) (SpawnError || std.fs.Dir.OpenError)!tp.pid { const self = try allocator.create(tree_walker); + errdefer allocator.destroy(self); self.* = .{ .allocator = allocator, .root_path = try allocator.dupe(u8, root_path),