fix(buffer): fix stale extmarks, duplicate window, and fold state loss (#92)

Problem: Deleting lines (`dd`, `dat`, `d3j`) left extmarks stranded on
adjacent rows since `render()` only clears and reapplies marks on `:w`.
Quickfix `<CR>` opened the pending buffer in a second window because
`BufEnter` did not redirect to `task_winid`. Category fold state was
lost across `<Tab>/<Tab>` view toggles because `render()` overwrote the
saved state with an empty snapshot taken while folds were disabled.

Solution: Add a `TextChanged`/`TextChangedI` autocmd that clears the
extmark namespace immediately on any edit. Fix `BufEnter` to close
duplicate windows and redirect focus to `task_winid`, updating it when
stale. Fix `snapshot_folds` to skip if a state is already saved, and
`restore_folds` to always clear the saved state; snapshot in
`toggle_view` before the view flips so the state survives the round-trip.
This commit is contained in:
Barrett Ruth 2026-03-06 21:36:04 -05:00 committed by GitHub
parent c392874311
commit 559ab863a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 1 deletions

View file

@ -77,6 +77,18 @@ function M.clear_winid()
task_winid = nil
end
---@param winid integer
---@return nil
function M.update_winid(winid)
task_winid = winid
end
---@param b? integer
---@return nil
function M.clear_marks(b)
vim.api.nvim_buf_clear_namespace(b or task_bufnr, task_ns, 0, -1)
end
---@return nil
function M.close()
if not task_winid or not vim.api.nvim_win_is_valid(task_winid) then
@ -263,6 +275,9 @@ local function snapshot_folds(bufnr)
return
end
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
if _fold_state[winid] ~= nil then
goto continue
end
local state = {}
vim.api.nvim_win_call(winid, function()
for lnum, m in ipairs(_meta) do
@ -274,6 +289,7 @@ local function snapshot_folds(bufnr)
end
end)
_fold_state[winid] = state
::continue::
end
end
@ -283,6 +299,7 @@ local function restore_folds(bufnr)
end
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
local state = _fold_state[winid]
_fold_state[winid] = nil
if state and next(state) ~= nil then
vim.api.nvim_win_call(winid, function()
vim.cmd('normal! zx')
@ -295,7 +312,6 @@ local function restore_folds(bufnr)
end
vim.api.nvim_win_set_cursor(0, saved)
end)
_fold_state[winid] = nil
end
end
end
@ -372,6 +388,7 @@ end
---@return nil
function M.toggle_view()
snapshot_folds(task_bufnr)
if current_view == 'category' then
current_view = 'priority'
else

View file

@ -224,12 +224,37 @@ function M._setup_autocmds(bufnr)
group = group,
buffer = bufnr,
callback = function()
local cur_win = vim.api.nvim_get_current_win()
local tw = buffer.winid()
if tw and vim.api.nvim_win_is_valid(tw) and cur_win ~= tw then
local cursor = vim.api.nvim_win_get_cursor(cur_win)
vim.schedule(function()
if vim.api.nvim_win_is_valid(cur_win) and #vim.api.nvim_list_wins() > 1 then
pcall(vim.api.nvim_win_close, cur_win, false)
end
if vim.api.nvim_win_is_valid(tw) then
vim.api.nvim_set_current_win(tw)
pcall(vim.api.nvim_win_set_cursor, tw, cursor)
end
end)
return
end
if not tw or not vim.api.nvim_win_is_valid(tw) then
buffer.update_winid(cur_win)
end
if not vim.bo[bufnr].modified then
get_store():load()
buffer.render(bufnr)
end
end,
})
vim.api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI' }, {
group = group,
buffer = bufnr,
callback = function()
buffer.clear_marks(bufnr)
end,
})
vim.api.nvim_create_autocmd('WinClosed', {
group = group,
callback = function(ev)