From 7f0bd43b34e6c79b6c96f8fa76d33ef554dbc159 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Tue, 24 Feb 2026 22:01:57 -0500 Subject: [PATCH] feat(buffer): preserve category fold state across re-renders Problem: pressing :w, toggling priority, or any other operation that calls buffer.render() reset foldlevel = 99, causing all manually collapsed category sections to snap back open. Solution: snapshot which categories are folded (per window) before nvim_buf_set_lines destroys the fold tree, then restore them after fold options are re-applied by calling normal! zc on each previously closed header line. State persists across all render call sites within a session. --- lua/pending/buffer.lua | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lua/pending/buffer.lua b/lua/pending/buffer.lua index 2036357..861aca3 100644 --- a/lua/pending/buffer.lua +++ b/lua/pending/buffer.lua @@ -12,6 +12,8 @@ local task_ns = vim.api.nvim_create_namespace('pending') local current_view = nil ---@type pending.LineMeta[] local _meta = {} +---@type table> +local _fold_state = {} ---@return pending.LineMeta[] function M.meta() @@ -148,6 +150,50 @@ local function setup_highlights() vim.api.nvim_set_hl(0, 'PendingPriority', { link = 'DiagnosticWarn', default = true }) end +local function snapshot_folds(bufnr) + if current_view ~= 'category' then + return + end + for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do + local state = {} + vim.api.nvim_win_call(winid, function() + for lnum, m in ipairs(_meta) do + if m.type == 'header' and m.category then + if vim.fn.foldclosed(lnum) ~= -1 then + state[m.category] = true + end + end + end + end) + _fold_state[winid] = state + end +end + +local function restore_folds(bufnr) + if current_view ~= 'category' then + return + end + for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do + local state = _fold_state[winid] + if not state or next(state) == nil then + goto continue + end + vim.api.nvim_win_call(winid, function() + vim.cmd('normal! zx') + local saved = vim.api.nvim_win_get_cursor(0) + for lnum, m in ipairs(_meta) do + if m.type == 'header' and m.category and state[m.category] then + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + vim.cmd('normal! zc') + end + end + vim.api.nvim_win_set_cursor(0, saved) + end) + _fold_state[winid] = nil + ::continue:: + end +end + ---@param bufnr? integer function M.render(bufnr) bufnr = bufnr or task_bufnr @@ -167,6 +213,7 @@ function M.render(bufnr) _meta = line_meta + snapshot_folds(bufnr) vim.bo[bufnr].modifiable = true vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) vim.bo[bufnr].modified = false @@ -185,6 +232,7 @@ function M.render(bufnr) vim.wo[winid].foldenable = false end end + restore_folds(bufnr) end function M.toggle_view()