From fc3224137d2d68ea3d88c6d7aef5d4858a45bf23 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 25 Mar 2025 15:04:23 +0100 Subject: [PATCH] fix: remove blocking project manager call on startup closes #214 The blocking call get_mru_postion to the project mananger may cause a deadlock if the project manager has not started running yet. --- src/completion.zig | 65 +++++++++++++++++++++++++++++++++++++++++ src/project_manager.zig | 11 +++---- src/tui/mainview.zig | 50 +++++++++++++++++++++++++++---- 3 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 src/completion.zig diff --git a/src/completion.zig b/src/completion.zig new file mode 100644 index 0000000..ac8a6ee --- /dev/null +++ b/src/completion.zig @@ -0,0 +1,65 @@ +const std = @import("std"); +const tp = @import("thespian"); + +const OutOfMemoryError = error{OutOfMemory}; +const SpawnError = error{ThespianSpawnFailed}; + +pub fn send( + allocator: std.mem.Allocator, + to: tp.pid_ref, + m: anytype, + ctx: anytype, +) (OutOfMemoryError || SpawnError)!void { + return RequestContext(@TypeOf(ctx)).send(allocator, to, ctx, tp.message.fmt(m)); +} + +fn RequestContext(T: type) type { + return struct { + receiver: ReceiverT, + ctx: T, + to: tp.pid, + request: tp.message, + response: ?tp.message, + a: std.mem.Allocator, + + const Self = @This(); + const ReceiverT = tp.Receiver(*@This()); + + fn send(a: std.mem.Allocator, to: tp.pid_ref, ctx: T, request: tp.message) (OutOfMemoryError || SpawnError)!void { + const self = try a.create(@This()); + self.* = .{ + .receiver = undefined, + .ctx = if (@hasDecl(T, "clone")) ctx.clone() else ctx, + .to = to.clone(), + .request = try request.clone(std.heap.c_allocator), + .response = null, + .a = a, + }; + self.receiver = ReceiverT.init(receive_, self); + const proc = try tp.spawn_link(a, self, start, @typeName(@This())); + defer proc.deinit(); + } + + fn deinit(self: *@This()) void { + if (@hasDecl(T, "deinit")) self.ctx.deinit(); + std.heap.c_allocator.free(self.request.buf); + self.to.deinit(); + self.a.destroy(self); + } + + fn start(self: *@This()) tp.result { + _ = tp.set_trap(true); + if (@hasDecl(T, "link")) try self.ctx.link(); + errdefer self.deinit(); + try self.to.link(); + try self.to.send_raw(self.request); + tp.receive(&self.receiver); + } + + fn receive_(self: *@This(), _: tp.pid_ref, m: tp.message) tp.result { + defer self.deinit(); + self.ctx.receive(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); + return tp.exit_normal(); + } + }; +} diff --git a/src/project_manager.zig b/src/project_manager.zig index cdf536f..705fedc 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -232,16 +232,13 @@ pub fn update_mru(file_path: []const u8, row: usize, col: usize, ephemeral: bool return send(.{ "update_mru", project, file_path, row, col }); } -pub fn get_mru_position(allocator: std.mem.Allocator, file_path: []const u8) (ProjectManagerError || ProjectError || CallError || cbor.Error)!?Project.FilePos { - const frame = tracy.initZone(@src(), .{ .name = "get_mru_position" }); - defer frame.deinit(); +pub fn get_mru_position(allocator: std.mem.Allocator, file_path: []const u8, ctx: anytype) (ProjectManagerError || ProjectError)!void { const project = tp.env.get().str("project"); if (project.len == 0) return error.NoProject; - const rsp = try (try get()).pid.call(allocator, request_timeout, .{ "get_mru_position", project, file_path }); - defer allocator.free(rsp.buf); - var pos: Project.FilePos = undefined; - return if (try cbor.match(rsp.buf, .{ tp.extract(&pos.row), tp.extract(&pos.col) })) pos else null; + + const cp = @import("completion.zig"); + return cp.send(allocator, (try get()).pid, .{ "get_mru_position", project, file_path }, ctx); } const Process = struct { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index dafb048..7db301e 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -115,6 +115,11 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { var end_line: usize = undefined; var end_pos: usize = undefined; var lines: []const u8 = undefined; + var same_file: bool = undefined; + var goto_args: []const u8 = undefined; + var line: i64 = undefined; + var column: i64 = undefined; + if (try m.match(.{ "REF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { try self.add_find_in_files_result(.references, path, begin_line, begin_pos, end_line, end_pos, lines, .Information); return true; @@ -139,6 +144,12 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { .end = .{ .row = end_line, .col = end_pos }, }); return true; + } else if (try m.match(.{ "navigate_complete", tp.extract(&same_file), tp.extract(&path), tp.extract(&goto_args), tp.extract(&line), tp.extract(&column) })) { + cmds.navigate_complete(self, same_file, path, goto_args, line, column) catch |e| return tp.exit_error(e, @errorReturnTrace()); + return true; + } else if (try m.match(.{ "navigate_complete", tp.extract(&same_file), tp.extract(&path), tp.extract(&goto_args), tp.null_, tp.null_ })) { + cmds.navigate_complete(self, same_file, path, goto_args, null, null) catch |e| return tp.exit_error(e, @errorReturnTrace()); + return true; } return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m); } @@ -371,12 +382,42 @@ const cmds = struct { const same_file = if (self.get_active_file_path()) |fp| std.mem.eql(u8, fp, f) else false; const have_editor_metadata = if (self.buffer_manager.get_buffer_for_file(f)) |_| true else false; - if (!same_file and !have_editor_metadata and line == null) - if (try project_manager.get_mru_position(self.allocator, f)) |pos| { - line = @intCast(pos.row); - column = @intCast(pos.col); + if (!same_file and !have_editor_metadata and line == null) { + const ctx_: struct { + allocator: std.mem.Allocator, + from: tp.pid, + same_file: bool, + path: []const u8, + goto_args: []const u8, + + pub fn deinit(ctx_: *@This()) void { + ctx_.from.deinit(); + ctx_.allocator.free(ctx_.path); + ctx_.allocator.free(ctx_.goto_args); + } + pub fn receive(ctx_: @This(), rsp: tp.message) !void { + var line_: ?i64 = null; + var column_: ?i64 = null; + _ = try cbor.match(rsp.buf, .{ tp.extract(&line_), tp.extract(&column_) }); + try ctx_.from.send(.{ "navigate_complete", ctx_.same_file, ctx_.path, ctx_.goto_args, line_, column_ }); + } + } = .{ + .allocator = self.allocator, + .from = tp.self_pid().clone(), + .same_file = same_file, + .path = try self.allocator.dupe(u8, f), + .goto_args = try self.allocator.dupe(u8, goto_args), }; + try project_manager.get_mru_position(self.allocator, f, ctx_); + return; + } + + return cmds.navigate_complete(self, same_file, f, goto_args, line, column); + } + pub const navigate_meta: Meta = .{ .arguments = &.{.object} }; + + fn navigate_complete(self: *Self, same_file: bool, f: []const u8, goto_args: []const u8, line: ?i64, column: ?i64) Result { if (!same_file) { if (self.get_active_editor()) |editor| { editor.send_editor_jump_source() catch {}; @@ -395,7 +436,6 @@ const cmds = struct { } tui.need_render(); } - pub const navigate_meta: Meta = .{ .arguments = &.{.object} }; pub fn open_help(self: *Self, _: Ctx) Result { tui.reset_drag_context();