feat(buffer): persist fold state across sessions
Problem: folded category headers are lost when Neovim exits because `_fold_state` only lives in memory. Users must re-fold categories every session. Solution: store folded category names in the JSON data file as a top-level `folded_categories` field. On first render, `restore_folds` seeds from the store instead of the empty in-memory state. Folds are persisted on `M.close()` and `VimLeavePre`.
This commit is contained in:
parent
26d43688d0
commit
5a4cc7f8a1
4 changed files with 121 additions and 1 deletions
|
|
@ -1,4 +1,5 @@
|
|||
local config = require('pending.config')
|
||||
local log = require('pending.log')
|
||||
local views = require('pending.views')
|
||||
|
||||
---@class pending.buffer
|
||||
|
|
@ -18,6 +19,8 @@ local current_view = nil
|
|||
local _meta = {}
|
||||
---@type table<integer, table<string, boolean>>
|
||||
local _fold_state = {}
|
||||
---@type boolean
|
||||
local _initial_fold_loaded = false
|
||||
---@type string[]
|
||||
local _filter_predicates = {}
|
||||
---@type table<integer, true>
|
||||
|
|
@ -89,12 +92,52 @@ function M.clear_marks(b)
|
|||
vim.api.nvim_buf_clear_namespace(b or task_bufnr, task_ns, 0, -1)
|
||||
end
|
||||
|
||||
---@return nil
|
||||
function M.persist_folds()
|
||||
log.debug(('persist_folds: view=%s store=%s'):format(tostring(current_view), tostring(_store ~= nil)))
|
||||
if current_view ~= 'category' or not _store then
|
||||
log.debug('persist_folds: early return (view or store)')
|
||||
return
|
||||
end
|
||||
local bufnr = task_bufnr
|
||||
if not bufnr or not vim.api.nvim_buf_is_valid(bufnr) then
|
||||
log.debug('persist_folds: early return (no valid bufnr)')
|
||||
return
|
||||
end
|
||||
local folded = {}
|
||||
local seen = {}
|
||||
local wins = vim.fn.win_findbuf(bufnr)
|
||||
log.debug(('persist_folds: checking %d windows for bufnr=%d, meta has %d entries'):format(#wins, bufnr, #_meta))
|
||||
for _, winid in ipairs(wins) do
|
||||
if vim.api.nvim_win_is_valid(winid) then
|
||||
vim.api.nvim_win_call(winid, function()
|
||||
for lnum, m in ipairs(_meta) do
|
||||
if m.type == 'header' and m.category and not seen[m.category] then
|
||||
local closed = vim.fn.foldclosed(lnum)
|
||||
log.debug(('persist_folds: win=%d lnum=%d cat=%s foldclosed=%d'):format(winid, lnum, m.category, closed))
|
||||
if closed ~= -1 then
|
||||
seen[m.category] = true
|
||||
table.insert(folded, m.category)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
log.debug(('persist_folds: saving %d folded categories: %s'):format(#folded, table.concat(folded, ', ')))
|
||||
_store:set_folded_categories(folded)
|
||||
end
|
||||
|
||||
---@return nil
|
||||
function M.close()
|
||||
if not task_winid or not vim.api.nvim_win_is_valid(task_winid) then
|
||||
task_winid = nil
|
||||
return
|
||||
end
|
||||
M.persist_folds()
|
||||
if _store then
|
||||
_store:save()
|
||||
end
|
||||
local wins = vim.api.nvim_list_wins()
|
||||
if #wins == 1 then
|
||||
vim.cmd.enew()
|
||||
|
|
@ -275,7 +318,7 @@ local function snapshot_folds(bufnr)
|
|||
return
|
||||
end
|
||||
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
|
||||
if _fold_state[winid] == nil then
|
||||
if _fold_state[winid] == nil and _initial_fold_loaded then
|
||||
local state = {}
|
||||
vim.api.nvim_win_call(winid, function()
|
||||
for lnum, m in ipairs(_meta) do
|
||||
|
|
@ -292,18 +335,39 @@ local function snapshot_folds(bufnr)
|
|||
end
|
||||
|
||||
local function restore_folds(bufnr)
|
||||
log.debug(('restore_folds: view=%s folding_enabled=%s'):format(
|
||||
tostring(current_view), tostring(config.resolve_folding().enabled)))
|
||||
if current_view ~= 'category' or not config.resolve_folding().enabled then
|
||||
return
|
||||
end
|
||||
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
|
||||
local state = _fold_state[winid]
|
||||
_fold_state[winid] = nil
|
||||
log.debug(('restore_folds: win=%d has_fold_state=%s initial_loaded=%s has_store=%s'):format(
|
||||
winid, tostring(state ~= nil), tostring(_initial_fold_loaded), tostring(_store ~= nil)))
|
||||
if not state and not _initial_fold_loaded and _store then
|
||||
_initial_fold_loaded = true
|
||||
local cats = _store:get_folded_categories()
|
||||
log.debug(('restore_folds: loaded %d categories from store: %s'):format(#cats, table.concat(cats, ', ')))
|
||||
if #cats > 0 then
|
||||
state = {}
|
||||
for _, cat in ipairs(cats) do
|
||||
state[cat] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if state and next(state) ~= nil then
|
||||
local applying = {}
|
||||
for k in pairs(state) do
|
||||
table.insert(applying, k)
|
||||
end
|
||||
log.debug(('restore_folds: applying folds for: %s'):format(table.concat(applying, ', ')))
|
||||
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
|
||||
log.debug(('restore_folds: folding lnum=%d cat=%s'):format(lnum, m.category))
|
||||
vim.api.nvim_win_set_cursor(0, { lnum, 0 })
|
||||
vim.cmd('normal! zc')
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue