local config = require('todo.config') ---@class todo.LineMeta ---@field type 'task'|'header'|'blank' ---@field id? integer ---@field due? string ---@field raw_due? string ---@field status? string ---@field category? string ---@class todo.views local M = {} ---@param due? string ---@return string? local function format_due(due) if not due then return nil end local y, m, d = due:match('^(%d%d%d%d)-(%d%d)-(%d%d)$') if not y then return due end local t = os.time({ year = tonumber(y), month = tonumber(m), day = tonumber(d) }) return os.date(config.get().date_format, t) end ---@param tasks todo.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 todo.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 todo.Task[] ---@return string[] lines ---@return todo.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 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 indent = ' ' local prio = task.priority == 1 and '! ' or '' local line = prefix .. indent .. prio .. 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, }) end end return lines, meta end ---@param tasks todo.Task[] ---@return string[] lines ---@return todo.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 indent = ' ' local prio = task.priority == 1 and '! ' or '' local line = prefix .. indent .. prio .. 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, }) end return lines, meta end return M