No description
Find a file
Barrett Ruth f21658f138
feat: overdue highlighting, relative dates, undo write, buffer mappings (#1)
* 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
2026-02-24 18:33:07 -05:00
.github feat: rename 2026-02-24 15:21:44 -05:00
doc feat: overdue highlighting, relative dates, undo write, buffer mappings (#1) 2026-02-24 18:33:07 -05:00
lua/pending feat: overdue highlighting, relative dates, undo write, buffer mappings (#1) 2026-02-24 18:33:07 -05:00
plugin feat: rename 2026-02-24 15:21:44 -05:00
spec feat: overdue highlighting, relative dates, undo write, buffer mappings (#1) 2026-02-24 18:33:07 -05:00
syntax feat: rename 2026-02-24 15:21:44 -05:00
.busted build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
.editorconfig build: initial repo scaffold 2026-02-24 15:08:35 -05:00
.gitignore build: initial repo scaffold 2026-02-24 15:08:35 -05:00
.luarc.json build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
.pre-commit-config.yaml ci: format 2026-02-24 15:17:24 -05:00
flake.lock build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
flake.nix build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
LICENSE build: initial repo scaffold 2026-02-24 15:08:35 -05:00
pending.nvim-scm-1.rockspec feat: rename 2026-02-24 15:21:44 -05:00
README.md ci: format 2026-02-24 15:23:43 -05:00
selene.toml build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
stylua.toml build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00
vim.yaml build: add tooling and dev environment configs 2026-02-24 15:08:44 -05:00

pending.nvim

Edit tasks like text. :w saves them.

A buffer-centric task manager for Neovim. Tasks live in a plain buffer — add with o, delete with dd, reorder with dd/p, rename by editing. Write the buffer and the diff is computed against a JSON store. No UI chrome, no floating windows, no abstractions between you and your tasks.

How it works

School
  ! Read chapter 5                             Feb 28
  Submit homework                              Feb 25

Errands
  Buy groceries                                Mar 01
  Clean apartment

Category headers sit at column 0. Tasks are indented below them. ! marks priority. Due dates appear as right-aligned virtual text. Done tasks get strikethrough. Everything you see is editable buffer text — the IDs are concealed, and metadata is parsed from inline syntax on save.

Install

luarocks install pending.nvim

lazy.nvim:

{ 'barrettruth/pending.nvim' }

Requires Neovim 0.10+. No external dependencies for local use. Google Calendar sync requires curl and openssl.

Usage

:Pending opens the task buffer. From there, it's just vim:

Key Action
o / O Add a new task
dd Delete a task (on :w)
p Paste (duplicates get new IDs)
:w Save all changes
<CR> Toggle complete (immediate)
<Tab> Switch category / priority view
g? Show keybind help

Inline metadata

Type metadata tokens at the end of a task line before saving:

Buy milk due:2026-03-15 cat:Errands

On :w, the date and category are extracted. The description becomes Buy milk, the due date renders as virtual text, and the task moves under the Errands header.

Quick add

:Pending add Buy groceries due:2026-03-15
:Pending add School: Submit homework

Archive

:Pending archive      " purge done tasks older than 30 days
:Pending archive 7    " purge done tasks older than 7 days

Configuration

No setup() call required. Set vim.g.pending before the plugin loads:

vim.g.pending = {
  data_path = vim.fn.stdpath('data') .. '/pending/tasks.json',
  default_view = 'category',  -- 'category' or 'priority'
  default_category = 'Inbox',
  date_format = '%b %d',      -- strftime format for virtual text
  date_syntax = 'due',        -- inline token name (e.g. 'by' for by:2026-03-15)
}

All fields are optional. Absent keys use the defaults shown above.

Google Calendar sync

One-way push of tasks with due dates to a dedicated Google Calendar as all-day events.

vim.g.pending = {
  gcal = {
    calendar = 'Tasks',
    credentials_path = '/path/to/client_secret.json',
  },
}
:Pending sync

On first run, a browser window opens for OAuth consent. The refresh token is stored at stdpath('data')/pending/gcal_tokens.json. Completed or deleted tasks have their calendar events removed. Due date changes update events in place.

Mappings

The plugin defines <Plug> mappings for custom keybinds:

vim.keymap.set('n', '<leader>t', '<Plug>(pending-open)')
vim.keymap.set('n', '<leader>T', '<Plug>(pending-toggle)')
Plug mapping Action
<Plug>(pending-open) Open task buffer
<Plug>(pending-toggle) Toggle complete
<Plug>(pending-view) Switch view
<Plug>(pending-priority) Toggle priority flag
<Plug>(pending-date) Prompt for due date

Data format

Tasks are stored as JSON at stdpath('data')/pending/tasks.json. The schema is versioned and forward-compatible — unknown fields are preserved on round-trip.

Documentation

:checkhealth pending