local config = require('pending.config') local parse = require('pending.parse') ---@class pending.LineMeta ---@field type 'task'|'header'|'blank'|'filter' ---@field id? integer ---@field due? string ---@field raw_due? string ---@field status? string ---@field category? string ---@field overdue? boolean ---@field show_category? boolean ---@field priority? integer ---@field recur? string ---@class pending.views local M = {} ---@param due? string ---@return string? local function format_due(due) if not due then return nil end local y, m, d, hh, mm = due:match('^(%d%d%d%d)-(%d%d)-(%d%d)T(%d%d):(%d%d)$') if not y then y, m, d = due:match('^(%d%d%d%d)-(%d%d)-(%d%d)$') end if not y then return due end local t = os.time({ year = tonumber(y) --[[@as integer]], month = tonumber(m) --[[@as integer]], day = tonumber(d) --[[@as integer]], }) local formatted = os.date(config.get().date_format, t) --[[@as string]] if hh then formatted = formatted .. ' ' .. hh .. ':' .. mm end return formatted end ---@param tasks pending.Task[] local function sort_tasks(tasks) table.sort(tasks, function(a, b) if a.priority ~= b.priority then return a.priority > b.priority end if a.order ~= b.order then return a.order < b.order end return a.id < b.id end) end ---@param tasks pending.Task[] local function sort_tasks_priority(tasks) table.sort(tasks, function(a, b) if a.priority ~= b.priority then return a.priority > b.priority end local a_due = a.due or '' local b_due = b.due or '' if a_due ~= b_due then if a_due == '' then return false end if b_due == '' then return true end return a_due < b_due end if a.order ~= b.order then return a.order < b.order end return a.id < b.id end) end ---@param tasks pending.Task[] ---@return string[] lines ---@return pending.LineMeta[] meta function M.category_view(tasks) local by_cat = {} local cat_order = {} local cat_seen = {} local done_by_cat = {} for _, task in ipairs(tasks) do local cat = task.category or config.get().default_category if not cat_seen[cat] then cat_seen[cat] = true table.insert(cat_order, cat) by_cat[cat] = {} done_by_cat[cat] = {} end if task.status == 'done' then table.insert(done_by_cat[cat], task) else table.insert(by_cat[cat], task) end end local cfg_order = config.get().category_order if cfg_order and #cfg_order > 0 then local ordered = {} local seen = {} for _, name in ipairs(cfg_order) do if cat_seen[name] then table.insert(ordered, name) seen[name] = true end end for _, name in ipairs(cat_order) do if not seen[name] then table.insert(ordered, name) end end cat_order = ordered end for _, cat in ipairs(cat_order) do sort_tasks(by_cat[cat]) sort_tasks(done_by_cat[cat]) end local lines = {} local meta = {} for i, cat in ipairs(cat_order) do if i > 1 then table.insert(lines, '') table.insert(meta, { type = 'blank' }) end table.insert(lines, '# ' .. cat) table.insert(meta, { type = 'header', category = cat }) local all = {} for _, t in ipairs(by_cat[cat]) do table.insert(all, t) end for _, t in ipairs(done_by_cat[cat]) do table.insert(all, t) end for _, task in ipairs(all) do local prefix = '/' .. task.id .. '/' local state = task.status == 'done' and 'x' or (task.priority > 0 and '!' or ' ') local line = prefix .. '- [' .. state .. '] ' .. task.description table.insert(lines, line) table.insert(meta, { type = 'task', id = task.id, due = format_due(task.due), raw_due = task.due, status = task.status, category = cat, priority = task.priority, overdue = task.status == 'pending' and task.due ~= nil and parse.is_overdue(task.due) or nil, recur = task.recur, }) end end return lines, meta end ---@param tasks pending.Task[] ---@return string[] lines ---@return pending.LineMeta[] meta function M.priority_view(tasks) local pending = {} local done = {} for _, task in ipairs(tasks) do if task.status == 'done' then table.insert(done, task) else table.insert(pending, task) end end sort_tasks_priority(pending) sort_tasks_priority(done) local lines = {} local meta = {} local all = {} for _, t in ipairs(pending) do table.insert(all, t) end for _, t in ipairs(done) do table.insert(all, t) end for _, task in ipairs(all) do local prefix = '/' .. task.id .. '/' local state = task.status == 'done' and 'x' or (task.priority > 0 and '!' or ' ') local line = prefix .. '- [' .. state .. '] ' .. task.description table.insert(lines, line) table.insert(meta, { type = 'task', id = task.id, due = format_due(task.due), raw_due = task.due, status = task.status, category = task.category, priority = task.priority, overdue = task.status == 'pending' and task.due ~= nil and parse.is_overdue(task.due) or nil, show_category = true, recur = task.recur, }) end return lines, meta end return M