feat(store): add snapshot() and atomic json write

Problem: the single-level undo used shallow references so mutations
during diff.apply() corrupted the saved state. JSON writes were also
non-atomic, risking partial writes on crash.

Solution: add M.snapshot() which deep-copies active tasks (including
_extra). Change M.save() to write a .tmp file then rename atomically.
This commit is contained in:
Barrett Ruth 2026-02-24 18:29:49 -05:00 committed by Barrett Ruth
parent fc45ca3fcd
commit 4e9ce2bc8a

View file

@ -175,12 +175,18 @@ function M.save()
table.insert(out.tasks, task_to_table(task))
end
local encoded = vim.json.encode(out)
local f = io.open(path, 'w')
local tmp = path .. '.tmp'
local f = io.open(tmp, 'w')
if not f then
error('pending.nvim: cannot write to ' .. path)
error('pending.nvim: cannot write to ' .. tmp)
end
f:write(encoded)
f:close()
local ok, rename_err = os.rename(tmp, path)
if not ok then
os.remove(tmp)
error('pending.nvim: cannot rename ' .. tmp .. ' to ' .. path .. ': ' .. tostring(rename_err))
end
end
---@return pending.Data
@ -284,6 +290,27 @@ function M.replace_tasks(tasks)
M.data().tasks = tasks
end
---@return pending.Task[]
function M.snapshot()
local result = {}
for _, task in ipairs(M.active_tasks()) do
local copy = {}
for k, v in pairs(task) do
if k ~= '_extra' then
copy[k] = v
end
end
if task._extra then
copy._extra = {}
for k, v in pairs(task._extra) do
copy._extra[k] = v
end
end
table.insert(result, copy --[[@as pending.Task]])
end
return result
end
---@param id integer
function M.set_next_id(id)
M.data().next_id = id