feat(views): make queue view sort order configurable (#154)

Problem: the queue/priority view sort in `sort_tasks_priority()` uses a
hardcoded tiebreak chain (status, priority, due, order, id). Users who
care more about due dates than priority have no way to reorder it.

Solution: add `view.queue.sort` config field (string[]) that defines an
ordered tiebreak chain. `build_queue_comparator()` maps each key to a
comparison function and returns a single comparator. Unknown keys emit a
`log.warn`. The default matches the previous hardcoded behavior.
This commit is contained in:
Barrett Ruth 2026-03-12 20:19:36 -04:00
parent b9b12fd2a5
commit 3b3cdc8965
4 changed files with 169 additions and 12 deletions

View file

@ -529,5 +529,83 @@ describe('views', function()
end
assert.is_nil(task_meta.recur)
end)
it('sorts by due before priority when sort config is reordered', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { queue = { sort = { 'status', 'due', 'priority', 'order', 'id' } } },
}
config.reset()
s:add({ description = 'High no due', category = 'Work', priority = 2 })
s:add({ description = 'Low with due', category = 'Work', priority = 0, due = '2050-01-01' })
local lines, meta = views.priority_view(s:active_tasks())
local due_row, nodue_row
for i, m in ipairs(meta) do
if m.type == 'task' then
if lines[i]:find('Low with due') then
due_row = i
elseif lines[i]:find('High no due') then
nodue_row = i
end
end
end
assert.is_true(due_row < nodue_row)
end)
it('uses default sort when config sort is nil', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { queue = {} },
}
config.reset()
s:add({ description = 'Low', category = 'Work', priority = 0 })
s:add({ description = 'High', category = 'Work', priority = 1 })
local lines, meta = views.priority_view(s:active_tasks())
local high_row, low_row
for i, m in ipairs(meta) do
if m.type == 'task' then
if lines[i]:find('High') then
high_row = i
elseif lines[i]:find('Low') then
low_row = i
end
end
end
assert.is_true(high_row < low_row)
end)
it('ignores unknown sort keys with a warning', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { queue = { sort = { 'bogus', 'status', 'id' } } },
}
config.reset()
s:add({ description = 'A', category = 'Work' })
s:add({ description = 'B', category = 'Work' })
local lines = views.priority_view(s:active_tasks())
assert.is_true(#lines == 2)
end)
it('supports age sort key as alias for id', function()
vim.g.pending = {
data_path = tmpdir .. '/tasks.json',
view = { queue = { sort = { 'age' } } },
}
config.reset()
s:add({ description = 'Older', category = 'Work' })
s:add({ description = 'Newer', category = 'Work' })
local lines, meta = views.priority_view(s:active_tasks())
local older_row, newer_row
for i, m in ipairs(meta) do
if m.type == 'task' then
if lines[i]:find('Older') then
older_row = i
elseif lines[i]:find('Newer') then
newer_row = i
end
end
end
assert.is_true(older_row < newer_row)
end)
end)
end)