From d4e4d1499a07d7a33aa611d79175f75824cfdd58 Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:56:11 -0500 Subject: [PATCH] feat: add \`:Pending done \` command (#76) Toggles a task's done/pending status by ID from the command line, matching the buffer \`\` behaviour including recurrence spawning. Tab-completes active task IDs. --- lua/pending/init.lua | 44 ++++++++++++++++++++++++++++++++++++++ lua/pending/sync/oauth.lua | 2 +- plugin/pending.lua | 12 ++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lua/pending/init.lua b/lua/pending/init.lua index 8ede23a..36f5282 100644 --- a/lua/pending/init.lua +++ b/lua/pending/init.lua @@ -430,6 +430,48 @@ function M.toggle_complete() end end +---@param id_str string +---@return nil +function M.done(id_str) + local id = tonumber(id_str) + if not id then + log.error('Invalid task ID: ' .. tostring(id_str)) + return + end + local s = get_store() + s:load() + local task = s:get(id) + if not task then + log.error('No task with ID ' .. id .. '.') + return + end + local was_done = task.status == 'done' + if was_done then + s:update(id, { status = 'pending', ['end'] = vim.NIL }) + else + if task.recur and task.due then + local recur = require('pending.recur') + local mode = task.recur_mode or 'scheduled' + local next_date = recur.next_due(task.due, task.recur, mode) + s:add({ + description = task.description, + category = task.category, + priority = task.priority, + due = next_date, + recur = task.recur, + recur_mode = task.recur_mode, + }) + end + s:update(id, { status = 'done' }) + end + _save_and_notify() + local bufnr = buffer.bufnr() + if bufnr and vim.api.nvim_buf_is_valid(bufnr) then + buffer.render(bufnr) + end + log.info('Task #' .. id .. ' marked ' .. (was_done and 'pending' or 'done')) +end + ---@return nil function M.toggle_priority() local bufnr = buffer.bufnr() @@ -856,6 +898,8 @@ function M.command(args) local cmd, rest = args:match('^(%S+)%s*(.*)') if cmd == 'add' then M.add(rest) + elseif cmd == 'done' then + M.done(rest:match('^(%S+)')) elseif cmd == 'edit' then local id_str, edit_rest = rest:match('^(%S+)%s*(.*)') M.edit(id_str, edit_rest) diff --git a/lua/pending/sync/oauth.lua b/lua/pending/sync/oauth.lua index bc00208..224476b 100644 --- a/lua/pending/sync/oauth.lua +++ b/lua/pending/sync/oauth.lua @@ -350,7 +350,7 @@ end function OAuthClient:auth(on_complete) local creds = self:resolve_credentials() if creds.client_id == BUNDLED_CLIENT_ID then - log.error(self.name .. ': no credentials configured — run :Pending ' .. self.name .. ' setup') + log.error(self.name .. ': no credentials configured — run :Pending auth') return end local port = self.port diff --git a/plugin/pending.lua b/plugin/pending.lua index 93a0b11..d246fba 100644 --- a/plugin/pending.lua +++ b/plugin/pending.lua @@ -167,7 +167,7 @@ end, { nargs = '*', complete = function(arg_lead, cmd_line) local pending = require('pending') - local subcmds = { 'add', 'archive', 'auth', 'due', 'edit', 'filter', 'undo' } + local subcmds = { 'add', 'archive', 'auth', 'done', 'due', 'edit', 'filter', 'undo' } for _, b in ipairs(pending.sync_backends()) do table.insert(subcmds, b) end @@ -200,6 +200,16 @@ end, { end return filtered end + if cmd_line:match('^Pending%s+done%s') then + local store = require('pending.store') + local s = store.new(store.resolve_path()) + s:load() + local ids = {} + for _, task in ipairs(s:active_tasks()) do + table.insert(ids, tostring(task.id)) + end + return filter_candidates(arg_lead, ids) + end if cmd_line:match('^Pending%s+edit') then return complete_edit(arg_lead, cmd_line) end