fix(diff): preserve due/rec when absent from buffer line (#68)
* fix(diff): preserve due/rec when absent from buffer line Problem: `diff.apply` overwrites `task.due` and `task.recur` with `nil` whenever those fields aren't present as inline tokens in the buffer line. Because metadata is rendered as virtual text (never in the line text), every description edit silently clears due dates and recurrence rules. Solution: Only update `due`, `recur`, and `recur_mode` in the existing- task branch when the parsed entry actually contains them (non-nil). Users can still set/change these inline by typing `due:<date>` or `rec:<rule>`; clearing them requires `:Pending edit <id> -due`. * refactor: remove project-local store discovery Problem: `store.resolve_path()` searched upward for `.pending.json`, silently splitting task data across multiple files depending on CWD. Solution: `resolve_path()` now always returns `config.get().data_path`. Remove `M.init()` and the `:Pending init` command and tab-completion entry. Remove the project-local health message. * refactor: extract log.lua, standardise [pending.nvim]: prefix Problem: Notifications were scattered across files using bare `vim.notify` with inconsistent `pending.nvim: ` prefixes, and the `debug` guard in `textobj.lua` and `init.lua` was duplicated inline. Solution: Add `lua/pending/log.lua` with `info`, `warn`, `error`, and `debug` functions (prefix `[pending.nvim]: `). `log.debug` only fires when `config.debug = true` or the optional `override` param is `true`. Replace all `vim.notify` callsites and remove inline debug guards. * feat(parse): configurable input date formats Problem: `due:` only accepted ISO `YYYY-MM-DD` and built-in keywords; users expecting locale-style dates like `03/15/2026` or `15-Mar-2026` had no way to configure alternative input formats. Solution: Add `input_date_formats` config field (string[]). Each entry is a strftime-like format string supporting `%Y`, `%y`, `%m`, `%d`, `%e`, `%b`, `%B`. Formats are tried in order after built-in keywords fail. When no year specifier is present the current or next year is inferred. Update vimdoc and add 8 parse_spec tests.
This commit is contained in:
parent
b7ce1c05ec
commit
7fb3289b21
16 changed files with 300 additions and 109 deletions
|
|
@ -47,6 +47,7 @@
|
|||
---@field date_syntax string
|
||||
---@field recur_syntax string
|
||||
---@field someday_date string
|
||||
---@field input_date_formats? string[]
|
||||
---@field category_order? string[]
|
||||
---@field drawer_height? integer
|
||||
---@field debug? boolean
|
||||
|
|
|
|||
|
|
@ -121,17 +121,19 @@ function M.apply(lines, s, hidden_ids)
|
|||
task.priority = entry.priority
|
||||
changed = true
|
||||
end
|
||||
if task.due ~= entry.due then
|
||||
if entry.due ~= nil and task.due ~= entry.due then
|
||||
task.due = entry.due
|
||||
changed = true
|
||||
end
|
||||
if task.recur ~= entry.rec then
|
||||
task.recur = entry.rec
|
||||
changed = true
|
||||
end
|
||||
if task.recur_mode ~= entry.rec_mode then
|
||||
task.recur_mode = entry.rec_mode
|
||||
changed = true
|
||||
if entry.rec ~= nil then
|
||||
if task.recur ~= entry.rec then
|
||||
task.recur = entry.rec
|
||||
changed = true
|
||||
end
|
||||
if task.recur_mode ~= entry.rec_mode then
|
||||
task.recur_mode = entry.rec_mode
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
if entry.status and task.status ~= entry.status then
|
||||
task.status = entry.status
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function M.check()
|
|||
return
|
||||
end
|
||||
|
||||
local cfg = config.get()
|
||||
config.get()
|
||||
vim.health.ok('Config loaded')
|
||||
|
||||
local store_ok, store = pcall(require, 'pending.store')
|
||||
|
|
@ -21,9 +21,6 @@ function M.check()
|
|||
|
||||
local resolved_path = store.resolve_path()
|
||||
vim.health.info('Store path: ' .. resolved_path)
|
||||
if resolved_path ~= cfg.data_path then
|
||||
vim.health.info('(project-local store; global path: ' .. cfg.data_path .. ')')
|
||||
end
|
||||
|
||||
if vim.fn.filereadable(resolved_path) == 1 then
|
||||
local s = store.new(resolved_path)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
local buffer = require('pending.buffer')
|
||||
local diff = require('pending.diff')
|
||||
local log = require('pending.log')
|
||||
local parse = require('pending.parse')
|
||||
local store = require('pending.store')
|
||||
|
||||
|
|
@ -328,12 +329,7 @@ function M._setup_buf_mappings(bufnr)
|
|||
|
||||
for name, fn in pairs(motions) do
|
||||
local key = km[name]
|
||||
if cfg.debug then
|
||||
vim.notify(
|
||||
('[pending] mapping motion %s → %s (buf=%d)'):format(name, key or 'nil', bufnr),
|
||||
vim.log.levels.INFO
|
||||
)
|
||||
end
|
||||
log.debug(('mapping motion %s → %s (buf=%d)'):format(name, key or 'nil', bufnr))
|
||||
if key and key ~= false then
|
||||
vim.keymap.set({ 'n', 'x', 'o' }, key --[[@as string]], function()
|
||||
fn(vim.v.count1)
|
||||
|
|
@ -377,7 +373,7 @@ function M.undo_write()
|
|||
local s = get_store()
|
||||
local stack = s:undo_stack()
|
||||
if #stack == 0 then
|
||||
vim.notify('Nothing to undo.', vim.log.levels.WARN)
|
||||
log.warn('Nothing to undo.')
|
||||
return
|
||||
end
|
||||
local state = table.remove(stack)
|
||||
|
|
@ -494,7 +490,7 @@ function M.prompt_date()
|
|||
not due:match('^%d%d%d%d%-%d%d%-%d%d$')
|
||||
and not due:match('^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d$')
|
||||
then
|
||||
vim.notify('Invalid date format. Use YYYY-MM-DD or YYYY-MM-DDThh:mm.', vim.log.levels.ERROR)
|
||||
log.error('Invalid date format. Use YYYY-MM-DD or YYYY-MM-DDThh:mm.')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
@ -508,14 +504,14 @@ end
|
|||
---@return nil
|
||||
function M.add(text)
|
||||
if not text or text == '' then
|
||||
vim.notify('Usage: :Pending add <description>', vim.log.levels.ERROR)
|
||||
log.error('Usage: :Pending add <description>')
|
||||
return
|
||||
end
|
||||
local s = get_store()
|
||||
s:load()
|
||||
local description, metadata = parse.command_add(text)
|
||||
if not description or description == '' then
|
||||
vim.notify('Pending must have a description.', vim.log.levels.ERROR)
|
||||
log.error('Pending must have a description.')
|
||||
return
|
||||
end
|
||||
s:add({
|
||||
|
|
@ -530,7 +526,7 @@ function M.add(text)
|
|||
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
|
||||
buffer.render(bufnr)
|
||||
end
|
||||
vim.notify('Pending added: ' .. description)
|
||||
log.info('Pending added: ' .. description)
|
||||
end
|
||||
|
||||
---@type string[]
|
||||
|
|
@ -548,7 +544,7 @@ end
|
|||
local function run_sync(backend_name, action)
|
||||
local ok, backend = pcall(require, 'pending.sync.' .. backend_name)
|
||||
if not ok then
|
||||
vim.notify('Unknown sync backend: ' .. backend_name, vim.log.levels.ERROR)
|
||||
log.error('Unknown sync backend: ' .. backend_name)
|
||||
return
|
||||
end
|
||||
if not action or action == '' then
|
||||
|
|
@ -559,11 +555,11 @@ local function run_sync(backend_name, action)
|
|||
end
|
||||
end
|
||||
table.sort(actions)
|
||||
vim.notify(backend_name .. ' actions: ' .. table.concat(actions, ', '), vim.log.levels.INFO)
|
||||
log.info(backend_name .. ' actions: ' .. table.concat(actions, ', '))
|
||||
return
|
||||
end
|
||||
if type(backend[action]) ~= 'function' then
|
||||
vim.notify(backend_name .. " backend has no '" .. action .. "' action", vim.log.levels.ERROR)
|
||||
log.error(backend_name .. " backend has no '" .. action .. "' action")
|
||||
return
|
||||
end
|
||||
backend[action]()
|
||||
|
|
@ -601,7 +597,7 @@ function M.archive(days)
|
|||
end
|
||||
s:replace_tasks(kept)
|
||||
_save_and_notify()
|
||||
vim.notify('Archived ' .. archived .. ' tasks.')
|
||||
log.info('Archived ' .. archived .. ' tasks.')
|
||||
local bufnr = buffer.bufnr()
|
||||
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
|
||||
buffer.render(bufnr)
|
||||
|
|
@ -653,7 +649,7 @@ function M.due()
|
|||
end
|
||||
|
||||
if #qf_items == 0 then
|
||||
vim.notify('No due or overdue tasks.')
|
||||
log.info('No due or overdue tasks.')
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -740,16 +736,15 @@ end
|
|||
---@return nil
|
||||
function M.edit(id_str, rest)
|
||||
if not id_str or id_str == '' then
|
||||
vim.notify(
|
||||
'Usage: :Pending edit <id> [due:<date>] [cat:<name>] [rec:<pattern>] [+!] [-!] [-due] [-cat] [-rec]',
|
||||
vim.log.levels.ERROR
|
||||
log.error(
|
||||
'Usage: :Pending edit <id> [due:<date>] [cat:<name>] [rec:<pattern>] [+!] [-!] [-due] [-cat] [-rec]'
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local id = tonumber(id_str)
|
||||
if not id then
|
||||
vim.notify('Invalid task ID: ' .. id_str, vim.log.levels.ERROR)
|
||||
log.error('Invalid task ID: ' .. id_str)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -757,14 +752,13 @@ function M.edit(id_str, rest)
|
|||
s:load()
|
||||
local task = s:get(id)
|
||||
if not task then
|
||||
vim.notify('No task with ID ' .. id .. '.', vim.log.levels.ERROR)
|
||||
log.error('No task with ID ' .. id .. '.')
|
||||
return
|
||||
end
|
||||
|
||||
if not rest or rest == '' then
|
||||
vim.notify(
|
||||
'Usage: :Pending edit <id> [due:<date>] [cat:<name>] [rec:<pattern>] [+!] [-!] [-due] [-cat] [-rec]',
|
||||
vim.log.levels.ERROR
|
||||
log.error(
|
||||
'Usage: :Pending edit <id> [due:<date>] [cat:<name>] [rec:<pattern>] [+!] [-!] [-due] [-cat] [-rec]'
|
||||
)
|
||||
return
|
||||
end
|
||||
|
|
@ -780,7 +774,7 @@ function M.edit(id_str, rest)
|
|||
for _, tok in ipairs(tokens) do
|
||||
local field, value, err = parse_edit_token(tok)
|
||||
if err then
|
||||
vim.notify(err, vim.log.levels.ERROR)
|
||||
log.error(err)
|
||||
return
|
||||
end
|
||||
if field == 'recur' then
|
||||
|
|
@ -831,20 +825,7 @@ function M.edit(id_str, rest)
|
|||
buffer.render(bufnr)
|
||||
end
|
||||
|
||||
vim.notify('Task #' .. id .. ' updated: ' .. table.concat(feedback, ', '))
|
||||
end
|
||||
|
||||
---@return nil
|
||||
function M.init()
|
||||
local path = vim.fn.getcwd() .. '/.pending.json'
|
||||
if vim.fn.filereadable(path) == 1 then
|
||||
vim.notify('pending.nvim: .pending.json already exists', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
local s = store.new(path)
|
||||
s:load()
|
||||
s:save()
|
||||
vim.notify('pending.nvim: created ' .. path)
|
||||
log.info('Task #' .. id .. ' updated: ' .. table.concat(feedback, ', '))
|
||||
end
|
||||
|
||||
---@param args string
|
||||
|
|
@ -872,10 +853,8 @@ function M.command(args)
|
|||
M.filter(rest)
|
||||
elseif cmd == 'undo' then
|
||||
M.undo_write()
|
||||
elseif cmd == 'init' then
|
||||
M.init()
|
||||
else
|
||||
vim.notify('Unknown Pending subcommand: ' .. cmd, vim.log.levels.ERROR)
|
||||
log.error('Unknown Pending subcommand: ' .. cmd)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
30
lua/pending/log.lua
Normal file
30
lua/pending/log.lua
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---@class pending.log
|
||||
local M = {}
|
||||
|
||||
local PREFIX = '[pending.nvim]: '
|
||||
|
||||
---@param msg string
|
||||
function M.info(msg)
|
||||
vim.notify(PREFIX .. msg)
|
||||
end
|
||||
|
||||
---@param msg string
|
||||
function M.warn(msg)
|
||||
vim.notify(PREFIX .. msg, vim.log.levels.WARN)
|
||||
end
|
||||
|
||||
---@param msg string
|
||||
function M.error(msg)
|
||||
vim.notify(PREFIX .. msg, vim.log.levels.ERROR)
|
||||
end
|
||||
|
||||
---@param msg string
|
||||
---@param override? boolean
|
||||
function M.debug(msg, override)
|
||||
local cfg = require('pending.config').get()
|
||||
if cfg.debug or override then
|
||||
vim.notify(PREFIX .. msg, vim.log.levels.DEBUG)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -151,6 +151,105 @@ local function append_time(date_part, time_suffix)
|
|||
return date_part
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return integer?
|
||||
local function month_name_to_num(name)
|
||||
return month_map[name:lower():sub(1, 3)]
|
||||
end
|
||||
|
||||
---@param fmt string
|
||||
---@return string, string[]
|
||||
local function input_format_to_pattern(fmt)
|
||||
local fields = {}
|
||||
local parts = {}
|
||||
local i = 1
|
||||
while i <= #fmt do
|
||||
local c = fmt:sub(i, i)
|
||||
if c == '%' and i < #fmt then
|
||||
local spec = fmt:sub(i + 1, i + 1)
|
||||
if spec == '%' then
|
||||
parts[#parts + 1] = '%%'
|
||||
i = i + 2
|
||||
elseif spec == 'Y' then
|
||||
fields[#fields + 1] = 'year'
|
||||
parts[#parts + 1] = '(%d%d%d%d)'
|
||||
i = i + 2
|
||||
elseif spec == 'y' then
|
||||
fields[#fields + 1] = 'year2'
|
||||
parts[#parts + 1] = '(%d%d)'
|
||||
i = i + 2
|
||||
elseif spec == 'm' then
|
||||
fields[#fields + 1] = 'month_num'
|
||||
parts[#parts + 1] = '(%d%d?)'
|
||||
i = i + 2
|
||||
elseif spec == 'd' or spec == 'e' then
|
||||
fields[#fields + 1] = 'day'
|
||||
parts[#parts + 1] = '(%d%d?)'
|
||||
i = i + 2
|
||||
elseif spec == 'b' or spec == 'B' then
|
||||
fields[#fields + 1] = 'month_name'
|
||||
parts[#parts + 1] = '(%a+)'
|
||||
i = i + 2
|
||||
else
|
||||
parts[#parts + 1] = vim.pesc(c)
|
||||
i = i + 1
|
||||
end
|
||||
else
|
||||
parts[#parts + 1] = vim.pesc(c)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return '^' .. table.concat(parts) .. '$', fields
|
||||
end
|
||||
|
||||
---@param date_input string
|
||||
---@param time_suffix? string
|
||||
---@return string?
|
||||
local function try_input_date_formats(date_input, time_suffix)
|
||||
local fmts = config.get().input_date_formats
|
||||
if not fmts or #fmts == 0 then
|
||||
return nil
|
||||
end
|
||||
local today = os.date('*t') --[[@as osdate]]
|
||||
for _, fmt in ipairs(fmts) do
|
||||
local pat, fields = input_format_to_pattern(fmt)
|
||||
local caps = { date_input:match(pat) }
|
||||
if caps[1] ~= nil then
|
||||
local year, month, day
|
||||
for j = 1, #fields do
|
||||
local field = fields[j]
|
||||
local val = caps[j]
|
||||
if field == 'year' then
|
||||
year = tonumber(val)
|
||||
elseif field == 'year2' then
|
||||
local y = tonumber(val) --[[@as integer]]
|
||||
year = y + (y >= 70 and 1900 or 2000)
|
||||
elseif field == 'month_num' then
|
||||
month = tonumber(val)
|
||||
elseif field == 'day' then
|
||||
day = tonumber(val)
|
||||
elseif field == 'month_name' then
|
||||
month = month_name_to_num(val)
|
||||
end
|
||||
end
|
||||
if month and day then
|
||||
if not year then
|
||||
year = today.year
|
||||
if month < today.month or (month == today.month and day < today.day) then
|
||||
year = year + 1
|
||||
end
|
||||
end
|
||||
local t = os.time({ year = year, month = month, day = day })
|
||||
local check = os.date('*t', t) --[[@as osdate]]
|
||||
if check.year == year and check.month == month and check.day == day then
|
||||
return append_time(os.date('%Y-%m-%d', t) --[[@as string]], time_suffix)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param text string
|
||||
---@return string|nil
|
||||
function M.resolve_date(text)
|
||||
|
|
@ -411,7 +510,7 @@ function M.resolve_date(text)
|
|||
)
|
||||
end
|
||||
|
||||
return nil
|
||||
return try_input_date_formats(date_input, time_suffix)
|
||||
end
|
||||
|
||||
---@param text string
|
||||
|
|
|
|||
|
|
@ -384,14 +384,6 @@ end
|
|||
|
||||
---@return string
|
||||
function M.resolve_path()
|
||||
local results = vim.fs.find('.pending.json', {
|
||||
upward = true,
|
||||
path = vim.fn.getcwd(),
|
||||
type = 'file',
|
||||
})
|
||||
if results and #results > 0 then
|
||||
return results[1]
|
||||
end
|
||||
return config.get().data_path
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
local config = require('pending.config')
|
||||
local log = require('pending.log')
|
||||
local oauth = require('pending.sync.oauth')
|
||||
|
||||
local M = {}
|
||||
|
|
@ -148,7 +149,7 @@ function M.push()
|
|||
|
||||
local calendars, cal_err = get_all_calendars(access_token)
|
||||
if cal_err or not calendars then
|
||||
vim.notify('pending.nvim: ' .. (cal_err or 'failed to fetch calendars'), vim.log.levels.ERROR)
|
||||
log.error(cal_err or 'failed to fetch calendars')
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ function M.push()
|
|||
local del_err =
|
||||
delete_event(access_token, cal_id --[[@as string]], event_id --[[@as string]])
|
||||
if del_err then
|
||||
vim.notify('pending.nvim: gcal delete failed: ' .. del_err, vim.log.levels.WARN)
|
||||
log.warn('gcal delete failed: ' .. del_err)
|
||||
else
|
||||
extra['_gcal_event_id'] = nil
|
||||
extra['_gcal_calendar_id'] = nil
|
||||
|
|
@ -189,21 +190,18 @@ function M.push()
|
|||
if event_id and cal_id then
|
||||
local upd_err = update_event(access_token, cal_id, event_id, task)
|
||||
if upd_err then
|
||||
vim.notify('pending.nvim: gcal update failed: ' .. upd_err, vim.log.levels.WARN)
|
||||
log.warn('gcal update failed: ' .. upd_err)
|
||||
else
|
||||
updated = updated + 1
|
||||
end
|
||||
else
|
||||
local lid, lid_err = find_or_create_calendar(access_token, cat, calendars)
|
||||
if lid_err or not lid then
|
||||
vim.notify(
|
||||
'pending.nvim: gcal calendar failed: ' .. (lid_err or 'unknown'),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
log.warn('gcal calendar failed: ' .. (lid_err or 'unknown'))
|
||||
else
|
||||
local new_id, create_err = create_event(access_token, lid, task)
|
||||
if create_err then
|
||||
vim.notify('pending.nvim: gcal create failed: ' .. create_err, vim.log.levels.WARN)
|
||||
log.warn('gcal create failed: ' .. create_err)
|
||||
elseif new_id then
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
|
|
@ -224,14 +222,7 @@ function M.push()
|
|||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(
|
||||
string.format(
|
||||
'pending.nvim: Google Calendar pushed — +%d ~%d -%d',
|
||||
created,
|
||||
updated,
|
||||
deleted
|
||||
)
|
||||
)
|
||||
log.info(string.format('Google Calendar pushed — +%d ~%d -%d', created, updated, deleted))
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
local config = require('pending.config')
|
||||
local log = require('pending.log')
|
||||
local oauth = require('pending.sync.oauth')
|
||||
|
||||
local M = {}
|
||||
|
|
@ -248,7 +249,7 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
if task.status == 'deleted' and gtid and list_id then
|
||||
local err = delete_gtask(access_token, list_id, gtid)
|
||||
if err then
|
||||
vim.notify('pending.nvim: gtasks delete failed: ' .. err, vim.log.levels.WARN)
|
||||
log.warn('gtasks delete failed: ' .. err)
|
||||
else
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
|
|
@ -265,7 +266,7 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
if gtid and list_id then
|
||||
local err = update_gtask(access_token, list_id, gtid, task_to_gtask(task))
|
||||
if err then
|
||||
vim.notify('pending.nvim: gtasks update failed: ' .. err, vim.log.levels.WARN)
|
||||
log.warn('gtasks update failed: ' .. err)
|
||||
else
|
||||
updated = updated + 1
|
||||
end
|
||||
|
|
@ -275,7 +276,7 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
if not err and lid then
|
||||
local new_id, create_err = create_gtask(access_token, lid, task_to_gtask(task))
|
||||
if create_err then
|
||||
vim.notify('pending.nvim: gtasks create failed: ' .. create_err, vim.log.levels.WARN)
|
||||
log.warn('gtasks create failed: ' .. create_err)
|
||||
elseif new_id then
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
|
|
@ -305,10 +306,7 @@ local function pull_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
for list_name, list_id in pairs(tasklists) do
|
||||
local items, err = list_gtasks(access_token, list_id)
|
||||
if err then
|
||||
vim.notify(
|
||||
'pending.nvim: error fetching list ' .. list_name .. ': ' .. err,
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
log.warn('error fetching list ' .. list_name .. ': ' .. err)
|
||||
else
|
||||
for _, gtask in ipairs(items or {}) do
|
||||
local local_task = by_gtasks_id[gtask.id]
|
||||
|
|
@ -350,7 +348,7 @@ local function sync_setup()
|
|||
end
|
||||
local tasklists, tl_err = get_all_tasklists(access_token)
|
||||
if tl_err or not tasklists then
|
||||
vim.notify('pending.nvim: ' .. (tl_err or 'failed to fetch task lists'), vim.log.levels.ERROR)
|
||||
log.error(tl_err or 'failed to fetch task lists')
|
||||
return nil
|
||||
end
|
||||
local s = require('pending').store()
|
||||
|
|
@ -379,9 +377,7 @@ function M.push()
|
|||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(
|
||||
string.format('pending.nvim: Google Tasks pushed — +%d ~%d -%d', created, updated, deleted)
|
||||
)
|
||||
log.info(string.format('Google Tasks pushed — +%d ~%d -%d', created, updated, deleted))
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
@ -402,7 +398,7 @@ function M.pull()
|
|||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(string.format('pending.nvim: Google Tasks pulled — +%d ~%d', created, updated))
|
||||
log.info(string.format('Google Tasks pulled — +%d ~%d', created, updated))
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
@ -425,9 +421,9 @@ function M.sync()
|
|||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(
|
||||
log.info(
|
||||
string.format(
|
||||
'pending.nvim: Google Tasks synced — push: +%d ~%d -%d, pull: +%d ~%d',
|
||||
'Google Tasks synced — push: +%d ~%d -%d, pull: +%d ~%d',
|
||||
pushed_create,
|
||||
pushed_update,
|
||||
pushed_delete,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
local config = require('pending.config')
|
||||
local log = require('pending.log')
|
||||
|
||||
local TOKEN_URL = 'https://oauth2.googleapis.com/token'
|
||||
local AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
|
||||
|
|
@ -247,7 +248,7 @@ function OAuthClient:get_access_token()
|
|||
if now - obtained > expires - 60 then
|
||||
tokens = self:refresh_access_token(creds, tokens)
|
||||
if not tokens then
|
||||
vim.notify('pending.nvim: Failed to refresh access token.', vim.log.levels.ERROR)
|
||||
log.error('Failed to refresh access token.')
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
|
@ -289,7 +290,7 @@ function OAuthClient:auth()
|
|||
.. '&code_challenge_method=S256'
|
||||
|
||||
vim.ui.open(auth_url)
|
||||
vim.notify('pending.nvim: Opening browser for Google authorization...')
|
||||
log.info('Opening browser for Google authorization...')
|
||||
|
||||
local server = vim.uv.new_tcp()
|
||||
local server_closed = false
|
||||
|
|
@ -337,7 +338,7 @@ function OAuthClient:auth()
|
|||
vim.defer_fn(function()
|
||||
if not server_closed then
|
||||
close_server()
|
||||
vim.notify('pending.nvim: OAuth callback timed out (120s).', vim.log.levels.WARN)
|
||||
log.warn('OAuth callback timed out (120s).')
|
||||
end
|
||||
end, 120000)
|
||||
end
|
||||
|
|
@ -373,19 +374,19 @@ function OAuthClient:_exchange_code(creds, code, code_verifier, port)
|
|||
}, { text = true })
|
||||
|
||||
if result.code ~= 0 then
|
||||
vim.notify('pending.nvim: Token exchange failed.', vim.log.levels.ERROR)
|
||||
log.error('Token exchange failed.')
|
||||
return
|
||||
end
|
||||
|
||||
local ok, decoded = pcall(vim.json.decode, result.stdout or '')
|
||||
if not ok or not decoded.access_token then
|
||||
vim.notify('pending.nvim: Invalid token response.', vim.log.levels.ERROR)
|
||||
log.error('Invalid token response.')
|
||||
return
|
||||
end
|
||||
|
||||
decoded.obtained_at = os.time()
|
||||
self:save_tokens(decoded)
|
||||
vim.notify('pending.nvim: ' .. self.name .. ' authorized successfully.')
|
||||
log.info(self.name .. ' authorized successfully.')
|
||||
end
|
||||
|
||||
---@param opts { name: string, scope: string, port: integer, config_key: string }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
local buffer = require('pending.buffer')
|
||||
local config = require('pending.config')
|
||||
local log = require('pending.log')
|
||||
|
||||
---@class pending.textobj
|
||||
local M = {}
|
||||
|
|
@ -7,9 +8,7 @@ local M = {}
|
|||
---@param ... any
|
||||
---@return nil
|
||||
local function dbg(...)
|
||||
if config.get().debug then
|
||||
vim.notify('[pending.textobj] ' .. string.format(...), vim.log.levels.INFO)
|
||||
end
|
||||
log.debug(string.format(...))
|
||||
end
|
||||
|
||||
---@param lnum integer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue