From b1cf854ed4219f9027842bc3c9237460eeb53bea Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 19:54:25 +0100 Subject: [PATCH 1/8] fix: location_update should use editor that generated the event --- src/tui/mainview.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 4360507..ce5ea6e 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1478,7 +1478,7 @@ pub fn handle_editor_event(self: *Self, editor: *ed.Editor, m: tp.message) tp.re var sel: ed.Selection = undefined; if (try m.match(.{ "E", "location", tp.more })) - return self.location_update(m); + return self.location_update(editor, m); if (try m.match(.{ "E", "close" })) { if (!self.closing_project) { @@ -1511,11 +1511,11 @@ pub fn handle_editor_event(self: *Self, editor: *ed.Editor, m: tp.message) tp.re } } -pub fn location_update(self: *Self, m: tp.message) tp.result { +pub fn location_update(self: *Self, editor: *ed.Editor, m: tp.message) tp.result { var row: usize = 0; var col: usize = 0; - const file_path = self.get_active_file_path() orelse return; - const ephemeral = if (self.get_active_buffer()) |buffer| buffer.is_ephemeral() else false; + const file_path = editor.file_path orelse return; + const ephemeral = if (editor.buffer) |buffer| buffer.is_ephemeral() else false; if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col) })) { if (row == 0 and col == 0) return; From 94e6965ad083d736e792c84af91b7ee0b04680ba Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:15:59 +0100 Subject: [PATCH 2/8] refactor: add Buffer.last_view field --- src/buffer/Buffer.zig | 11 +++++++++++ src/buffer/Manager.zig | 1 + 2 files changed, 12 insertions(+) diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index a5b2b16..9374a1a 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -49,6 +49,7 @@ meta: ?[]const u8 = null, lsp_version: usize = 1, vcs_id: ?[]const u8 = null, vcs_content: ?ArrayList(u8) = null, +last_view: ?usize = null, undo_head: ?*UndoNode = null, redo_head: ?*UndoNode = null, @@ -1241,6 +1242,14 @@ pub inline fn get_file_path(self: *const Self) []const u8 { return self.file_path_buf.items; } +pub fn set_last_view(self: *Self, last_view: ?usize) void { + self.last_view = last_view; +} + +pub fn get_last_view(self: *Self) ?usize { + return self.last_view; +} + pub fn set_vcs_id(self: *Self, vcs_id: []const u8) error{OutOfMemory}!bool { if (self.vcs_id) |old_id| { if (std.mem.eql(u8, old_id, vcs_id)) return false; @@ -1687,6 +1696,7 @@ pub fn write_state(self: *const Self, writer: *std.Io.Writer) error{ Stop, OutOf self.hidden, self.ephemeral, self.auto_save, + self.last_view, dirty, self.meta, self.file_type_name, @@ -1712,6 +1722,7 @@ pub fn extract_state(self: *Self, iter: *[]const u8) !void { cbor.extract(&self.hidden), cbor.extract(&self.ephemeral), cbor.extract(&self.auto_save), + cbor.extract(&self.last_view), cbor.extract(&dirty), cbor.extract(&meta), cbor.extract(&file_type_name), diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index be4075f..0fc469e 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -112,6 +112,7 @@ pub fn retire(_: *Self, buffer: *Buffer, meta: ?[]const u8) void { pub fn close_buffer(self: *Self, buffer: *Buffer) void { buffer.hidden = true; + buffer.set_last_view(null); tp.trace(tp.channel.debug, .{ "buffer", "close", buffer.get_file_path(), "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); if (buffer.is_ephemeral()) self.delete_buffer(buffer); From 1263d3c9e81e4582a85954224903709e61e3fc8c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:16:37 +0100 Subject: [PATCH 3/8] refactor: update Buffer.last_view when navigating --- src/tui/mainview.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index ce5ea6e..b7fce23 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1534,6 +1534,8 @@ pub fn location_update_from_editor(self: *Self) void { const editor = self.get_active_editor() orelse return; const file_path = editor.file_path orelse return; const ephemeral = if (editor.buffer) |buffer| buffer.is_ephemeral() else false; + if (editor.buffer) |buffer| + buffer.set_last_view(self.active_view); const primary = editor.get_primary(); const row: usize = primary.cursor.row; const col: usize = primary.cursor.col; From 36e441e762ab16a18d0ef84b2dfdd2bd6c8ce2f5 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:17:02 +0100 Subject: [PATCH 4/8] refactor: load buffers into the last view used for them --- src/tui/mainview.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index b7fce23..571b38b 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -582,7 +582,9 @@ const cmds = struct { fn navigate_complete(self: *Self, view: ?usize, f: []const u8, goto_args: []const u8, line: ?i64, column: ?i64, offset: ?i64) Result { if (view) |n| try self.focus_view(n); - if (view == null) { + const different_file = if (self.get_active_file_path()) |active_file_path| !std.mem.eql(u8, active_file_path, f) else true; + + if (view == null or different_file) { if (self.get_active_editor()) |editor| { editor.send_editor_jump_source() catch {}; } @@ -1618,6 +1620,8 @@ pub fn get_view_for_file(self: *Self, file_path: []const u8) ?usize { if (std.mem.eql(u8, p.editor.file_path orelse continue, file_path)) return n; } + if (self.buffer_manager.get_buffer_for_file(file_path)) |buffer| + return buffer.get_last_view(); return null; } From 51b2509f22116fd073e87ff74616da8188ea3086 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:19:46 +0100 Subject: [PATCH 5/8] refactor: drop late diff responses --- src/tui/tui.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 2061852..f31369e 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -543,6 +543,9 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{ "PRJ", tp.more })) // drop late project manager query responses return; + if (try m.match(.{ "DIFF", tp.more })) // drop late diff responses + return; + if (try m.match(.{"INPUT_IDLE"})) { if (self.input_idle_timer) |*t| t.deinit(); self.input_idle_timer = null; From cf7c54aa63874b5fec06ede62b9e17520f565c62 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:30:32 +0100 Subject: [PATCH 6/8] refactor: make get_next_mru_buffer prefer buffers from current split --- src/tui/mainview.zig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 571b38b..facb088 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1927,6 +1927,18 @@ fn get_next_mru_buffer(self: *Self, mode: enum { all, hidden, non_hidden }) ?[]c 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(); + for (buffers) |buffer| { + if (active_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) + continue; + return buffer.get_file_path(); + } for (buffers) |buffer| { if (active_file_path) |fp| if (std.mem.eql(u8, fp, buffer.get_file_path())) continue; From 86a516630e1632e2e674ac41956ad31ab523a583 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:41:49 +0100 Subject: [PATCH 7/8] fix: clean-up empty splits when closing buffers --- src/tui/mainview.zig | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index facb088..f72bb06 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1484,10 +1484,14 @@ 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(.non_hidden)) |file_path| + if (self.get_next_mru_buffer_same_view_only(.non_hidden)) |file_path| self.show_file_async(file_path) - else - self.show_home_async(); + else { + if (self.views.widgets.items.len == 1) + self.show_home_async() + else + tp.self_pid().send(.{ "cmd", "close_split", .{} }) catch return; + } } else self.show_home_async(); return; } @@ -1923,6 +1927,25 @@ 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 { + 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(); + for (buffers) |buffer| { + if (active_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) + continue; + return buffer.get_file_path(); + } + return null; +} + fn get_next_mru_buffer(self: *Self, 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); From d3ae5e0e092184eec277ada2b161658301993b64 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 14 Jan 2026 20:50:15 +0100 Subject: [PATCH 8/8] fix: reset input mode when focusing views --- src/tui/editor.zig | 2 ++ src/tui/home.zig | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index cd4c823..0c609c7 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -6835,12 +6835,14 @@ pub const EditorWidget = struct { if (self.focused) return; self.commands.register() catch @panic("editor.commands.register"); self.focused = true; + command.executeName("enter_mode_default", .{}) catch {}; self.editor.send_focus_events() catch {}; } pub fn unfocus(self: *Self) void { if (self.focused) self.commands.unregister(); self.focused = false; + command.executeName("enter_mode_default", .{}) catch {}; } pub fn update(self: *Self) void { diff --git a/src/tui/home.zig b/src/tui/home.zig index 6093c2b..b0d0c29 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -151,14 +151,16 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { } pub fn focus(self: *Self) void { - self.unfocus(); + if (self.focused) return; self.commands.register() catch @panic("home.commands.register"); self.focused = true; + command.executeName("enter_mode", command.Context.fmt(.{"home"})) catch {}; } pub fn unfocus(self: *Self) void { if (self.focused) self.commands.unregister(); self.focused = false; + command.executeName("enter_mode_default", .{}) catch {}; } fn add_menu_command(self: *Self, command_name: []const u8, description: []const u8, hint: []const u8, menu: anytype) !void {