feat: time-aware due dates, persistent undo, @return audit (#33)

* fix(plugin): allow command chaining with bar separator

Problem: :Pending|only failed because the command definition lacked the
bar attribute, causing | to be consumed as an argument.

Solution: Add bar = true to nvim_create_user_command so | is treated as
a command separator, matching fugitive's :Git behavior.

* refactor(buffer): remove opinionated window options

Problem: The plugin hardcoded number, relativenumber, wrap, spell,
signcolumn, foldcolumn, and cursorline in set_win_options, overriding
user preferences with no way to opt out.

Solution: Remove all cosmetic window options. Users who want them can
set them in after/ftplugin/pending.lua. Only conceallevel,
concealcursor, and winfixheight remain as functionally required.

* feat: time-aware due dates, persistent undo, @return audit

Problem: Due dates had no time component, the undo stack was lost on
restart and stored in a separate file, and many public functions lacked
required @return annotations.

Solution: Add YYYY-MM-DDThh:mm support across parse, views, recur,
complete, and init with time-aware overdue checks. Merge the undo stack
into the task store JSON so a single file holds all state. Add @return
nil annotations to all 27 void public functions across every module.

* feat(parse): flexible time parsing for @ suffix

Problem: the @HH:MM time suffix required zero-padded 24-hour format,
forcing users to write due:tomorrow@14:00 instead of due:tomorrow@2pm.

Solution: add normalize_time() that accepts bare hours (9, 14),
H:MM (9:30), am/pm (2pm, 9:30am, 12am), and existing HH:MM format,
normalizing all to canonical HH:MM on save.

* feat(complete): add info descriptions to omnifunc items

Problem: completion menu items had no description, making it hard to
distinguish between similar entries like date shorthands and recurrence
patterns.

Solution: return { word, info } tables from date_completions() and
recur_completions(), surfacing human-readable descriptions in the
completion popup.

* ci: format
This commit is contained in:
Barrett Ruth 2026-02-25 20:37:50 -05:00 committed by GitHub
parent 72dbf037c7
commit c57cc0845b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 580 additions and 158 deletions

View file

@ -21,7 +21,10 @@ 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)$')
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
@ -30,7 +33,30 @@ local function format_due(due)
month = tonumber(m) --[[@as integer]],
day = tonumber(d) --[[@as integer]],
})
return os.date(config.get().date_format, t) --[[@as string]]
local formatted = os.date(config.get().date_format, t) --[[@as string]]
if hh then
formatted = formatted .. ' ' .. hh .. ':' .. mm
end
return formatted
end
---@param due string
---@return boolean
local function is_overdue(due)
local now = os.date('*t') --[[@as osdate]]
local today = os.date('%Y-%m-%d') --[[@as string]]
local date_part, time_part = due:match('^(.+)T(.+)$')
if not date_part then
return due < today
end
if date_part < today then
return true
end
if date_part > today then
return false
end
local current_time = string.format('%02d:%02d', now.hour, now.min)
return time_part < current_time
end
---@param tasks pending.Task[]
@ -74,7 +100,6 @@ end
---@return string[] lines
---@return pending.LineMeta[] meta
function M.category_view(tasks)
local today = os.date('%Y-%m-%d') --[[@as string]]
local by_cat = {}
local cat_order = {}
local cat_seen = {}
@ -149,7 +174,7 @@ function M.category_view(tasks)
raw_due = task.due,
status = task.status,
category = cat,
overdue = task.status == 'pending' and task.due ~= nil and task.due < today or nil,
overdue = task.status == 'pending' and task.due ~= nil and is_overdue(task.due) or nil,
recur = task.recur,
})
end
@ -162,7 +187,6 @@ end
---@return string[] lines
---@return pending.LineMeta[] meta
function M.priority_view(tasks)
local today = os.date('%Y-%m-%d') --[[@as string]]
local pending = {}
local done = {}
@ -200,7 +224,7 @@ function M.priority_view(tasks)
raw_due = task.due,
status = task.status,
category = task.category,
overdue = task.status == 'pending' and task.due ~= nil and task.due < today or nil,
overdue = task.status == 'pending' and task.due ~= nil and is_overdue(task.due) or nil,
show_category = true,
recur = task.recur,
})