pending.nvim/lua/pending/config.lua
Barrett Ruth b7ce1c05ec
fix: harden sync backends and fix edit recompute (#66)
* refactor(oauth): async coroutine support, pure-Lua PKCE, server hardening

Problem: OAuth module shelled out to openssl for PKCE, used blocking
`vim.system():wait()`, had a weak `os.time()` PRNG seed, and the TCP
callback server leaked on read errors with no timeout.

Solution: Add `M.system()` coroutine wrapper and `M.async()` helper,
replace openssl with `vim.fn.sha256` + `vim.base64.encode`, seed from
`vim.uv.hrtime()`, add `close_server()` guard with 120s timeout, and
close the server on read errors.

* fix(gtasks): async operations, error notifications, buffer refresh

Problem: Sync operations blocked the editor, `push_pass` silently
dropped delete/update/create API errors, and the buffer was not
re-rendered after push/pull/sync.

Solution: Wrap `push`, `pull`, `sync` in `oauth.async()`, add
`vim.notify` for all `push_pass` failure paths, and re-render the
pending buffer after each operation.

* fix(init): edit recompute, filter predicates, sync action listing

Problem: `M.edit()` skipped `_recompute_counts()` after saving,
`compute_hidden_ids` lacked `done`/`pending` predicates, and
`run_sync` defaulted to `sync` instead of listing available actions.

Solution: Replace `s:save()` with `_save_and_notify()` in `M.edit()`,
add `done` and `pending` filter predicates, and list backend actions
when no action is specified.

* refactor(gcal): per-category calendars, async push, error notifications

Problem: gcal used a single hardcoded calendar name, ran synchronously
blocking the editor, and silently dropped some API errors.

Solution: Fetch all calendars and map categories to calendars (creating
on demand), wrap push in `oauth.async()`, notify on individual API
failures, track `_gcal_calendar_id` in `_extra`, and remove the `$`
anchor from `next_day` pattern.

* refactor: formatting fixes, config cleanup, health simplification

Problem: Formatter disagreements in `init.lua` and `gtasks.lua`,
stale `calendar` field in gcal config, and redundant health checks
for data directory existence.

Solution: Apply stylua formatting, remove `calendar` field from
`pending.GcalConfig`, drop data-dir and no-file health messages,
add `done`/`pending` to filter tab-completion candidates.

* docs: update vimdoc for sync refactor, remove demo scripts

Problem: Docs still referenced openssl dependency, defaulting to `sync`
action, and the `calendar` config field. Demo scripts used the old
singleton `store` API.

Solution: Update vimdoc and README to reflect explicit actions, per-
category calendars, and pure-Lua PKCE. Remove stale demo scripts and
update sync specs to match new behavior.

* fix(types): correct LuaLS annotations in oauth and gcal
2026-03-05 11:50:13 -05:00

118 lines
2.6 KiB
Lua

---@class pending.Icons
---@field pending string
---@field done string
---@field priority string
---@field due string
---@field recur string
---@field category string
---@class pending.GcalConfig
---@field credentials_path? string
---@field client_id? string
---@field client_secret? string
---@class pending.GtasksConfig
---@field credentials_path? string
---@field client_id? string
---@field client_secret? string
---@class pending.SyncConfig
---@field gcal? pending.GcalConfig
---@field gtasks? pending.GtasksConfig
---@class pending.Keymaps
---@field close? string|false
---@field toggle? string|false
---@field view? string|false
---@field priority? string|false
---@field date? string|false
---@field undo? string|false
---@field filter? string|false
---@field open_line? string|false
---@field open_line_above? string|false
---@field a_task? string|false
---@field i_task? string|false
---@field a_category? string|false
---@field i_category? string|false
---@field next_header? string|false
---@field prev_header? string|false
---@field next_task? string|false
---@field prev_task? string|false
---@class pending.Config
---@field data_path string
---@field default_view 'category'|'priority'
---@field default_category string
---@field date_format string
---@field date_syntax string
---@field recur_syntax string
---@field someday_date string
---@field category_order? string[]
---@field drawer_height? integer
---@field debug? boolean
---@field keymaps pending.Keymaps
---@field sync? pending.SyncConfig
---@field icons pending.Icons
---@class pending.config
local M = {}
---@type pending.Config
local defaults = {
data_path = vim.fn.stdpath('data') .. '/pending/tasks.json',
default_view = 'category',
default_category = 'Todo',
date_format = '%b %d',
date_syntax = 'due',
recur_syntax = 'rec',
someday_date = '9999-12-30',
category_order = {},
keymaps = {
close = 'q',
toggle = '<CR>',
view = '<Tab>',
priority = '!',
date = 'D',
undo = 'U',
filter = 'F',
open_line = 'o',
open_line_above = 'O',
a_task = 'at',
i_task = 'it',
a_category = 'aC',
i_category = 'iC',
next_header = ']]',
prev_header = '[[',
next_task = ']t',
prev_task = '[t',
},
sync = {},
icons = {
pending = ' ',
done = 'x',
priority = '!',
due = '.',
recur = '~',
category = '#',
},
}
---@type pending.Config?
local _resolved = nil
---@return pending.Config
function M.get()
if _resolved then
return _resolved
end
local user = vim.g.pending or {}
_resolved = vim.tbl_deep_extend('force', defaults, user)
return _resolved
end
---@return nil
function M.reset()
_resolved = nil
end
return M