hx: Fix closing other buffers and improve user messages

This commit is contained in:
Igor Támara 2025-10-06 13:06:22 -05:00 committed by CJ van den Berg
parent e41ff1b7a5
commit f201728457
3 changed files with 45 additions and 27 deletions

View file

@ -134,17 +134,17 @@ pub fn is_dirty(self: *const Self) bool {
return false; return false;
} }
pub fn number_of_dirties(self: *const Self) usize { pub fn count_dirty_buffers(self: *const Self) usize {
var dirties: usize = 0; var count: usize = 0;
var i = self.buffers.iterator(); var i = self.buffers.iterator();
while (i.next()) |p| { while (i.next()) |p| {
const buffer = p.value_ptr.*; const buffer = p.value_ptr.*;
if (!buffer.is_ephemeral() and buffer.is_dirty()) { 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 { 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(); self.buffers.clearRetainingCapacity();
} }
pub fn delete_others(self: *Self, protected: *Buffer) void { pub fn delete_others(self: *Self, protected: *Buffer) error{OutOfMemory}!void {
var i = self.buffers.iterator(); var keys = try std.ArrayList(*[]const u8).initCapacity(self.allocator, self.buffers.size);
while (i.next()) |p| { defer keys.deinit(self.allocator);
var it = self.buffers.iterator();
while (it.next()) |p| {
const buffer = p.value_ptr.*; const buffer = p.value_ptr.*;
if (buffer != protected) { if (buffer != protected) {
buffer.reset_to_last_saved(); try keys.append(self.allocator, p.key_ptr);
self.close_buffer(buffer);
} }
} }
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 remaining: usize = 0;
var i = self.buffers.iterator(); var keys = try std.ArrayList(*[]const u8).initCapacity(self.allocator, self.buffers.size);
while (i.next()) |p| { defer keys.deinit(self.allocator);
var it = self.buffers.iterator();
while (it.next()) |p| {
const buffer = p.value_ptr.*; const buffer = p.value_ptr.*;
if (buffer != protected) { if (buffer != protected) {
if (buffer.is_ephemeral() or !buffer.is_dirty()) { if (buffer.is_ephemeral() or !buffer.is_dirty()) {
_ = self.buffers.remove(buffer.get_file_path()); try keys.append(self.allocator, p.key_ptr);
buffer.deinit(); } else {
} else remaining += 1; remaining += 1;
}
} }
} }
for (keys.items) |k| {
const buffer = self.buffers.get(k.*) orelse continue;
_ = self.buffers.remove(k.*);
buffer.deinit();
}
return remaining; return remaining;
} }

View file

@ -276,8 +276,8 @@ const cmds = struct {
const logger = log.logger("buffer"); const logger = log.logger("buffer");
defer logger.deinit(); defer logger.deinit();
self.check_all_not_dirty() catch |err| { self.check_all_not_dirty() catch |err| {
const dirties = self.buffer_manager.number_of_dirties(); const count_dirty_buffers = self.buffer_manager.count_dirty_buffers();
logger.print("There are {} unsaved buffer(s), use 'quit without saving' if not needed to save them", .{dirties}); logger.print("{} unsaved buffer(s), use 'quit without saving' to exit", .{count_dirty_buffers});
return err; return err;
}; };
try tp.self_pid().send("quit"); try tp.self_pid().send("quit");

View file

@ -63,7 +63,7 @@ const cmds_ = struct {
pub fn @"x!"(_: *void, _: Ctx) Result { pub fn @"x!"(_: *void, _: Ctx) Result {
try cmd("save_file", command.fmt(.{ "then", .{ "quit_without_saving", .{} } })); 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 { pub fn x(_: *void, _: Ctx) Result {
try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } }));
@ -90,7 +90,7 @@ const cmds_ = struct {
try cmd("quit_without_saving", .{}); 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 { pub fn wqa(_: *void, _: Ctx) Result {
if (tui.get_buffer_manager()) |bm| if (tui.get_buffer_manager()) |bm|
@ -105,18 +105,18 @@ const cmds_ = struct {
try cmd("quit_without_saving", .{}); 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 { pub fn rl(_: *void, _: Ctx) Result {
try cmd("reload_file", .{}); 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 { pub fn rla(_: *void, _: Ctx) Result {
if (tui.get_buffer_manager()) |bm| if (tui.get_buffer_manager()) |bm|
bm.reload_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); 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 { pub fn o(_: *void, _: Ctx) Result {
try cmd("open_file", .{}); try cmd("open_file", .{});
@ -152,15 +152,15 @@ const cmds_ = struct {
pub fn @"bc!"(_: *void, _: Ctx) Result { pub fn @"bc!"(_: *void, _: Ctx) Result {
try cmd("close_file_without_saving", .{}); 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 { pub fn @"bco!"(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
if (tui.get_buffer_manager()) |bm| { 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 { pub fn bco(_: *void, _: Ctx) Result {
const logger = log.logger("helix-mode"); const logger = log.logger("helix-mode");
@ -168,13 +168,14 @@ const cmds_ = struct {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
const bm = tui.get_buffer_manager() orelse return; const bm = tui.get_buffer_manager() orelse return;
if (mv.get_active_buffer()) |buffer| { if (mv.get_active_buffer()) |buffer| {
const remaining = bm.close_others(buffer); const remaining = try bm.close_others(buffer);
if (remaining > 0) { if (remaining > 0) {
logger.print("{} unsaved buffer(s) remaining", .{remaining}); 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 { pub fn save_selection(_: *void, _: Ctx) Result {
const logger = log.logger("helix-mode"); const logger = log.logger("helix-mode");