feat(views): add hide_done_categories config option (#153)

Problem: Categories where every task is done still render in the buffer,
cluttering the view when entire categories are finished.

Solution: Add `view.category.hide_done_categories` (boolean, default
false). When enabled, `category_view()` skips categories whose tasks are
all done/deleted, returns their IDs as `done_cat_hidden_ids`, and
`_on_write` merges those IDs into `hidden_ids` passed to `diff.apply()`
so hidden tasks are not mistakenly deleted on `:w`.
This commit is contained in:
Barrett Ruth 2026-03-12 20:19:27 -04:00 committed by Barrett Ruth
parent ea59bbae96
commit 283f93eda1
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
6 changed files with 123 additions and 4 deletions

View file

@ -280,6 +280,85 @@ describe('views', function()
assert.are.equal('# Alpha', headers[1])
assert.are.equal('# Beta', headers[2])
end)
it('returns empty done_cat_hidden_ids when hide_done_categories is false', function()
local t1 = s:add({ description = 'Done task', category = 'Work' })
s:update(t1.id, { status = 'done' })
s:add({ description = 'Active', category = 'Personal' })
local _, _, done_hidden = views.category_view(s:active_tasks())
assert.are.same({}, done_hidden)
end)
it('hides categories with only done tasks when hide_done_categories is true', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { category = { hide_done_categories = true } },
}
config.reset()
local t1 = s:add({ description = 'Done task', category = 'Work' })
s:update(t1.id, { status = 'done' })
s:add({ description = 'Active', category = 'Personal' })
local lines, meta, done_hidden = views.category_view(s:active_tasks())
local headers = {}
for i, m in ipairs(meta) do
if m.type == 'header' then
table.insert(headers, lines[i])
end
end
assert.are.equal(1, #headers)
assert.are.equal('# Personal', headers[1])
assert.are.same({ [t1.id] = true }, done_hidden)
end)
it('shows categories with a mix of done and pending tasks', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { category = { hide_done_categories = true } },
}
config.reset()
local t1 = s:add({ description = 'Done task', category = 'Work' })
s:update(t1.id, { status = 'done' })
s:add({ description = 'Active task', category = 'Work' })
local lines, meta, done_hidden = views.category_view(s:active_tasks())
local headers = {}
for i, m in ipairs(meta) do
if m.type == 'header' then
table.insert(headers, lines[i])
end
end
assert.are.equal(1, #headers)
assert.are.equal('# Work', headers[1])
assert.are.same({}, done_hidden)
end)
it('does not insert leading blank line when first category is hidden', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { category = { hide_done_categories = true } },
}
config.reset()
local t1 = s:add({ description = 'Done task', category = 'Alpha' })
s:update(t1.id, { status = 'done' })
s:add({ description = 'Active', category = 'Beta' })
local lines, meta = views.category_view(s:active_tasks())
assert.are.equal('header', meta[1].type)
assert.are.equal('# Beta', lines[1])
end)
it('returns all done task ids from hidden categories', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { category = { hide_done_categories = true } },
}
config.reset()
local t1 = s:add({ description = 'Done A', category = 'Work' })
local t2 = s:add({ description = 'Done B', category = 'Work' })
s:update(t1.id, { status = 'done' })
s:update(t2.id, { status = 'done' })
s:add({ description = 'Active', category = 'Personal' })
local _, _, done_hidden = views.category_view(s:active_tasks())
assert.are.same({ [t1.id] = true, [t2.id] = true }, done_hidden)
end)
end)
describe('priority_view', function()