* feat(buffer): open as bottom-drawer split like fugitive
Problem: :Pending replaced the current buffer, making it impossible to
view tasks alongside the file being edited. No way to close the drawer
without :q or switching buffers manually.
Solution: open the task buffer in a botright horizontal split instead of
replacing the current buffer. Track the drawer window ID so re-opening
focuses it rather than creating a second split. Set winfixheight so the
drawer keeps its height when other windows open or close. Add q/<Esc>
mappings to close the drawer, and a WinClosed autocmd to clear the
tracked window ID when the user closes it manually. Add drawer_height
config option (default 15).
* fix(buffer): default to natural split height like fugitive
Problem: hardcoded drawer_height=15 was too small and diverged from
fugitive's model. Fugitive issues a plain botright split and lets Vim's
own split rules (equalalways, winheight) divide the available space.
Solution: remove the default height so the split sizes naturally. Only
call nvim_win_set_height when the user sets drawer_height to a positive
value, preserving the opt-in customization path.
* refactor(config): change default category from Inbox to Todo
* refactor(views): adopt markdown checkbox line format
Problem: task lines used an opaque /ID/ [N] prefix format that was
hard to read and inconsistent between category and priority views.
Header lines had no visual marker distinguishing them from tasks.
Solution: render headers as '## Cat', task lines as
'/ID/- [x|!| ] description'. State encoding: [x]=done, [!]=urgent,
[ ]=pending. Both views use the same construction.
* refactor(diff): parse and reconcile markdown checkbox format
Problem: parse_buffer matched the old ' text' indent pattern and
detected headers via '^%S'. Priority was read from a '[N] ' prefix.
apply() never reconciled status changes written into the buffer.
Solution: match '- [.] text' for tasks and '^## ' for headers.
Extract state char to derive priority (! -> 1) and status (x -> done).
apply() now reconciles status from the buffer, setting/clearing 'end'
timestamps — enabling the oil-style edit-checkbox-then-:w workflow.
* refactor(buffer): update syntax, extmarks, and render for checkbox format
Problem: syntax patterns matched the old indent/[N] format; right_align
virtual text produced a broken layout in narrow windows; the done
strikethrough skipped past the ' ' indent leaving '- [x] ' unstyled;
render() added undo history entries so 'u' could undo a re-render.
Solution: update taskHeader/taskLine patterns for '## '/'- [.]'; rename
taskPriority -> taskCheckbox matching '[!]'; switch virt_text_pos to
'eol'; drop the +2 col_start offset so strikethrough covers '- [x] ';
guard nvim_buf_set_lines with undolevels=-1 so renders are not undoable.
Also fix open_line to insert '- [ ] ' and position cursor at col 6.
* refactor(init): replace multi-level priority with binary toggle
Problem: <C-a>/<C-x> overrode Vim's native number increment and the
visual g<C-a>/g<C-x> variants added complexity for marginal value.
toggle_complete() left the cursor on the wrong line after re-render.
Solution: remove change_priority/change_priority_visual; add
toggle_priority() (0<->1) mapped to '!', with cursor-follow after
render matching the pattern already used in priority toggle. Add
cursor-follow to toggle_complete() for the same reason. Update plugin
plugs (priority-up/down -> priority) and add 'due'/'undo' to the
:Pending completion list. Update help text accordingly.
* feat(buffer): reflect current view in buffer name
Problem: no way to tell at a glance which view (category vs priority)
is active — the buffer was always named 'pending://'.
Solution: update the buffer name to 'pending://category' or
'pending://priority' on every render, so the view is visible in
the statusline/tabline without any extra UI.
Problem: priority was binary (0 or 1), toggled with !, with no way
to express finer gradations or use Vim's native increment idiom.
Solution: replace toggle_priority with change_priority(delta) which
clamps to floor 0. Display format changes from '! ' to '[N] ' so any
integer level is representable. Parser updated to extract numeric
level from the [N] prefix. Visual g<C-a>/g<C-x> apply the delta to
all tasks in the selection. <Plug>(pending-priority) replaced with
<Plug>(pending-priority-up) and <Plug>(pending-priority-down).
Problem: setup_indentexpr always returned 0 because no task line
starts with whitespace (the /ID/ prefix begins with /), so the
return 2 branch was dead code. Pressing o or O opened a blank line
at column 0 with no ID prefix, which the diff parser cannot
recognise as a task.
Solution: remove setup_indentexpr and M.get_indent() entirely; add
M.open_line(above) which inserts a two-space stub line and enters
insert mode at the end so the user types directly into the new task
body. The diff layer already handles lines matching ^ .+ as new
tasks. Add o and O buffer-local mappings in init.lua.
Problem: pressing ! re-sorts the view so the toggled task moves to
the top of its category, but the cursor stays on the original line
number and lands on a different task.
Solution: after buffer.render(), iterate buffer.meta() to find the
new line number for the toggled task's id and call
nvim_win_set_cursor to follow it.
Problem: mapping d for the date prompt intercepts dd before Vim can
recognize it as a motion, so dd never deletes a line.
Solution: move the date prompt to D, restoring full d-operator
behaviour (dd, dw, d$, etc.) and updating the help popup to match.
Problem: undo was single-level with shallow references; no way to
query due/overdue tasks via quickfix; two instances sharing
tasks.json would diverge silently.
Solution: replace _undo_state with _undo_states[] (cap 20, deep
copies via store.snapshot()); add M.due() which populates the
quickfix list with overdue/due-today tasks; add BufEnter autocmd
that reloads from disk when the buffer is unmodified; expand
show_help() with folds, :Pending due, relative date syntax,
PendingOverdue, and empty-input date clearing.
* feat(config): add category_order field
Problem: category display order was always insertion order with no way
to configure it.
Solution: add category_order to config defaults so users can declare a
preferred category ordering; unspecified categories append after.
* feat(parse): add relative date resolution
Problem: due dates required full YYYY-MM-DD input, adding friction for
common cases like "today" or "next monday".
Solution: add resolve_date() supporting today, tomorrow, +Nd, and
weekday abbreviations; extend inline token parsing to resolve relative
values before falling back to strict date validation.
* feat(views): overdue flag, category in priority view, category ordering
Problem: overdue tasks were visually indistinct from upcoming ones;
priority view had no category context; category display order was not
configurable.
Solution: compute overdue meta flag for pending tasks past their due
date; set show_category on priority view task meta; reorder categories
according to config.category_order when present.
* feat(buffer): overdue highlight, category virt text in priority view
Problem: overdue tasks had no visual distinction; priority view showed
no category context alongside due dates.
Solution: add PendingOverdue highlight group; render category name as
right-aligned virtual text in priority view, composited with the due
date when both are present.
* feat(init): undo write and buffer-local default mappings
Problem: _undo_state was captured on every save but never consumed;
toggle_priority and prompt_date had no buffer-local defaults, requiring
manual <Plug> configuration.
Solution: implement undo_write() to restore pre-save task state; add !,
d, and U as buffer-local defaults following fugitive's philosophy of
owning the buffer; expose :Pending undo as a command alias.
* test(views): add views spec
Problem: views.lua had no test coverage.
Solution: add 26 tests covering category_view and priority_view
including sort order, line format, overdue detection, show_category
meta, and category_order config behavior.
* test(archive): add archive spec
Problem: archive had no test coverage.
Solution: add 9 tests covering cutoff logic, custom day counts, pending
task preservation, deleted task cleanup, and notify output.
* docs: add vimdoc
Problem: no :help documentation existed.
Solution: add doc/pending.txt covering all features — commands,
mappings, views, configuration, Google Calendar sync, highlight groups,
data format, and health check — following standard vimdoc conventions.
* ci: format
* fix: resolve lint and type check errors
Problem: selene flagged unused variables in new spec files; LuaLS
flagged os.date/os.time return type mismatches, integer? assignments,
and stale task.Task/task.GcalConfig type references.
Solution: prefix unused spec variables with _ or drop unnecessary
assignments; add --[[@as string/integer]] casts for os.date and
os.time calls; add category_order field to pending.Config annotation;
fix task.GcalConfig -> pending.GcalConfig and task.Task[] ->
pending.Task[]; add nil guards on meta[row].id before store calls;
cast store.data() return to non-optional.
* ci: format
* fix: sync
* ci: format