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

@ -87,6 +87,7 @@
---@field hide_done_categories? boolean
---@class pending.QueueViewConfig
---@field sort? string[]
---@class pending.ViewConfig
---@field default? 'category'|'priority'
@ -133,7 +134,9 @@ local defaults = {
folding = true,
hide_done_categories = false,
},
queue = {},
queue = {
sort = { 'status', 'priority', 'due', 'order', 'id' },
},
},
keymaps = {
close = 'q',

View file

@ -106,17 +106,23 @@ local function sort_tasks(tasks)
end)
end
---@param tasks pending.Task[]
local function sort_tasks_priority(tasks)
table.sort(tasks, function(a, b)
---@type table<string, fun(a: pending.Task, b: pending.Task): boolean?>
local sort_key_comparators = {
status = function(a, b)
local ra = status_rank[a.status] or 1
local rb = status_rank[b.status] or 1
if ra ~= rb then
return ra < rb
end
return nil
end,
priority = function(a, b)
if a.priority ~= b.priority then
return a.priority > b.priority
end
return nil
end,
due = function(a, b)
local a_due = a.due or ''
local b_due = b.due or ''
if a_due ~= b_due then
@ -128,11 +134,56 @@ local function sort_tasks_priority(tasks)
end
return a_due < b_due
end
return nil
end,
order = function(a, b)
if a.order ~= b.order then
return a.order < b.order
end
return a.id < b.id
end)
return nil
end,
id = function(a, b)
if a.id ~= b.id then
return a.id < b.id
end
return nil
end,
age = function(a, b)
if a.id ~= b.id then
return a.id < b.id
end
return nil
end,
}
---@return fun(a: pending.Task, b: pending.Task): boolean
local function build_queue_comparator()
local log = require('pending.log')
local keys = config.get().view.queue.sort or { 'status', 'priority', 'due', 'order', 'id' }
local comparators = {}
for _, key in ipairs(keys) do
local cmp = sort_key_comparators[key]
if cmp then
table.insert(comparators, cmp)
else
log.warn('unknown queue sort key: ' .. key)
end
end
return function(a, b)
for _, cmp in ipairs(comparators) do
local result = cmp(a, b)
if result ~= nil then
return result
end
end
return false
end
end
---@param tasks pending.Task[]
local function sort_tasks_priority(tasks)
local cmp = build_queue_comparator()
table.sort(tasks, cmp)
end
---@param tasks pending.Task[]