From f201728457aae12a7ba1823a79a8dce7179eafe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20T=C3=A1mara?= Date: Mon, 6 Oct 2025 13:06:22 -0500 Subject: [PATCH] hx: Fix closing other buffers and improve user messages --- src/buffer/Manager.zig | 47 ++++++++++++++++++++++++++++-------------- src/tui/mainview.zig | 4 ++-- src/tui/mode/helix.zig | 21 ++++++++++--------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index 1e22ed6..309b25d 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -134,17 +134,17 @@ pub fn is_dirty(self: *const Self) bool { return false; } -pub fn number_of_dirties(self: *const Self) usize { - var dirties: usize = 0; +pub fn count_dirty_buffers(self: *const Self) usize { + var count: usize = 0; var i = self.buffers.iterator(); while (i.next()) |p| { const buffer = p.value_ptr.*; if (!buffer.is_ephemeral() and buffer.is_dirty()) { - dirties += 1; + count += 1; } } - return dirties; + return count; } pub fn is_buffer_dirty(self: *const Self, file_path: []const u8) bool { @@ -182,29 +182,46 @@ pub fn delete_all(self: *Self) void { self.buffers.clearRetainingCapacity(); } -pub fn delete_others(self: *Self, protected: *Buffer) void { - var i = self.buffers.iterator(); - while (i.next()) |p| { +pub fn delete_others(self: *Self, protected: *Buffer) error{OutOfMemory}!void { + var keys = try std.ArrayList(*[]const u8).initCapacity(self.allocator, self.buffers.size); + defer keys.deinit(self.allocator); + + var it = self.buffers.iterator(); + + while (it.next()) |p| { const buffer = p.value_ptr.*; if (buffer != protected) { - buffer.reset_to_last_saved(); - self.close_buffer(buffer); + try keys.append(self.allocator, p.key_ptr); } } + for (keys.items) |k| { + const buffer = self.buffers.get(k.*) orelse continue; + _ = self.buffers.remove(k.*); + buffer.deinit(); + } } -pub fn close_others(self: *Self, protected: *Buffer) usize { +pub fn close_others(self: *Self, protected: *Buffer) error{OutOfMemory}!usize { var remaining: usize = 0; - var i = self.buffers.iterator(); - while (i.next()) |p| { + var keys = try std.ArrayList(*[]const u8).initCapacity(self.allocator, self.buffers.size); + defer keys.deinit(self.allocator); + + var it = self.buffers.iterator(); + while (it.next()) |p| { const buffer = p.value_ptr.*; if (buffer != protected) { if (buffer.is_ephemeral() or !buffer.is_dirty()) { - _ = self.buffers.remove(buffer.get_file_path()); - buffer.deinit(); - } else remaining += 1; + try keys.append(self.allocator, p.key_ptr); + } else { + remaining += 1; + } } } + for (keys.items) |k| { + const buffer = self.buffers.get(k.*) orelse continue; + _ = self.buffers.remove(k.*); + buffer.deinit(); + } return remaining; } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index def6ac9..0a53846 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -276,8 +276,8 @@ const cmds = struct { const logger = log.logger("buffer"); defer logger.deinit(); self.check_all_not_dirty() catch |err| { - const dirties = self.buffer_manager.number_of_dirties(); - logger.print("There are {} unsaved buffer(s), use 'quit without saving' if not needed to save them", .{dirties}); + const count_dirty_buffers = self.buffer_manager.count_dirty_buffers(); + logger.print("{} unsaved buffer(s), use 'quit without saving' to exit", .{count_dirty_buffers}); return err; }; try tp.self_pid().send("quit"); diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index 6ba7c03..9e1abe9 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -63,7 +63,7 @@ const cmds_ = struct { pub fn @"x!"(_: *void, _: Ctx) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit_without_saving", .{} } })); } - pub const @"x!_meta": Meta = .{ .description = "x! (write/save file and close forcefully, ignoring other unsaved changes)" }; + pub const @"x!_meta": Meta = .{ .description = "x! (write/save file and exit, ignoring other unsaved changes)" }; pub fn x(_: *void, _: Ctx) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); @@ -90,7 +90,7 @@ const cmds_ = struct { try cmd("quit_without_saving", .{}); } } - pub const @"xa!_meta": Meta = .{ .description = "xa! (write all and quit forcefully, ignoring unsaved changes)" }; + pub const @"xa!_meta": Meta = .{ .description = "xa! (write all and exit, ignoring other unsaved changes)" }; pub fn wqa(_: *void, _: Ctx) Result { if (tui.get_buffer_manager()) |bm| @@ -105,18 +105,18 @@ const cmds_ = struct { try cmd("quit_without_saving", .{}); } } - pub const @"wqa!_meta": Meta = .{ .description = "wqa! (write all and quit forcefully, ignoring unsaved changes)" }; + pub const @"wqa!_meta": Meta = .{ .description = "wqa! (write all and exit, ignoring unsaved changes)" }; pub fn rl(_: *void, _: Ctx) Result { try cmd("reload_file", .{}); } - pub const rl_meta: Meta = .{ .description = "rl (force reload current file)" }; + pub const rl_meta: Meta = .{ .description = "rl (reload current file)" }; pub fn rla(_: *void, _: Ctx) Result { if (tui.get_buffer_manager()) |bm| bm.reload_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); } - pub const rla_meta: Meta = .{ .description = "rla (reload all files discarding the current contents)" }; + pub const rla_meta: Meta = .{ .description = "rla (reload all files)" }; pub fn o(_: *void, _: Ctx) Result { try cmd("open_file", .{}); @@ -152,15 +152,15 @@ const cmds_ = struct { pub fn @"bc!"(_: *void, _: Ctx) Result { try cmd("close_file_without_saving", .{}); } - pub const @"bc!_meta": Meta = .{ .description = "bc! (Close buffer/tab forcefully, ignoring changes)" }; + pub const @"bc!_meta": Meta = .{ .description = "bc! (Close buffer/tab, ignoring changes)" }; pub fn @"bco!"(_: *void, _: Ctx) Result { const mv = tui.mainview() orelse return; if (tui.get_buffer_manager()) |bm| { - if (mv.get_active_buffer()) |buffer| bm.delete_others(buffer); + if (mv.get_active_buffer()) |buffer| try bm.delete_others(buffer); } } - pub const @"bco!_meta": Meta = .{ .description = "bco! (Close other buffers/tabs forcefully, ignoring changes)" }; + pub const @"bco!_meta": Meta = .{ .description = "bco! (Close other buffers/tabs, discarding changes)" }; pub fn bco(_: *void, _: Ctx) Result { const logger = log.logger("helix-mode"); @@ -168,13 +168,14 @@ const cmds_ = struct { const mv = tui.mainview() orelse return; const bm = tui.get_buffer_manager() orelse return; if (mv.get_active_buffer()) |buffer| { - const remaining = bm.close_others(buffer); + const remaining = try bm.close_others(buffer); if (remaining > 0) { logger.print("{} unsaved buffer(s) remaining", .{remaining}); + try cmd("next_tab", .{}); } } } - pub const bco_meta: Meta = .{ .description = "bco (Close other buffers/tabs, except this one)" }; + pub const bco_meta: Meta = .{ .description = "bco (Close other buffers/tabs)" }; pub fn save_selection(_: *void, _: Ctx) Result { const logger = log.logger("helix-mode");