From 5dbd39636526f091f466929d87089088df5d33a5 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 21:01:35 +0100 Subject: [PATCH 1/5] refactor: make get_next_mru_buffer_for_view more versatile --- src/tui/mainview.zig | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index f72bb06..36c0138 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1484,7 +1484,7 @@ pub fn handle_editor_event(self: *Self, editor: *ed.Editor, m: tp.message) tp.re if (try m.match(.{ "E", "close" })) { if (!self.closing_project) { - if (self.get_next_mru_buffer_same_view_only(.non_hidden)) |file_path| + if (self.get_next_mru_buffer_for_view(self.active_view, .non_hidden)) |file_path| self.show_file_async(file_path) else { if (self.views.widgets.items.len == 1) @@ -1629,6 +1629,12 @@ pub fn get_view_for_file(self: *Self, file_path: []const u8) ?usize { return null; } +pub fn get_file_for_view(self: *Self, view_: usize) ?[]const u8 { + const view = self.views.get_at(view_) orelse return null; + const editor = view.widget.get("editor") orelse return null; + return if (editor.dynamic_cast(ed.EditorWidget)) |p| p.editor.file_path else null; +} + pub fn get_active_file_path(self: *Self) ?[]const u8 { return if (self.get_active_editor()) |editor| editor.file_path orelse null else null; } @@ -1927,19 +1933,19 @@ fn send_buffer_did_open(allocator: std.mem.Allocator, buffer: *Buffer) !void { project_manager.request_vcs_id(buffer.get_file_path()) catch {}; } -fn get_next_mru_buffer_same_view_only(self: *Self, mode: enum { all, hidden, non_hidden }) ?[]const u8 { +fn get_next_mru_buffer_for_view(self: *Self, view: usize, mode: enum { all, hidden, non_hidden }) ?[]const u8 { const buffers = self.buffer_manager.list_most_recently_used(self.allocator) catch return null; defer self.allocator.free(buffers); - const active_file_path = self.get_active_file_path(); + const file_path = self.get_file_for_view(view); for (buffers) |buffer| { - if (active_file_path) |fp| if (std.mem.eql(u8, fp, buffer.get_file_path())) + if (file_path) |fp| if (std.mem.eql(u8, fp, buffer.get_file_path())) continue; if (switch (mode) { .all => false, .hidden => !buffer.hidden, .non_hidden => buffer.hidden, }) continue; - if (buffer.get_last_view() != self.active_view) + if (buffer.get_last_view() != view) continue; return buffer.get_file_path(); } From 69fec437f153c6293e5ca869010c11b6781027da Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 21:26:32 +0100 Subject: [PATCH 2/5] refactor: restore splits from session --- src/tui/mainview.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 36c0138..27afeba 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1631,7 +1631,7 @@ pub fn get_view_for_file(self: *Self, file_path: []const u8) ?usize { pub fn get_file_for_view(self: *Self, view_: usize) ?[]const u8 { const view = self.views.get_at(view_) orelse return null; - const editor = view.widget.get("editor") orelse return null; + const editor = view.get("editor") orelse return null; return if (editor.dynamic_cast(ed.EditorWidget)) |p| p.editor.file_path else null; } @@ -1911,7 +1911,15 @@ fn extract_state(self: *Self, iter: *[]const u8, mode: enum { no_project, with_p for (buffers) |buffer| if (!buffer.is_ephemeral()) send_buffer_did_open(self.allocator, buffer) catch {}; - if (editor_file_path) |file_path| + var max_last_view: usize = 0; + for (buffers) |buffer| if (buffer.get_last_view()) |view| { + max_last_view = @max(max_last_view, view); + }; + + for (0..max_last_view + 1) |view| { + if (self.get_next_mru_buffer_for_view(view, .non_hidden)) |file_path| + self.show_file_async(file_path); + } else if (editor_file_path) |file_path| if (self.buffer_manager.get_buffer_for_file(file_path)) |_| try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_path } }); } From 725a66e4e295e4bfce7d0ddc085ba10ae1de61c2 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 21:27:09 +0100 Subject: [PATCH 3/5] feat: add close_splits command --- src/tui/mainview.zig | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 27afeba..5f5e3a7 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -940,6 +940,18 @@ const cmds = struct { } pub const close_split_meta: Meta = .{ .description = "Close split view" }; + pub fn close_splits(self: *Self, _: Ctx) Result { + while (self.views.widgets.items.len > 1) + try self.remove_view(1); + + if (self.closing_project) return; + + const buffers = try self.buffer_manager.list_unordered(self.allocator); + defer self.allocator.free(buffers); + for (buffers) |buffer| buffer.set_last_view(0); + } + pub const close_splits_meta: Meta = .{ .description = "Close all split views" }; + pub fn focus_split(self: *Self, ctx: Ctx) Result { var n: usize = undefined; if (!try ctx.args.match(.{tp.extract(&n)})) return error.InvalidFocusSplitArgument; @@ -1699,15 +1711,20 @@ pub fn focus_view(self: *Self, n: usize) !void { if (self.views.get_at(self.active_view)) |view| view.focus(); } -fn remove_active_view(self: *Self) !void { +fn remove_view(self: *Self, view: usize) !void { if (self.views.widgets.items.len == 1) return; // can't delete last view - self.views.delete(self.active_view); + if (view >= self.views.widgets.items.len) return; + self.views.delete(view); if (self.active_view >= self.views.widgets.items.len) self.active_view = self.views.widgets.items.len - 1; - if (self.views.get_at(self.active_view)) |view| view.focus(); + if (self.views.get_at(self.active_view)) |active_view| active_view.focus(); tui.resize(); } +fn remove_active_view(self: *Self) !void { + return self.remove_view(self.active_view); +} + fn replace_active_view(self: *Self, widget: Widget) !void { const n = self.active_view; if (self.views.get_at(n)) |view| view.unfocus(); From 29e8b4293df7076ba51623907979cb58d4bf194a Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 21:27:20 +0100 Subject: [PATCH 4/5] fix: close splits when changing projects --- src/tui/mainview.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 5f5e3a7..e67407f 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -460,6 +460,7 @@ const cmds = struct { { self.closing_project = true; defer self.closing_project = false; + try close_splits(self, .{}); try self.close_all_editors(); self.delete_all_buffers(); self.clear_find_in_files_results(.diagnostics); From bfefe2f99d5d42a1919c798ce0e2f5832cdacc9a Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 21:31:02 +0100 Subject: [PATCH 5/5] fix: reset buffer.last_view when removing splits --- src/tui/mainview.zig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index e67407f..443c100 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -937,7 +937,15 @@ const cmds = struct { pub fn close_split(self: *Self, _: Ctx) Result { if (self.views.widgets.items.len == 1 and self.views.widgets.items[0].widget.dynamic_cast(home) != null) return command.executeName("quit", .{}); - return self.remove_active_view(); + try self.remove_active_view(); + + if (self.closing_project) return; + + const buffers = try self.buffer_manager.list_unordered(self.allocator); + defer self.allocator.free(buffers); + for (buffers) |buffer| if (buffer.get_last_view()) |view| + if (view >= self.views.widgets.items.len) + buffer.set_last_view(null); } pub const close_split_meta: Meta = .{ .description = "Close split view" };