Merge remote-tracking branch 'origin/main' into docs/sync-s3-auto-auth

This commit is contained in:
Barrett Ruth 2026-03-11 12:25:34 -04:00
commit 8abb7393fc
5 changed files with 127 additions and 2 deletions

View file

@ -1501,6 +1501,7 @@ Configuration: ~
vim.g.pending = {
forge = {
close = false,
validate = false,
warn_missing_cli = true,
github = {
icon = '',
@ -1527,6 +1528,10 @@ Top-level fields: ~
done on buffer open. Only forges with an explicit
per-forge key (e.g. `github = {}`) are checked;
unconfigured forges are skipped entirely.
{validate} (boolean, default: false) When true, new or changed
forge refs are validated on `:w` by fetching metadata.
Logs a warning if the ref is not found, auth fails, or
the CLI is missing.
{warn_missing_cli} (boolean, default: true) When true, warns once per
forge per session if the CLI is missing or fails.

View file

@ -41,6 +41,7 @@
---@class pending.ForgeConfig
---@field close? boolean
---@field validate? boolean
---@field warn_missing_cli? boolean
---@field [string] pending.ForgeInstanceConfig
@ -155,6 +156,7 @@ local defaults = {
sync = {},
forge = {
close = false,
validate = false,
warn_missing_cli = true,
github = {
icon = '',

View file

@ -82,14 +82,25 @@ function M.parse_buffer(lines)
return result
end
---@param a? pending.ForgeRef
---@param b? pending.ForgeRef
---@return boolean
local function refs_equal(a, b)
if not a or not b then
return false
end
return a.forge == b.forge and a.owner == b.owner and a.repo == b.repo and a.number == b.number
end
---@param lines string[]
---@param s pending.Store
---@param hidden_ids? table<integer, true>
---@return nil
---@return pending.ForgeRef[]
function M.apply(lines, s, hidden_ids)
local parsed = M.parse_buffer(lines)
local now = timestamp()
local data = s:data()
local new_refs = {} ---@type pending.ForgeRef[]
local old_by_id = {}
for _, task in ipairs(data.tasks) do
@ -120,6 +131,9 @@ function M.apply(lines, s, hidden_ids)
order = order_counter,
_extra = entry.forge_ref and { _forge_ref = entry.forge_ref } or nil,
})
if entry.forge_ref then
table.insert(new_refs, entry.forge_ref)
end
else
seen_ids[entry.id] = true
local task = old_by_id[entry.id]
@ -154,6 +168,10 @@ function M.apply(lines, s, hidden_ids)
end
end
if entry.forge_ref ~= nil then
local old_ref = task._extra and task._extra._forge_ref or nil
if not refs_equal(old_ref, entry.forge_ref) then
table.insert(new_refs, entry.forge_ref)
end
if not task._extra then
task._extra = {}
end
@ -188,6 +206,9 @@ function M.apply(lines, s, hidden_ids)
order = order_counter,
_extra = entry.forge_ref and { _forge_ref = entry.forge_ref } or nil,
})
if entry.forge_ref then
table.insert(new_refs, entry.forge_ref)
end
end
::continue::
@ -202,6 +223,7 @@ function M.apply(lines, s, hidden_ids)
end
s:save()
return new_refs
end
return M

View file

@ -489,9 +489,13 @@ function M._on_write(bufnr)
if #stack > UNDO_MAX then
table.remove(stack, 1)
end
diff.apply(lines, s, hidden)
local new_refs = diff.apply(lines, s, hidden)
M._recompute_counts()
buffer.render(bufnr)
if new_refs and #new_refs > 0 then
local forge = require('pending.forge')
forge.validate_refs(new_refs)
end
end
---@return nil

View file

@ -275,6 +275,98 @@ describe('diff', function()
assert.are.equal('completion', tasks[1].recur_mode)
end)
it('returns forge refs for new tasks', function()
local lines = {
'# Inbox',
'- [ ] Fix bug gh:user/repo#42',
}
local refs = diff.apply(lines, s)
assert.are.equal(1, #refs)
assert.are.equal('github', refs[1].forge)
assert.are.equal(42, refs[1].number)
end)
it('returns forge refs for changed refs on existing tasks', function()
s:add({
description = 'Fix bug gh:user/repo#1',
_extra = {
_forge_ref = {
forge = 'github',
owner = 'user',
repo = 'repo',
type = 'issue',
number = 1,
url = '',
},
},
})
s:save()
local lines = {
'# Todo',
'/1/- [ ] Fix bug gh:user/repo#99',
}
local refs = diff.apply(lines, s)
assert.are.equal(1, #refs)
assert.are.equal(99, refs[1].number)
end)
it('returns empty when forge ref is unchanged', function()
s:add({
description = 'Fix bug gh:user/repo#42',
_extra = {
_forge_ref = {
forge = 'github',
owner = 'user',
repo = 'repo',
type = 'issue',
number = 42,
url = '',
},
},
})
s:save()
local lines = {
'# Todo',
'/1/- [ ] Fix bug gh:user/repo#42',
}
local refs = diff.apply(lines, s)
assert.are.equal(0, #refs)
end)
it('returns empty for tasks without forge refs', function()
local lines = {
'# Inbox',
'- [ ] Plain task',
}
local refs = diff.apply(lines, s)
assert.are.equal(0, #refs)
end)
it('returns forge refs for duplicated tasks', function()
s:add({
description = 'Fix bug gh:user/repo#42',
_extra = {
_forge_ref = {
forge = 'github',
owner = 'user',
repo = 'repo',
type = 'issue',
number = 42,
url = '',
},
},
})
s:save()
local lines = {
'# Todo',
'/1/- [ ] Fix bug gh:user/repo#42',
'/1/- [ ] Fix bug gh:user/repo#42',
}
local refs = diff.apply(lines, s)
assert.are.equal(1, #refs)
assert.are.equal(42, refs[1].number)
end)
it('clears priority when [N] is removed from buffer line', function()
s:add({ description = 'Task name', priority = 1 })
s:save()