Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions lua/diffview/scene/window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,24 @@ Window.open_file = async.void(function(self)
---@diagnostic disable: invisible
assert(self.file)

if not (self:is_valid() and self.file.active) then
if not self:is_valid() then
return
end

if not self.file:is_valid() then
-- Skip the load entirely if the file was already deactivated by the
-- time we got here -- the user has navigated past this target, so a
-- fresh `git show` would be wasted work. Once the load is in flight,
-- `create_buffer` raises `CANCELLED` on its own active checks, which
-- surfaces as `ok = false` below.
if not self.file.active then
return
end
local ok = await(self:load_file())
await(async.scheduler())

-- Ensure validity after await
if not (self:is_valid() and self.file.active) then
if not self:is_valid() then
return
end

Expand All @@ -161,6 +169,19 @@ Window.open_file = async.void(function(self)
end
end

-- The file is loaded; display it regardless of `self.file.active`.
-- `Layout.open_files` yields on `async.scheduler()` between its load
-- loop and its open loop, and a rapid navigation in that gap can
-- deactivate the file. Bailing here on `active=false` used to leave
-- the window holding the prior `open_null` placeholder while
-- `file.loaded=true` claimed the buffer was ready -- a stale state
-- that trips `jump_to_first_change` (silent `]c` on an empty buffer
-- in the default branch; `Cursor position outside buffer` in
-- `diff1_inline`, which looks up hunks on `file.bufnr` and writes the
-- cursor in `main.id`'s current buffer). The buffer is already
-- populated, so setting it in the window is cheap; the next worker
-- iteration replaces it with the user's actual target.

self.emitter:emit("pre_open")

-- Disable context plugins BEFORE the buffer enters the window.
Expand Down
32 changes: 32 additions & 0 deletions lua/diffview/tests/functional/window_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,38 @@ describe("diffview.scene.window", function()
vim.api.nvim_buf_delete(bufnr, { force = true })
end)
)

-- Regression: `Layout.open_files` yields between its load loop and its
-- open loop. A rapid navigation in that gap deactivates the in-flight
-- file; bailing on `active=false` here used to leave the window holding
-- the `open_null` placeholder while `file.loaded=true` claimed the
-- buffer was ready, tripping `jump_to_first_change` (silent `]c` on an
-- empty buffer; "Cursor position outside buffer" in `diff1_inline`).
-- Once the buffer is populated, displaying it is cheap, so `open_file`
-- proceeds regardless of `active`.
it(
"displays an already-loaded buffer even when the file got deactivated",
helpers.async_test(function()
local adapter = mock_adapter()
local bufnr = vim.api.nvim_create_buf(false, true)
local win, file = make_window(adapter)
file.bufnr = bufnr
file.loaded = true
file.active = false -- deactivated post-load.
win.parent = stub_parent()

async.await(win:open_file())

assert.equals(bufnr, vim.api.nvim_win_get_buf(win.id))

if vim.api.nvim_win_is_valid(test_winid) then
vim.api.nvim_win_close(test_winid, true)
end
test_winid = nil
Window.winopt_store[bufnr] = nil
vim.api.nvim_buf_delete(bufnr, { force = true })
end)
)
end)

it(
Expand Down
Loading