feat: rename

This commit is contained in:
Barrett Ruth 2026-02-24 15:21:44 -05:00
parent c69a3957c8
commit 78a275d096
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
21 changed files with 191 additions and 191 deletions

View file

@ -4,8 +4,8 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Use this space for questions, ideas, and general discussion about todo.nvim. Use this space for questions, ideas, and general discussion about pending.nvim.
For bug reports, please [open an issue](https://github.com/barrettruth/todo.nvim/issues/new/choose) instead. For bug reports, please [open an issue](https://github.com/barrettruth/pending.nvim/issues/new/choose) instead.
- type: textarea - type: textarea
attributes: attributes:
label: Question or topic label: Question or topic

View file

@ -9,7 +9,7 @@ body:
options: options:
- label: - label:
I have searched [existing I have searched [existing
issues](https://github.com/barrettruth/todo.nvim/issues) issues](https://github.com/barrettruth/pending.nvim/issues)
required: true required: true
- label: I have updated to the latest version - label: I have updated to the latest version
required: true required: true
@ -69,7 +69,7 @@ body:
require('lazy.nvim').setup({ require('lazy.nvim').setup({
spec = { spec = {
{ {
'barrettruth/todo.nvim', 'barrettruth/pending.nvim',
opts = {}, opts = {},
}, },
}, },

View file

@ -1,5 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Questions - name: Questions
url: https://github.com/barrettruth/todo.nvim/discussions url: https://github.com/barrettruth/pending.nvim/discussions
about: Ask questions and discuss ideas about: Ask questions and discuss ideas

View file

@ -9,7 +9,7 @@ body:
options: options:
- label: - label:
I have searched [existing I have searched [existing
issues](https://github.com/barrettruth/todo.nvim/issues) issues](https://github.com/barrettruth/pending.nvim/issues)
required: true required: true
- type: textarea - type: textarea

View file

@ -1,4 +1,4 @@
# todo.nvim # pending.nvim
Edit tasks like text. `:w` saves them. Edit tasks like text. `:w` saves them.
@ -27,13 +27,13 @@ concealed, and metadata is parsed from inline syntax on save.
## Install ## Install
``` ```
luarocks install todo.nvim luarocks install pending.nvim
``` ```
**lazy.nvim:** **lazy.nvim:**
```lua ```lua
{ 'barrettruth/todo.nvim' } { 'barrettruth/pending.nvim' }
``` ```
Requires Neovim 0.10+. No external dependencies for local use. Google Calendar Requires Neovim 0.10+. No external dependencies for local use. Google Calendar
@ -41,7 +41,7 @@ sync requires `curl` and `openssl`.
## Usage ## Usage
`:Todo` opens the task buffer. From there, it's just vim: `:Pending` opens the task buffer. From there, it's just vim:
| Key | Action | | Key | Action |
| --------- | ------------------------------- | | --------- | ------------------------------- |
@ -68,24 +68,24 @@ header.
### Quick add ### Quick add
```vim ```vim
:Todo add Buy groceries due:2026-03-15 :Pending add Buy groceries due:2026-03-15
:Todo add School: Submit homework :Pending add School: Submit homework
``` ```
### Archive ### Archive
```vim ```vim
:Todo archive " purge done tasks older than 30 days :Pending archive " purge done tasks older than 30 days
:Todo archive 7 " purge done tasks older than 7 days :Pending archive 7 " purge done tasks older than 7 days
``` ```
## Configuration ## Configuration
No `setup()` call required. Set `vim.g.todo` before the plugin loads: No `setup()` call required. Set `vim.g.pending` before the plugin loads:
```lua ```lua
vim.g.todo = { vim.g.pending = {
data_path = vim.fn.stdpath('data') .. '/todo/tasks.json', data_path = vim.fn.stdpath('data') .. '/pending/tasks.json',
default_view = 'category', -- 'category' or 'priority' default_view = 'category', -- 'category' or 'priority'
default_category = 'Inbox', default_category = 'Inbox',
date_format = '%b %d', -- strftime format for virtual text date_format = '%b %d', -- strftime format for virtual text
@ -101,7 +101,7 @@ One-way push of tasks with due dates to a dedicated Google Calendar as all-day
events. events.
```lua ```lua
vim.g.todo = { vim.g.pending = {
gcal = { gcal = {
calendar = 'Tasks', calendar = 'Tasks',
credentials_path = '/path/to/client_secret.json', credentials_path = '/path/to/client_secret.json',
@ -110,11 +110,11 @@ vim.g.todo = {
``` ```
```vim ```vim
:Todo sync :Pending sync
``` ```
On first run, a browser window opens for OAuth consent. The refresh token is On first run, a browser window opens for OAuth consent. The refresh token is
stored at `stdpath('data')/todo/gcal_tokens.json`. Completed or deleted tasks stored at `stdpath('data')/pending/gcal_tokens.json`. Completed or deleted tasks
have their calendar events removed. Due date changes update events in place. have their calendar events removed. Due date changes update events in place.
## Mappings ## Mappings
@ -122,25 +122,25 @@ have their calendar events removed. Due date changes update events in place.
The plugin defines `<Plug>` mappings for custom keybinds: The plugin defines `<Plug>` mappings for custom keybinds:
```lua ```lua
vim.keymap.set('n', '<leader>t', '<Plug>(todo-open)') vim.keymap.set('n', '<leader>t', '<Plug>(pending-open)')
vim.keymap.set('n', '<leader>T', '<Plug>(todo-toggle)') vim.keymap.set('n', '<leader>T', '<Plug>(pending-toggle)')
``` ```
| Plug mapping | Action | | Plug mapping | Action |
| ---------------------- | --------------------- | | ---------------------- | --------------------- |
| `<Plug>(todo-open)` | Open task buffer | | `<Plug>(pending-open)` | Open task buffer |
| `<Plug>(todo-toggle)` | Toggle complete | | `<Plug>(pending-toggle)` | Toggle complete |
| `<Plug>(todo-view)` | Switch view | | `<Plug>(pending-view)` | Switch view |
| `<Plug>(todo-priority)`| Toggle priority flag | | `<Plug>(pending-priority)`| Toggle priority flag |
| `<Plug>(todo-date)` | Prompt for due date | | `<Plug>(pending-date)` | Prompt for due date |
## Data format ## Data format
Tasks are stored as JSON at `stdpath('data')/todo/tasks.json`. The schema is 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. versioned and forward-compatible — unknown fields are preserved on round-trip.
## Documentation ## Documentation
```vim ```vim
:checkhealth todo :checkhealth pending
``` ```

View file

@ -1,19 +1,19 @@
local config = require('todo.config') local config = require('pending.config')
local store = require('todo.store') local store = require('pending.store')
local views = require('todo.views') local views = require('pending.views')
---@class todo.buffer ---@class pending.buffer
local M = {} local M = {}
---@type integer? ---@type integer?
local task_bufnr = nil local task_bufnr = nil
local task_ns = vim.api.nvim_create_namespace('todo') local task_ns = vim.api.nvim_create_namespace('pending')
---@type 'category'|'priority'|nil ---@type 'category'|'priority'|nil
local current_view = nil local current_view = nil
---@type todo.LineMeta[] ---@type pending.LineMeta[]
local _meta = {} local _meta = {}
---@return todo.LineMeta[] ---@return pending.LineMeta[]
function M.meta() function M.meta()
return _meta return _meta
end end
@ -33,7 +33,7 @@ local function set_buf_options(bufnr)
vim.bo[bufnr].buftype = 'acwrite' vim.bo[bufnr].buftype = 'acwrite'
vim.bo[bufnr].bufhidden = 'hide' vim.bo[bufnr].bufhidden = 'hide'
vim.bo[bufnr].swapfile = false vim.bo[bufnr].swapfile = false
vim.bo[bufnr].filetype = 'todo' vim.bo[bufnr].filetype = 'pending'
vim.bo[bufnr].modifiable = true vim.bo[bufnr].modifiable = true
end end
@ -65,7 +65,7 @@ end
---@param bufnr integer ---@param bufnr integer
local function setup_indentexpr(bufnr) local function setup_indentexpr(bufnr)
vim.bo[bufnr].indentexpr = 'v:lua.require("todo.buffer").get_indent()' vim.bo[bufnr].indentexpr = 'v:lua.require("pending.buffer").get_indent()'
end end
---@return integer ---@return integer
@ -82,7 +82,7 @@ function M.get_indent()
end end
---@param bufnr integer ---@param bufnr integer
---@param line_meta todo.LineMeta[] ---@param line_meta pending.LineMeta[]
local function apply_extmarks(bufnr, line_meta) local function apply_extmarks(bufnr, line_meta)
vim.api.nvim_buf_clear_namespace(bufnr, task_ns, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, task_ns, 0, -1)
for i, m in ipairs(line_meta) do for i, m in ipairs(line_meta) do
@ -90,7 +90,7 @@ local function apply_extmarks(bufnr, line_meta)
if m.type == 'task' then if m.type == 'task' then
if m.due then if m.due then
vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, 0, { vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, 0, {
virt_text = { { m.due, 'TodoDue' } }, virt_text = { { m.due, 'PendingDue' } },
virt_text_pos = 'right_align', virt_text_pos = 'right_align',
}) })
end end
@ -99,14 +99,14 @@ local function apply_extmarks(bufnr, line_meta)
local col_start = line:find('/%d+/') and select(2, line:find('/%d+/')) + 2 or 0 local col_start = line:find('/%d+/') and select(2, line:find('/%d+/')) + 2 or 0
vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, col_start, { vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, col_start, {
end_col = #line, end_col = #line,
hl_group = 'TodoDone', hl_group = 'PendingDone',
}) })
end end
elseif m.type == 'header' then elseif m.type == 'header' then
local line = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] or '' local line = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] or ''
vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, 0, { vim.api.nvim_buf_set_extmark(bufnr, task_ns, row, 0, {
end_col = #line, end_col = #line,
hl_group = 'TodoHeader', hl_group = 'PendingHeader',
}) })
end end
end end
@ -118,10 +118,10 @@ local function setup_highlights()
vim.api.nvim_set_hl(0, name, opts) vim.api.nvim_set_hl(0, name, opts)
end end
end end
hl('TodoHeader', { bold = true }) hl('PendingHeader', { bold = true })
hl('TodoDue', { fg = '#888888', italic = true }) hl('PendingDue', { fg = '#888888', italic = true })
hl('TodoDone', { strikethrough = true, fg = '#666666' }) hl('PendingDone', { strikethrough = true, fg = '#666666' })
hl('TodoPriority', { fg = '#e06c75', bold = true }) hl('PendingPriority', { fg = '#e06c75', bold = true })
end end
---@param bufnr? integer ---@param bufnr? integer
@ -179,7 +179,7 @@ function M.open()
end end
task_bufnr = vim.api.nvim_create_buf(true, false) task_bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_buf_set_name(task_bufnr, 'todo://') vim.api.nvim_buf_set_name(task_bufnr, 'pending://')
set_buf_options(task_bufnr) set_buf_options(task_bufnr)
setup_indentexpr(task_bufnr) setup_indentexpr(task_bufnr)

View file

@ -1,8 +1,8 @@
---@class todo.GcalConfig ---@class pending.GcalConfig
---@field calendar? string ---@field calendar? string
---@field credentials_path? string ---@field credentials_path? string
---@class todo.Config ---@class pending.Config
---@field data_path string ---@field data_path string
---@field default_view 'category'|'priority' ---@field default_view 'category'|'priority'
---@field default_category string ---@field default_category string
@ -10,27 +10,27 @@
---@field date_syntax string ---@field date_syntax string
---@field gcal? task.GcalConfig ---@field gcal? task.GcalConfig
---@class todo.config ---@class pending.config
local M = {} local M = {}
---@type todo.Config ---@type pending.Config
local defaults = { local defaults = {
data_path = vim.fn.stdpath('data') .. '/todo/tasks.json', data_path = vim.fn.stdpath('data') .. '/pending/tasks.json',
default_view = 'category', default_view = 'category',
default_category = 'Inbox', default_category = 'Inbox',
date_format = '%b %d', date_format = '%b %d',
date_syntax = 'due', date_syntax = 'due',
} }
---@type todo.Config? ---@type pending.Config?
local _resolved = nil local _resolved = nil
---@return todo.Config ---@return pending.Config
function M.get() function M.get()
if _resolved then if _resolved then
return _resolved return _resolved
end end
local user = vim.g.todo or {} local user = vim.g.pending or {}
_resolved = vim.tbl_deep_extend('force', defaults, user) _resolved = vim.tbl_deep_extend('force', defaults, user)
return _resolved return _resolved
end end

View file

@ -1,8 +1,8 @@
local config = require('todo.config') local config = require('pending.config')
local parse = require('todo.parse') local parse = require('pending.parse')
local store = require('todo.store') local store = require('pending.store')
---@class todo.ParsedEntry ---@class pending.ParsedEntry
---@field type 'task'|'header'|'blank' ---@field type 'task'|'header'|'blank'
---@field id? integer ---@field id? integer
---@field description? string ---@field description? string
@ -11,7 +11,7 @@ local store = require('todo.store')
---@field due? string ---@field due? string
---@field lnum integer ---@field lnum integer
---@class todo.diff ---@class pending.diff
local M = {} local M = {}
---@return string ---@return string
@ -20,7 +20,7 @@ local function timestamp()
end end
---@param lines string[] ---@param lines string[]
---@return todo.ParsedEntry[] ---@return pending.ParsedEntry[]
function M.parse_buffer(lines) function M.parse_buffer(lines)
local result = {} local result = {}
local current_category = nil local current_category = nil

View file

@ -1,11 +1,11 @@
local M = {} local M = {}
function M.check() function M.check()
vim.health.start('todo.nvim') vim.health.start('pending.nvim')
local ok, config = pcall(require, 'todo.config') local ok, config = pcall(require, 'pending.config')
if not ok then if not ok then
vim.health.error('Failed to load todo.config') vim.health.error('Failed to load pending.config')
return return
end end
@ -25,7 +25,7 @@ function M.check()
end end
if vim.fn.filereadable(cfg.data_path) == 1 then if vim.fn.filereadable(cfg.data_path) == 1 then
local store_ok, store = pcall(require, 'todo.store') local store_ok, store = pcall(require, 'pending.store')
if store_ok then if store_ok then
local load_ok, err = pcall(store.load) local load_ok, err = pcall(store.load)
if load_ok then if load_ok then

View file

@ -1,12 +1,12 @@
local buffer = require('todo.buffer') local buffer = require('pending.buffer')
local diff = require('todo.diff') local diff = require('pending.diff')
local parse = require('todo.parse') local parse = require('pending.parse')
local store = require('todo.store') local store = require('pending.store')
---@class task ---@class task
local M = {} local M = {}
---@type todo.Task[]? ---@type pending.Task[]?
local _undo_state = nil local _undo_state = nil
---@return integer bufnr ---@return integer bufnr
@ -19,7 +19,7 @@ end
---@param bufnr integer ---@param bufnr integer
function M._setup_autocmds(bufnr) function M._setup_autocmds(bufnr)
local group = vim.api.nvim_create_augroup('TodoBuffer', { clear = true }) local group = vim.api.nvim_create_augroup('PendingBuffer', { clear = true })
vim.api.nvim_create_autocmd('BufWriteCmd', { vim.api.nvim_create_autocmd('BufWriteCmd', {
group = group, group = group,
buffer = bufnr, buffer = bufnr,
@ -127,13 +127,13 @@ end
---@param text string ---@param text string
function M.add(text) function M.add(text)
if not text or text == '' then if not text or text == '' then
vim.notify('Usage: :Todo add <description>', vim.log.levels.ERROR) vim.notify('Usage: :Pending add <description>', vim.log.levels.ERROR)
return return
end end
store.load() store.load()
local description, metadata = parse.command_add(text) local description, metadata = parse.command_add(text)
if not description or description == '' then if not description or description == '' then
vim.notify('Todo must have a description.', vim.log.levels.ERROR) vim.notify('Pending must have a description.', vim.log.levels.ERROR)
return return
end end
store.add({ store.add({
@ -146,11 +146,11 @@ function M.add(text)
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
buffer.render(bufnr) buffer.render(bufnr)
end end
vim.notify('Todo added: ' .. description) vim.notify('Pending added: ' .. description)
end end
function M.sync() function M.sync()
local ok, gcal = pcall(require, 'todo.sync.gcal') local ok, gcal = pcall(require, 'pending.sync.gcal')
if not ok then if not ok then
vim.notify('Google Calendar sync module not available.', vim.log.levels.ERROR) vim.notify('Google Calendar sync module not available.', vim.log.levels.ERROR)
return return
@ -196,10 +196,10 @@ function M.archive(days)
end end
function M.show_help() function M.show_help()
local cfg = require('todo.config').get() local cfg = require('pending.config').get()
local dk = cfg.date_syntax or 'due' local dk = cfg.date_syntax or 'due'
local lines = { local lines = {
'todo.nvim keybindings', 'pending.nvim keybindings',
'', '',
'<CR> Toggle complete/uncomplete', '<CR> Toggle complete/uncomplete',
'<Tab> Switch category/priority view', '<Tab> Switch category/priority view',
@ -208,10 +208,10 @@ function M.show_help()
'p / P Paste (duplicates get new IDs)', 'p / P Paste (duplicates get new IDs)',
':w Save all changes', ':w Save all changes',
'', '',
':Todo add <text> Quick-add task', ':Pending add <text> Quick-add task',
':Todo add Cat: <text> Quick-add with category', ':Pending add Cat: <text> Quick-add with category',
':Todo sync Push to Google Calendar', ':Pending sync Push to Google Calendar',
':Todo archive [days] Purge old done tasks', ':Pending archive [days] Purge old done tasks',
'', '',
'Inline metadata (on new lines before :w):', 'Inline metadata (on new lines before :w):',
' ' .. dk .. ':YYYY-MM-DD Set due date', ' ' .. dk .. ':YYYY-MM-DD Set due date',
@ -257,7 +257,7 @@ function M.command(args)
local d = rest ~= '' and tonumber(rest) or nil local d = rest ~= '' and tonumber(rest) or nil
M.archive(d) M.archive(d)
else else
vim.notify('Unknown Todo subcommand: ' .. cmd, vim.log.levels.ERROR) vim.notify('Unknown Pending subcommand: ' .. cmd, vim.log.levels.ERROR)
end end
end end

View file

@ -1,6 +1,6 @@
local config = require('todo.config') local config = require('pending.config')
---@class todo.parse ---@class pending.parse
local M = {} local M = {}
---@param s string ---@param s string

View file

@ -1,6 +1,6 @@
local config = require('todo.config') local config = require('pending.config')
---@class todo.Task ---@class pending.Task
---@field id integer ---@field id integer
---@field description string ---@field description string
---@field status 'pending'|'done'|'deleted' ---@field status 'pending'|'done'|'deleted'
@ -13,20 +13,20 @@ local config = require('todo.config')
---@field order integer ---@field order integer
---@field _extra? table<string, any> ---@field _extra? table<string, any>
---@class todo.Data ---@class pending.Data
---@field version integer ---@field version integer
---@field next_id integer ---@field next_id integer
---@field tasks task.Task[] ---@field tasks task.Task[]
---@class todo.store ---@class pending.store
local M = {} local M = {}
local SUPPORTED_VERSION = 1 local SUPPORTED_VERSION = 1
---@type todo.Data? ---@type pending.Data?
local _data = nil local _data = nil
---@return todo.Data ---@return pending.Data
local function empty_data() local function empty_data()
return { return {
version = SUPPORTED_VERSION, version = SUPPORTED_VERSION,
@ -62,7 +62,7 @@ local known_fields = {
order = true, order = true,
} }
---@param task todo.Task ---@param task pending.Task
---@return table ---@return table
local function task_to_table(task) local function task_to_table(task)
local t = { local t = {
@ -96,7 +96,7 @@ local function task_to_table(task)
end end
---@param t table ---@param t table
---@return todo.Task ---@return pending.Task
local function table_to_task(t) local function table_to_task(t)
local task = { local task = {
id = t.id, id = t.id,
@ -122,7 +122,7 @@ local function table_to_task(t)
return task return task
end end
---@return todo.Data ---@return pending.Data
function M.load() function M.load()
local path = config.get().data_path local path = config.get().data_path
local f = io.open(path, 'r') local f = io.open(path, 'r')
@ -138,11 +138,11 @@ function M.load()
end end
local ok, decoded = pcall(vim.json.decode, content) local ok, decoded = pcall(vim.json.decode, content)
if not ok then if not ok then
error('todo.nvim: failed to parse ' .. path .. ': ' .. tostring(decoded)) error('pending.nvim: failed to parse ' .. path .. ': ' .. tostring(decoded))
end end
if decoded.version and decoded.version > SUPPORTED_VERSION then if decoded.version and decoded.version > SUPPORTED_VERSION then
error( error(
'todo.nvim: data file version ' 'pending.nvim: data file version '
.. decoded.version .. decoded.version
.. ' is newer than supported version ' .. ' is newer than supported version '
.. SUPPORTED_VERSION .. SUPPORTED_VERSION
@ -177,13 +177,13 @@ function M.save()
local encoded = vim.json.encode(out) local encoded = vim.json.encode(out)
local f = io.open(path, 'w') local f = io.open(path, 'w')
if not f then if not f then
error('todo.nvim: cannot write to ' .. path) error('pending.nvim: cannot write to ' .. path)
end end
f:write(encoded) f:write(encoded)
f:close() f:close()
end end
---@return todo.Data ---@return pending.Data
function M.data() function M.data()
if not _data then if not _data then
M.load() M.load()
@ -191,12 +191,12 @@ function M.data()
return _data return _data
end end
---@return todo.Task[] ---@return pending.Task[]
function M.tasks() function M.tasks()
return M.data().tasks return M.data().tasks
end end
---@return todo.Task[] ---@return pending.Task[]
function M.active_tasks() function M.active_tasks()
local result = {} local result = {}
for _, task in ipairs(M.tasks()) do for _, task in ipairs(M.tasks()) do
@ -208,7 +208,7 @@ function M.active_tasks()
end end
---@param id integer ---@param id integer
---@return todo.Task? ---@return pending.Task?
function M.get(id) function M.get(id)
for _, task in ipairs(M.tasks()) do for _, task in ipairs(M.tasks()) do
if task.id == id then if task.id == id then
@ -219,7 +219,7 @@ function M.get(id)
end end
---@param fields { description: string, status?: string, category?: string, priority?: integer, due?: string, order?: integer, _extra?: table } ---@param fields { description: string, status?: string, category?: string, priority?: integer, due?: string, order?: integer, _extra?: table }
---@return todo.Task ---@return pending.Task
function M.add(fields) function M.add(fields)
local data = M.data() local data = M.data()
local now = timestamp() local now = timestamp()
@ -243,7 +243,7 @@ end
---@param id integer ---@param id integer
---@param fields table<string, any> ---@param fields table<string, any>
---@return todo.Task? ---@return pending.Task?
function M.update(id, fields) function M.update(id, fields)
local task = M.get(id) local task = M.get(id)
if not task then if not task then
@ -263,7 +263,7 @@ function M.update(id, fields)
end end
---@param id integer ---@param id integer
---@return todo.Task? ---@return pending.Task?
function M.delete(id) function M.delete(id)
return M.update(id, { status = 'deleted', ['end'] = timestamp() }) return M.update(id, { status = 'deleted', ['end'] = timestamp() })
end end
@ -279,7 +279,7 @@ function M.find_index(id)
return nil return nil
end end
---@param tasks todo.Task[] ---@param tasks pending.Task[]
function M.replace_tasks(tasks) function M.replace_tasks(tasks)
M.data().tasks = tasks M.data().tasks = tasks
end end

View file

@ -1,5 +1,5 @@
local config = require('todo.config') local config = require('pending.config')
local store = require('todo.store') local store = require('pending.store')
local M = {} local M = {}
@ -14,12 +14,12 @@ local function gcal_config()
end end
local function token_path() local function token_path()
return vim.fn.stdpath('data') .. '/todo/gcal_tokens.json' return vim.fn.stdpath('data') .. '/pending/gcal_tokens.json'
end end
local function credentials_path() local function credentials_path()
local gc = gcal_config() local gc = gcal_config()
return gc.credentials_path or (vim.fn.stdpath('data') .. '/todo/gcal_credentials.json') return gc.credentials_path or (vim.fn.stdpath('data') .. '/pending/gcal_credentials.json')
end end
local function load_json_file(path) local function load_json_file(path)
@ -153,7 +153,7 @@ local function get_access_token()
local creds = load_credentials() local creds = load_credentials()
if not creds then if not creds then
vim.notify( vim.notify(
'todo.nvim: No Google Calendar credentials found at ' .. credentials_path(), 'pending.nvim: No Google Calendar credentials found at ' .. credentials_path(),
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return nil return nil
@ -172,7 +172,7 @@ local function get_access_token()
if now - obtained > expires - 60 then if now - obtained > expires - 60 then
tokens = refresh_access_token(creds, tokens) tokens = refresh_access_token(creds, tokens)
if not tokens then if not tokens then
vim.notify('todo.nvim: Failed to refresh access token.', vim.log.levels.ERROR) vim.notify('pending.nvim: Failed to refresh access token.', vim.log.levels.ERROR)
return nil return nil
end end
end end
@ -183,7 +183,7 @@ function M.authorize()
local creds = load_credentials() local creds = load_credentials()
if not creds then if not creds then
vim.notify( vim.notify(
'todo.nvim: No Google Calendar credentials found at ' .. credentials_path(), 'pending.nvim: No Google Calendar credentials found at ' .. credentials_path(),
vim.log.levels.ERROR vim.log.levels.ERROR
) )
return return
@ -225,7 +225,7 @@ function M.authorize()
.. '&code_challenge_method=S256' .. '&code_challenge_method=S256'
vim.ui.open(auth_url) vim.ui.open(auth_url)
vim.notify('todo.nvim: Opening browser for Google authorization...') vim.notify('pending.nvim: Opening browser for Google authorization...')
local server = vim.uv.new_tcp() local server = vim.uv.new_tcp()
server:bind('127.0.0.1', port) server:bind('127.0.0.1', port)
@ -288,24 +288,24 @@ function M._exchange_code(creds, code, code_verifier, port)
:wait() :wait()
if result.code ~= 0 then if result.code ~= 0 then
vim.notify('todo.nvim: Token exchange failed.', vim.log.levels.ERROR) vim.notify('pending.nvim: Token exchange failed.', vim.log.levels.ERROR)
return return
end end
local ok, decoded = pcall(vim.json.decode, result.stdout or '') local ok, decoded = pcall(vim.json.decode, result.stdout or '')
if not ok or not decoded.access_token then if not ok or not decoded.access_token then
vim.notify('todo.nvim: Invalid token response.', vim.log.levels.ERROR) vim.notify('pending.nvim: Invalid token response.', vim.log.levels.ERROR)
return return
end end
decoded.obtained_at = os.time() decoded.obtained_at = os.time()
save_tokens(decoded) save_tokens(decoded)
vim.notify('todo.nvim: Google Calendar authorized successfully.') vim.notify('pending.nvim: Google Calendar authorized successfully.')
end end
local function find_or_create_calendar(access_token) local function find_or_create_calendar(access_token)
local gc = gcal_config() local gc = gcal_config()
local cal_name = gc.calendar or 'Todos' local cal_name = gc.calendar or 'Pendings'
local data, err = local data, err =
curl_request('GET', BASE_URL .. '/users/me/calendarList', auth_headers(access_token)) curl_request('GET', BASE_URL .. '/users/me/calendarList', auth_headers(access_token))
@ -389,7 +389,7 @@ function M.sync()
local calendar_id, err = find_or_create_calendar(access_token) local calendar_id, err = find_or_create_calendar(access_token)
if err then if err then
vim.notify('todo.nvim: ' .. err, vim.log.levels.ERROR) vim.notify('pending.nvim: ' .. err, vim.log.levels.ERROR)
return return
end end
@ -442,7 +442,7 @@ function M.sync()
store.save() store.save()
vim.notify( vim.notify(
string.format( string.format(
'todo.nvim: Synced to Google Calendar (created: %d, updated: %d, deleted: %d)', 'pending.nvim: Synced to Google Calendar (created: %d, updated: %d, deleted: %d)',
created, created,
updated, updated,
deleted deleted

View file

@ -1,6 +1,6 @@
local config = require('todo.config') local config = require('pending.config')
---@class todo.LineMeta ---@class pending.LineMeta
---@field type 'task'|'header'|'blank' ---@field type 'task'|'header'|'blank'
---@field id? integer ---@field id? integer
---@field due? string ---@field due? string
@ -8,7 +8,7 @@ local config = require('todo.config')
---@field status? string ---@field status? string
---@field category? string ---@field category? string
---@class todo.views ---@class pending.views
local M = {} local M = {}
---@param due? string ---@param due? string
@ -25,7 +25,7 @@ local function format_due(due)
return os.date(config.get().date_format, t) return os.date(config.get().date_format, t)
end end
---@param tasks todo.Task[] ---@param tasks pending.Task[]
local function sort_tasks(tasks) local function sort_tasks(tasks)
table.sort(tasks, function(a, b) table.sort(tasks, function(a, b)
if a.priority ~= b.priority then if a.priority ~= b.priority then
@ -38,7 +38,7 @@ local function sort_tasks(tasks)
end) end)
end end
---@param tasks todo.Task[] ---@param tasks pending.Task[]
local function sort_tasks_priority(tasks) local function sort_tasks_priority(tasks)
table.sort(tasks, function(a, b) table.sort(tasks, function(a, b)
if a.priority ~= b.priority then if a.priority ~= b.priority then
@ -62,9 +62,9 @@ local function sort_tasks_priority(tasks)
end) end)
end end
---@param tasks todo.Task[] ---@param tasks pending.Task[]
---@return string[] lines ---@return string[] lines
---@return todo.LineMeta[] meta ---@return pending.LineMeta[] meta
function M.category_view(tasks) function M.category_view(tasks)
local by_cat = {} local by_cat = {}
local cat_order = {} local cat_order = {}
@ -130,9 +130,9 @@ function M.category_view(tasks)
return lines, meta return lines, meta
end end
---@param tasks todo.Task[] ---@param tasks pending.Task[]
---@return string[] lines ---@return string[] lines
---@return todo.LineMeta[] meta ---@return pending.LineMeta[] meta
function M.priority_view(tasks) function M.priority_view(tasks)
local pending = {} local pending = {}
local done = {} local done = {}

View file

@ -1,14 +1,14 @@
rockspec_format = '3.0' rockspec_format = '3.0'
package = 'todo.nvim' package = 'pending.nvim'
version = 'scm-1' version = 'scm-1'
source = { source = {
url = 'git+https://github.com/barrettruth/todo.nvim.git', url = 'git+https://github.com/barrettruth/pending.nvim.git',
} }
description = { description = {
summary = 'Oil-like task management for Neovim', summary = 'Oil-like task management for Neovim',
homepage = 'https://github.com/barrettruth/todo.nvim', homepage = 'https://github.com/barrettruth/pending.nvim',
license = 'MIT', license = 'MIT',
} }

39
plugin/pending.lua Normal file
View file

@ -0,0 +1,39 @@
if vim.g.loaded_pending then
return
end
vim.g.loaded_pending = true
vim.api.nvim_create_user_command('Pending', function(opts)
require('pending').command(opts.args)
end, {
nargs = '*',
complete = function(arg_lead, cmd_line)
local subcmds = { 'add', 'sync', 'archive' }
if not cmd_line:match('^Pending%s+%S') then
return vim.tbl_filter(function(s)
return s:find(arg_lead, 1, true) == 1
end, subcmds)
end
return {}
end,
})
vim.keymap.set('n', '<Plug>(pending-open)', function()
require('pending').open()
end)
vim.keymap.set('n', '<Plug>(pending-toggle)', function()
require('pending').toggle_complete()
end)
vim.keymap.set('n', '<Plug>(pending-view)', function()
require('pending.buffer').toggle_view()
end)
vim.keymap.set('n', '<Plug>(pending-priority)', function()
require('pending').toggle_priority()
end)
vim.keymap.set('n', '<Plug>(pending-date)', function()
require('pending').prompt_date()
end)

View file

@ -1,39 +0,0 @@
if vim.g.loaded_todo then
return
end
vim.g.loaded_todo = true
vim.api.nvim_create_user_command('Todo', function(opts)
require('todo').command(opts.args)
end, {
nargs = '*',
complete = function(arg_lead, cmd_line)
local subcmds = { 'add', 'sync', 'archive' }
if not cmd_line:match('^Todo%s+%S') then
return vim.tbl_filter(function(s)
return s:find(arg_lead, 1, true) == 1
end, subcmds)
end
return {}
end,
})
vim.keymap.set('n', '<Plug>(todo-open)', function()
require('todo').open()
end)
vim.keymap.set('n', '<Plug>(todo-toggle)', function()
require('todo').toggle_complete()
end)
vim.keymap.set('n', '<Plug>(todo-view)', function()
require('todo.buffer').toggle_view()
end)
vim.keymap.set('n', '<Plug>(todo-priority)', function()
require('todo').toggle_priority()
end)
vim.keymap.set('n', '<Plug>(todo-date)', function()
require('todo').prompt_date()
end)

View file

@ -1,16 +1,16 @@
require('spec.helpers') require('spec.helpers')
local config = require('todo.config') local config = require('pending.config')
local store = require('todo.store') local store = require('pending.store')
describe('diff', function() describe('diff', function()
local tmpdir local tmpdir
local diff = require('todo.diff') local diff = require('pending.diff')
before_each(function() before_each(function()
tmpdir = vim.fn.tempname() tmpdir = vim.fn.tempname()
vim.fn.mkdir(tmpdir, 'p') vim.fn.mkdir(tmpdir, 'p')
vim.g.todo = { data_path = tmpdir .. '/tasks.json' } vim.g.pending = { data_path = tmpdir .. '/tasks.json' }
config.reset() config.reset()
store.unload() store.unload()
store.load() store.load()
@ -18,7 +18,7 @@ describe('diff', function()
after_each(function() after_each(function()
vim.fn.delete(tmpdir, 'rf') vim.fn.delete(tmpdir, 'rf')
vim.g.todo = nil vim.g.pending = nil
config.reset() config.reset()
end) end)

View file

@ -1,19 +1,19 @@
require('spec.helpers') require('spec.helpers')
local config = require('todo.config') local config = require('pending.config')
describe('parse', function() describe('parse', function()
before_each(function() before_each(function()
vim.g.todo = nil vim.g.pending = nil
config.reset() config.reset()
end) end)
after_each(function() after_each(function()
vim.g.todo = nil vim.g.pending = nil
config.reset() config.reset()
end) end)
local parse = require('todo.parse') local parse = require('pending.parse')
describe('body', function() describe('body', function()
it('returns plain description when no metadata', function() it('returns plain description when no metadata', function()
@ -73,7 +73,7 @@ describe('parse', function()
end) end)
it('uses configurable date syntax', function() it('uses configurable date syntax', function()
vim.g.todo = { date_syntax = 'by' } vim.g.pending = { date_syntax = 'by' }
config.reset() config.reset()
local desc, meta = parse.body('Buy milk by:2026-03-15') local desc, meta = parse.body('Buy milk by:2026-03-15')
assert.are.equal('Buy milk', desc) assert.are.equal('Buy milk', desc)
@ -81,7 +81,7 @@ describe('parse', function()
end) end)
it('ignores old syntax when date_syntax is changed', function() it('ignores old syntax when date_syntax is changed', function()
vim.g.todo = { date_syntax = 'by' } vim.g.pending = { date_syntax = 'by' }
config.reset() config.reset()
local desc, meta = parse.body('Buy milk due:2026-03-15') local desc, meta = parse.body('Buy milk due:2026-03-15')
assert.are.equal('Buy milk due:2026-03-15', desc) assert.are.equal('Buy milk due:2026-03-15', desc)

View file

@ -1,7 +1,7 @@
require('spec.helpers') require('spec.helpers')
local config = require('todo.config') local config = require('pending.config')
local store = require('todo.store') local store = require('pending.store')
describe('store', function() describe('store', function()
local tmpdir local tmpdir
@ -9,14 +9,14 @@ describe('store', function()
before_each(function() before_each(function()
tmpdir = vim.fn.tempname() tmpdir = vim.fn.tempname()
vim.fn.mkdir(tmpdir, 'p') vim.fn.mkdir(tmpdir, 'p')
vim.g.todo = { data_path = tmpdir .. '/tasks.json' } vim.g.pending = { data_path = tmpdir .. '/tasks.json' }
config.reset() config.reset()
store.unload() store.unload()
end) end)
after_each(function() after_each(function()
vim.fn.delete(tmpdir, 'rf') vim.fn.delete(tmpdir, 'rf')
vim.g.todo = nil vim.g.pending = nil
config.reset() config.reset()
end) end)
@ -37,14 +37,14 @@ describe('store', function()
tasks = { tasks = {
{ {
id = 1, id = 1,
description = 'Todo one', description = 'Pending one',
status = 'pending', status = 'pending',
entry = '2026-01-01T00:00:00Z', entry = '2026-01-01T00:00:00Z',
modified = '2026-01-01T00:00:00Z', modified = '2026-01-01T00:00:00Z',
}, },
{ {
id = 2, id = 2,
description = 'Todo two', description = 'Pending two',
status = 'done', status = 'done',
entry = '2026-01-01T00:00:00Z', entry = '2026-01-01T00:00:00Z',
modified = '2026-01-01T00:00:00Z', modified = '2026-01-01T00:00:00Z',
@ -55,7 +55,7 @@ describe('store', function()
local data = store.load() local data = store.load()
assert.are.equal(3, data.next_id) assert.are.equal(3, data.next_id)
assert.are.equal(2, #data.tasks) assert.are.equal(2, #data.tasks)
assert.are.equal('Todo one', data.tasks[1].description) assert.are.equal('Pending one', data.tasks[1].description)
assert.are.equal('done', data.tasks[2].status) assert.are.equal('done', data.tasks[2].status)
end) end)
@ -68,7 +68,7 @@ describe('store', function()
tasks = { tasks = {
{ {
id = 1, id = 1,
description = 'Todo', description = 'Pending',
status = 'pending', status = 'pending',
entry = '2026-01-01T00:00:00Z', entry = '2026-01-01T00:00:00Z',
modified = '2026-01-01T00:00:00Z', modified = '2026-01-01T00:00:00Z',
@ -157,7 +157,7 @@ describe('store', function()
tasks = { tasks = {
{ {
id = 1, id = 1,
description = 'Todo', description = 'Pending',
status = 'pending', status = 'pending',
entry = '2026-01-01T00:00:00Z', entry = '2026-01-01T00:00:00Z',
modified = '2026-01-01T00:00:00Z', modified = '2026-01-01T00:00:00Z',

View file

@ -7,8 +7,8 @@ syntax match taskHeader /^\S.*$/ contains=taskId
syntax match taskPriority /!\ze / contained syntax match taskPriority /!\ze / contained
syntax match taskLine /^\/\d\+\/ .*$/ contains=taskId,taskPriority syntax match taskLine /^\/\d\+\/ .*$/ contains=taskId,taskPriority
highlight default link taskHeader TodoHeader highlight default link taskHeader PendingHeader
highlight default link taskPriority TodoPriority highlight default link taskPriority PendingPriority
highlight default link taskLine Normal highlight default link taskLine Normal
let b:current_syntax = 'task' let b:current_syntax = 'task'