diff --git a/doc/pending.txt b/doc/pending.txt index 9195c76..a7250ea 100644 --- a/doc/pending.txt +++ b/doc/pending.txt @@ -1500,7 +1500,7 @@ Configuration: ~ >lua vim.g.pending = { forge = { - auto_close = false, + close = false, warn_missing_cli = true, github = { icon = '', @@ -1522,7 +1522,7 @@ Configuration: ~ < Top-level fields: ~ - {auto_close} (boolean, default: false) When true, tasks linked to + {close} (boolean, default: false) When true, tasks linked to closed/merged remote issues are automatically marked done on buffer open. {warn_missing_cli} (boolean, default: true) When true, warns once per @@ -1550,7 +1550,7 @@ than 5 minutes are re-fetched asynchronously. The buffer renders immediately with cached data and updates extmarks when the fetch completes. State pull: ~ -Requires `forge.auto_close = true`. After fetching, if the remote issue/PR +Requires `forge.close = true`. After fetching, if the remote issue/PR is closed or merged and the local task is pending/wip/blocked, the task is automatically marked as done. Disabled by default. One-way: local status changes do not push back to the forge. diff --git a/lua/pending/complete.lua b/lua/pending/complete.lua index 135d1a4..98291ce 100644 --- a/lua/pending/complete.lua +++ b/lua/pending/complete.lua @@ -1,4 +1,5 @@ local config = require('pending.config') +local forge = require('pending.forge') ---@class pending.CompletionItem ---@field word string @@ -109,6 +110,17 @@ local function recur_completions() return result end +---@param source string +---@return boolean +function M._is_forge_source(source) + for _, b in ipairs(forge.backends()) do + if b.shorthand == source then + return true + end + end + return false +end + ---@type string? local _complete_source = nil @@ -128,10 +140,10 @@ function M.omnifunc(findstart, base) { vim.pesc(dk) .. ':([%S]*)$', dk }, { 'cat:([%S]*)$', 'cat' }, { vim.pesc(rk) .. ':([%S]*)$', rk }, - { 'gh:([%S]*)$', 'gh' }, - { 'gl:([%S]*)$', 'gl' }, - { 'cb:([%S]*)$', 'cb' }, } + for _, b in ipairs(forge.backends()) do + table.insert(checks, { vim.pesc(b.shorthand) .. ':([%S]*)$', b.shorthand }) + end for _, check in ipairs(checks) do local start = before:find(check[1]) @@ -172,7 +184,7 @@ function M.omnifunc(findstart, base) table.insert(matches, { word = c.word, menu = '[' .. source .. ']', info = c.info }) end end - elseif source == 'gh' or source == 'gl' or source == 'cb' then + elseif M._is_forge_source(source) then local s = require('pending.buffer').store() if s then local seen = {} diff --git a/lua/pending/config.lua b/lua/pending/config.lua index b1ab639..60775fe 100644 --- a/lua/pending/config.lua +++ b/lua/pending/config.lua @@ -37,9 +37,10 @@ ---@field icon? string ---@field issue_format? string ---@field instances? string[] +---@field shorthand? string ---@class pending.ForgeConfig ----@field auto_close? boolean +---@field close? boolean ---@field warn_missing_cli? boolean ---@field [string] pending.ForgeInstanceConfig @@ -153,7 +154,7 @@ local defaults = { }, sync = {}, forge = { - auto_close = false, + close = false, warn_missing_cli = true, github = { icon = '', diff --git a/lua/pending/forge.lua b/lua/pending/forge.lua index 5ed9b5b..0ecdb51 100644 --- a/lua/pending/forge.lua +++ b/lua/pending/forge.lua @@ -63,6 +63,14 @@ function M.backends() return _backends end +function M._reset_instances() + _instances_resolved = false + _by_shorthand = {} + for _, b in ipairs(_backends) do + _by_shorthand[b.shorthand] = b + end +end + local function _ensure_instances() if _instances_resolved then return @@ -74,17 +82,27 @@ local function _ensure_instances() for _, inst in ipairs(forge_cfg.instances or {}) do _by_host[inst] = backend end + if forge_cfg.shorthand and forge_cfg.shorthand ~= backend.shorthand then + _by_shorthand[backend.shorthand] = nil + backend.shorthand = forge_cfg.shorthand + _by_shorthand[backend.shorthand] = backend + end end end ---@param token string ---@return pending.ForgeRef? function M._parse_shorthand(token) - local prefix, rest = token:match('^(%l%l):(.+)$') - if not prefix then - return nil + _ensure_instances() + local backend, rest + for prefix, b in pairs(_by_shorthand) do + local candidate = token:match('^' .. vim.pesc(prefix) .. ':(.+)$') + if candidate then + backend = b + rest = candidate + break + end end - local backend = _by_shorthand[prefix] if not backend then return nil end @@ -291,7 +309,7 @@ end ---@param s pending.Store function M.refresh(s) local forge_cfg = config.get().forge or {} - if not forge_cfg.auto_close then + if not forge_cfg.close then return end local tasks = s:tasks() @@ -309,7 +327,7 @@ function M.refresh(s) any_fetched = true local forge_cfg = config.get().forge or {} if - forge_cfg.auto_close + forge_cfg.close and (cache.state == 'closed' or cache.state == 'merged') and (task.status == 'pending' or task.status == 'wip' or task.status == 'blocked') then diff --git a/spec/forge_spec.lua b/spec/forge_spec.lua index fac8021..8bd4162 100644 --- a/spec/forge_spec.lua +++ b/spec/forge_spec.lua @@ -391,6 +391,73 @@ describe('forge registry', function() end) end) +describe('custom forge prefixes', function() + local config = require('pending.config') + local complete = require('pending.complete') + + it('parses custom-length shorthand (3+ chars)', function() + local custom = forge.gitea_backend({ + name = 'customforge', + shorthand = 'cgf', + default_host = 'custom.example.com', + }) + forge.register(custom) + + local ref = forge._parse_shorthand('cgf:alice/proj#99') + assert.is_not_nil(ref) + assert.equals('customforge', ref.forge) + assert.equals('alice', ref.owner) + assert.equals('proj', ref.repo) + assert.equals(99, ref.number) + end) + + it('parse_ref dispatches custom-length shorthand', function() + local ref = forge.parse_ref('cgf:alice/proj#5') + assert.is_not_nil(ref) + assert.equals('customforge', ref.forge) + assert.equals(5, ref.number) + end) + + it('find_refs finds custom-length shorthand', function() + local refs = forge.find_refs('Fix cgf:alice/proj#12') + assert.equals(1, #refs) + assert.equals('customforge', refs[1].ref.forge) + assert.equals(12, refs[1].ref.number) + end) + + it('completion returns entries for custom backends', function() + assert.is_true(complete._is_forge_source('cgf')) + end) + + it('config shorthand override re-registers backend', function() + vim.g.pending = { + forge = { + github = { shorthand = 'github' }, + }, + } + config.reset() + forge._reset_instances() + + local ref = forge._parse_shorthand('github:user/repo#1') + assert.is_not_nil(ref) + assert.equals('github', ref.forge) + assert.equals('user', ref.owner) + assert.equals('repo', ref.repo) + assert.equals(1, ref.number) + + assert.is_nil(forge._parse_shorthand('gh:user/repo#1')) + + vim.g.pending = nil + config.reset() + for _, b in ipairs(forge.backends()) do + if b.name == 'github' then + b.shorthand = 'gh' + end + end + forge._reset_instances() + end) +end) + describe('forge diff integration', function() local store = require('pending.store') local diff = require('pending.diff')