From 88ca1cd3436624bf5fe8b9f90c7cfae811d7733b Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 9 Feb 2025 20:39:32 +0100 Subject: [PATCH] feat(project): add `close_project` command bound to `del` in recent projects list --- src/project_manager.zig | 17 +++++++++++++++++ src/tui/mainview.zig | 15 +++++++++++++++ src/tui/mode/overlay/open_recent_project.zig | 14 ++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/project_manager.zig b/src/project_manager.zig index 01aaf4d..2795012 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -64,6 +64,12 @@ pub fn open(rel_project_directory: []const u8) (ProjectManagerError || FileSyste return send(.{ "open", project_directory }); } +pub fn close(project_directory: []const u8) (ProjectManagerError || error{CloseCurrentProject})!void { + const current_project = tp.env.get().str("project"); + if (std.mem.eql(u8, current_project, project_directory)) return error.CloseCurrentProject; + return send(.{ "close", project_directory }); +} + pub fn request_n_most_recent_file(allocator: std.mem.Allocator, n: usize) (CallError || ProjectError || cbor.Error)!?[]const u8 { const project = tp.env.get().str("project"); if (project.len == 0) @@ -339,6 +345,8 @@ const Process = struct { self.logger.print_err("lsp-handling", "child '{s}' terminated", .{path}); } else if (try cbor.match(m.buf, .{ "open", tp.extract(&project_directory) })) { self.open(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; + } else if (try cbor.match(m.buf, .{ "close", tp.extract(&project_directory) })) { + self.close(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "request_n_most_recent_file", tp.extract(&project_directory), tp.extract(&n) })) { self.request_n_most_recent_file(from, project_directory, n) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) { @@ -412,6 +420,15 @@ const Process = struct { } } + fn close(self: *Process, project_directory: []const u8) error{}!void { + if (self.projects.fetchRemove(project_directory)) |kv| { + self.allocator.free(kv.key); + kv.value.deinit(); + self.allocator.destroy(kv.value); + self.logger.print("closed: {s}", .{project_directory}); + } + } + fn loaded(self: *Process, project_directory: []const u8) OutOfMemoryError!void { const project = self.projects.get(project_directory) orelse return; try project.merge_pending_files(); diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index b4d588f..66fd364 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -274,6 +274,21 @@ const cmds = struct { } pub const open_project_dir_meta = .{ .arguments = &.{.string} }; + pub fn close_project(_: *Self, ctx: Ctx) Result { + var project_dir: []const u8 = undefined; + if (!try ctx.args.match(.{tp.extract(&project_dir)})) + return; + project_manager.close(project_dir) catch |e| switch (e) { + error.CloseCurrentProject => { + const logger = log.logger("project"); + defer logger.deinit(); + logger.print_err("project", "cannot close current project", .{}); + }, + else => return e, + }; + } + pub const close_project_meta: Meta = .{ .arguments = &.{.string} }; + pub fn change_project(self: *Self, ctx: Ctx) Result { var project_dir: []const u8 = undefined; if (!try ctx.args.match(.{tp.extract(&project_dir)})) diff --git a/src/tui/mode/overlay/open_recent_project.zig b/src/tui/mode/overlay/open_recent_project.zig index 5aeea38..d9fd219 100644 --- a/src/tui/mode/overlay/open_recent_project.zig +++ b/src/tui/mode/overlay/open_recent_project.zig @@ -2,8 +2,10 @@ const std = @import("std"); const cbor = @import("cbor"); const tp = @import("thespian"); const project_manager = @import("project_manager"); +const command = @import("command"); pub const Type = @import("palette.zig").Create(@This()); +const module_name = @typeName(@This()); pub const label = "Search projects"; pub const name = " project"; @@ -44,6 +46,10 @@ pub fn load_entries(palette: *Type) !usize { return 1; } +pub fn clear_entries(palette: *Type) void { + palette.entries.clearRetainingCapacity(); +} + pub fn add_menu_entry(palette: *Type, entry: *Entry, matches: ?[]const usize) !void { var value = std.ArrayList(u8).init(palette.allocator); defer value.deinit(); @@ -62,3 +68,11 @@ fn select(menu: **Type.MenuState, button: *Type.ButtonState) void { tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("open_recent_project", e); tp.self_pid().send(.{ "cmd", "change_project", .{name_} }) catch |e| menu.*.opts.ctx.logger.err("open_recent_project", e); } + +pub fn delete_item(menu: *Type.MenuState, button: *Type.ButtonState) bool { + var name_: []const u8 = undefined; + var iter = button.opts.label; + if (!(cbor.matchString(&iter, &name_) catch false)) return false; + command.executeName("close_project", command.fmt(.{name_})) catch |e| menu.*.opts.ctx.logger.err(module_name, e); + return true; //refresh list +}