* fix(buffer): use `default_category` config for empty placeholder
Problem: The empty-buffer fallback hardcoded the category name `TODO`,
ignoring the user's `default_category` config value (default: `Todo`).
Solution: Read `config.get().default_category` at render time and use
that value for both the header line and `LineMeta` category field.
* fix(diff): match optional checkbox char in `parse_buffer` patterns
Problem: `parse_buffer` used `%[.%]` which requires exactly one
character between brackets, failing to parse empty `[]` checkboxes.
Solution: Change to `%[.?%]` so the character is optional, matching
`[]`, `[ ]`, `[x]`, and `[!]` uniformly.
* fix(init): add `nowait` to buffer keymap opts
Problem: Buffer-local mappings like `!` could be swallowed by Neovim's
operator-pending machinery or by global maps sharing a prefix, since
the keymap opts did not include `nowait`.
Solution: Add `nowait = true` to the shared `opts` table used for all
buffer-local mappings in `_setup_buf_mappings`.
* feat(init): allow `:Pending done` with no args to use cursor line
Problem: `:Pending done` required an explicit task ID, making it
awkward to mark the current task done while inside the pending buffer.
Solution: When called with no ID, `M.done()` reads the cursor row from
`buffer.meta()` to resolve the task ID, erroring if the cursor is not
on a saved task line.
* fix(views): populate `priority` field in `LineMeta`
Problem: Both `category_view` and `priority_view` omitted `priority`
from the `LineMeta` they produced. `apply_extmarks` checks `m.priority`
to decide whether to render the priority icon, so it was always nil,
causing the `[ ]` pending-icon overlay to replace the `[!]` buffer text.
Solution: Add `priority = task.priority` to both LineMeta constructors.
* fix(buffer): keep `_meta` in sync when `open_line` inserts a new line
Problem: `open_line` inserted a buffer line without updating `_meta`,
leaving the entry at that row pointing to the task that was shifted
down. Pressing `<CR>` (toggle_complete) would read the stale meta,
find a real task ID, toggle it, and re-render — destroying the unsaved
new line.
Solution: Insert a `{ type = 'blank' }` sentinel into `_meta` at the
new line's position so buffer-local actions see no task there.
* fix(buffer): use task sentinel in `open_line` for better unsaved-task errors
* feat(init): warn on dirty buffer before store-dependent actions
Problem: `toggle_complete`, `toggle_priority`, `prompt_date`, and
`done` (no-args) all read from `buffer.meta()` which is stale whenever
the buffer has unsaved edits, leading to silent no-ops or acting on the
wrong task.
Solution: Add a `require_saved()` guard that emits a `log.warn` and
returns false when the buffer is modified. Each store-dependent action
calls it before touching meta or the store.
* fix(init): guard `view`, `undo`, and `filter` against dirty buffer
Problem: `toggle_view`, `undo_write`, and `filter` all call
`buffer.render()` which rewrites the buffer from the store, silently
discarding any unsaved edits. The previous `require_saved()` change
missed these three entry points.
Solution: Add `require_saved()` to the `view` and `filter` keymap
lambdas and to `M.undo_write()`. Also guard `M.filter()` directly so
`:Pending filter` from the command line is covered too.
* fix(init): improve dirty-buffer warning message
* fix(init): tighten dirty-buffer warning message
* feat(oauth): add `OAuthClient:clear_tokens()` method
Problem: no way to wipe just the token file while keeping credentials
intact — `_wipe()` removed both.
Solution: add `clear_tokens()` that removes only the token file.
* fix(sync): warn instead of auto-reauth when token is missing
Problem: `with_token` silently triggered an OAuth browser flow when no
tokens existed, with no user-facing explanation.
Solution: replace the auto-reauth branch with a `log.warn` directing
the user to run `:Pending auth`.
* feat(init): add `clear` and `reset` actions to `:Pending auth`
Problem: no CLI path existed to wipe stale tokens or reset credentials,
and the `vim.ui.select` backend picker was misleading given shared tokens.
Solution: accept an args string in `M.auth()`, dispatching `clear` to
`clear_tokens()`, `reset` to `_wipe()`, and bare backend names to the
existing auth flow. Remove the picker.
* feat(plugin): add tab completion for `:Pending auth` subcommands
`:Pending auth <Tab>` completes `gcal gtasks clear reset`;
`:Pending auth <backend> <Tab>` completes `clear reset`.
This commit is contained in:
parent
2929b4d8fa
commit
1e2196fe2e
5 changed files with 42 additions and 29 deletions
|
|
@ -918,22 +918,33 @@ function M.edit(id_str, rest)
|
|||
log.info('Task #' .. id .. ' updated: ' .. table.concat(feedback, ', '))
|
||||
end
|
||||
|
||||
---@param args? string
|
||||
---@return nil
|
||||
function M.auth()
|
||||
function M.auth(args)
|
||||
local oauth = require('pending.sync.oauth')
|
||||
vim.ui.select({ 'Google Tasks', 'Google Calendar', 'Google Tasks and Google Calendar' }, {
|
||||
prompt = 'Authenticate with:',
|
||||
}, function(choice)
|
||||
if not choice then
|
||||
return
|
||||
end
|
||||
local parts = {}
|
||||
for w in (args or ''):gmatch('%S+') do
|
||||
table.insert(parts, w)
|
||||
end
|
||||
local action = parts[#parts]
|
||||
if action == parts[1] and (action == 'gtasks' or action == 'gcal') then
|
||||
action = nil
|
||||
end
|
||||
|
||||
if action == 'clear' then
|
||||
oauth.google_client:clear_tokens()
|
||||
log.info('OAuth tokens cleared — run :Pending auth to re-authenticate.')
|
||||
elseif action == 'reset' then
|
||||
oauth.google_client:_wipe()
|
||||
log.info('OAuth tokens and credentials cleared — run :Pending auth to set up from scratch.')
|
||||
else
|
||||
local creds = oauth.google_client:resolve_credentials()
|
||||
if creds.client_id == oauth.BUNDLED_CLIENT_ID then
|
||||
oauth.google_client:setup()
|
||||
else
|
||||
oauth.google_client:auth()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
---@param args string
|
||||
|
|
@ -952,7 +963,7 @@ function M.command(args)
|
|||
local id_str, edit_rest = rest:match('^(%S+)%s*(.*)')
|
||||
M.edit(id_str, edit_rest)
|
||||
elseif cmd == 'auth' then
|
||||
M.auth()
|
||||
M.auth(rest)
|
||||
elseif SYNC_BACKEND_SET[cmd] then
|
||||
local action = rest:match('^(%S+)')
|
||||
run_sync(cmd, action)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue