From 71b0de0a2af8583b3918927fbd8c4fe2e05aa1bb Mon Sep 17 00:00:00 2001 From: Yanuo Ma Date: Wed, 4 Mar 2026 22:12:32 -0500 Subject: [PATCH 1/3] fix(refresh): show welcome after cross-tab commit Git watcher discarded fs_event callbacks when the codediff tab wasn't active. If the user committed from another tab, the event was lost and diff panes stayed stale on return. Set _pending_refresh flag when events fire on wrong tabpage and add TabEnter autocmd to flush pending refreshes when the user returns to the codediff tab. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lua/codediff/ui/explorer/refresh.lua | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lua/codediff/ui/explorer/refresh.lua b/lua/codediff/ui/explorer/refresh.lua index 23b5a24..0b6ae86 100644 --- a/lua/codediff/ui/explorer/refresh.lua +++ b/lua/codediff/ui/explorer/refresh.lua @@ -92,9 +92,13 @@ function M.setup_auto_refresh(explorer, tabpage) if watch_err then return end - -- Only refresh if this tabpage is current - if vim.api.nvim_get_current_tabpage() == tabpage and vim.api.nvim_tabpage_is_valid(tabpage) and not explorer.is_hidden then + if not vim.api.nvim_tabpage_is_valid(tabpage) or explorer.is_hidden then + return + end + if vim.api.nvim_get_current_tabpage() == tabpage then debounced_refresh() + else + explorer._pending_refresh = true end end) ) @@ -118,6 +122,17 @@ function M.setup_auto_refresh(explorer, tabpage) callback = cleanup, }) + -- Flush pending refresh when returning to the codediff tab + vim.api.nvim_create_autocmd("TabEnter", { + group = group, + callback = function() + if explorer._pending_refresh and vim.api.nvim_get_current_tabpage() == tabpage then + explorer._pending_refresh = nil + debounced_refresh() + end + end, + }) + return cleanup end From 22ae13d1838493ab2fa058e8ac61f171d0b408a7 Mon Sep 17 00:00:00 2001 From: Yanuo Ma Date: Wed, 4 Mar 2026 22:53:11 -0500 Subject: [PATCH 2/3] fix(helpers): use exact buffer name matching in prepare_buffer vim.fn.bufnr() uses pattern/prefix matching, causing collisions when one filename is a prefix of another (e.g. Makefile matching Makefile.win). This caused the left diff pane to show stale content when navigating between such files. Replace with exact nvim_buf_get_name() comparison to ensure the correct buffer is returned. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lua/codediff/ui/view/helpers.lua | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lua/codediff/ui/view/helpers.lua b/lua/codediff/ui/view/helpers.lua index 48d846f..c525618 100644 --- a/lua/codediff/ui/view/helpers.lua +++ b/lua/codediff/ui/view/helpers.lua @@ -9,6 +9,17 @@ function M.is_virtual_revision(revision) return revision ~= nil and revision ~= "WORKING" end +-- Exact buffer name lookup (vim.fn.bufnr uses pattern matching which +-- causes prefix collisions, e.g. "Makefile" matching "Makefile.win") +local function bufnr_exact(name) + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_valid(buf) and vim.api.nvim_buf_get_name(buf) == name then + return buf + end + end + return -1 +end + -- Prepare buffer information for loading -- Returns: { bufnr = number?, target = string?, needs_edit = boolean } -- - If buffer already exists: { bufnr = 123, target = nil, needs_edit = false } @@ -17,8 +28,8 @@ function M.prepare_buffer(is_virtual, git_root, revision, path) if is_virtual then -- Virtual file: generate URL local virtual_url = virtual_file.create_url(git_root, revision, path) - -- Check if buffer already exists - local existing_buf = vim.fn.bufnr(virtual_url) + -- Check if buffer already exists (exact match to avoid prefix collisions) + local existing_buf = bufnr_exact(virtual_url) -- For :0 (staged index), always force reload because index can change -- when user runs git add/reset. For commits (immutable), we can cache. @@ -41,8 +52,8 @@ function M.prepare_buffer(is_virtual, git_root, revision, path) } end else - -- Real file: check if already loaded - local existing_buf = vim.fn.bufnr(path) + -- Real file: use exact match for buffer lookup + local existing_buf = bufnr_exact(path) if existing_buf ~= -1 then -- Buffer already exists, reuse it return { From 2a8a1f19be36f1dbf9ee21b5f7f9e8059a96ac51 Mon Sep 17 00:00:00 2001 From: Yanuo Ma Date: Thu, 5 Mar 2026 00:01:07 -0500 Subject: [PATCH 3/3] fix(render): prevent diff flicker for staged renames The same-file check in on_file_select failed for staged renames because session.modified_path is relative while abs_path is absolute. Each explorer refresh re-triggered view.update(), causing visible flicker. Add session.modified_path == file_path to the check to handle staged files where paths are stored as relative. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lua/codediff/ui/explorer/render.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/codediff/ui/explorer/render.lua b/lua/codediff/ui/explorer/render.lua index dbb2f16..88c6aa6 100644 --- a/lua/codediff/ui/explorer/render.lua +++ b/lua/codediff/ui/explorer/render.lua @@ -323,7 +323,7 @@ function M.create(status_result, git_root, tabpage, width, base_revision, target -- Same file can have different diffs (staged vs HEAD, working vs staged) local session = lifecycle.get_session(tabpage) if session then - local is_same_file = (session.modified_path == abs_path or (session.git_root and session.original_path == file_path)) + local is_same_file = (session.modified_path == abs_path or session.modified_path == file_path or (session.git_root and session.original_path == file_path)) if is_same_file and not opts.force then -- Check if it's the same diff comparison