* refactor(sync): extract backend interface, adapt gcal module
Problem: :Pending sync hardcodes Google Calendar — M.sync() does
pcall(require, 'pending.sync.gcal') and calls gcal.sync() directly.
The config has a flat gcal field. This prevents adding new sync backends
without modifying init.lua.
Solution: Define a backend interface contract (name, auth, sync, health
fields), refactor :Pending sync to dispatch via require('pending.sync.'
.. backend_name), add sync table to config with legacy gcal migration,
rename gcal.authorize to gcal.auth, add gcal.health for checkhealth,
and add tab completion for backend names and actions.
* docs(sync): update vimdoc for backend interface
Problem: Vimdoc documents :Pending sync as a bare command that pushes
to Google Calendar, with no mention of backends or the sync table config.
Solution: Update :Pending sync section to show {backend} [{action}]
syntax with examples, add SYNC BACKENDS section documenting the interface
contract, update config example to use sync.gcal, document legacy gcal
migration, and update health check description.
* test(sync): add backend dispatch tests
Problem: No test coverage for sync dispatch logic, config migration,
or gcal module interface conformance.
Solution: Add spec/sync_spec.lua with tests for: bare sync errors,
empty backend errors, unknown backend errors, unknown action errors,
default-to-sync routing, explicit sync/auth routing, legacy gcal config
migration, explicit sync.gcal precedence, and gcal module interface
fields (name, auth, sync, health).
97 lines
2.3 KiB
Lua
97 lines
2.3 KiB
Lua
---@class pending.GcalConfig
|
|
---@field calendar? string
|
|
---@field credentials_path? string
|
|
|
|
---@class pending.SyncConfig
|
|
---@field gcal? pending.GcalConfig
|
|
|
|
---@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 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 gcal? pending.GcalConfig
|
|
|
|
---@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',
|
|
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 = {},
|
|
}
|
|
|
|
---@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)
|
|
if _resolved.gcal and not (_resolved.sync and _resolved.sync.gcal) then
|
|
_resolved.sync = _resolved.sync or {}
|
|
_resolved.sync.gcal = _resolved.gcal
|
|
end
|
|
return _resolved
|
|
end
|
|
|
|
---@return nil
|
|
function M.reset()
|
|
_resolved = nil
|
|
end
|
|
|
|
return M
|