mirror of
https://github.com/harivansh-afk/forge.nvim.git
synced 2026-04-15 05:02:09 +00:00
feat(picker): add multi-backend picker abstraction
Problem: all pickers were tightly coupled to fzf-lua via ANSI strings and fzf-specific action tables, making it impossible to use telescope or snacks.nvim. Solution: introduce `forge.picker` dispatcher with `fzf`, `telescope`, and `snacks` backends. Format functions now return `forge.Segment[]` instead of ANSI strings. `pickers.lua` builds backend-agnostic `forge.PickerEntry[]` and delegates to `forge.picker.pick()`. Backend auto-detection tries fzf-lua, snacks, telescope in order. Commits, branches, and worktree pickers remain fzf-only with graceful fallback.
This commit is contained in:
parent
354c5000c0
commit
fa7cab89af
6 changed files with 826 additions and 599 deletions
|
|
@ -1,6 +1,7 @@
|
|||
local M = {}
|
||||
|
||||
---@class forge.Config
|
||||
---@field picker 'fzf-lua'|'telescope'|'snacks'|'auto'
|
||||
---@field ci forge.CIConfig
|
||||
---@field sources table<string, forge.SourceConfig>
|
||||
---@field keys forge.KeysConfig|false
|
||||
|
|
@ -83,6 +84,7 @@ local M = {}
|
|||
|
||||
---@type forge.Config
|
||||
local DEFAULTS = {
|
||||
picker = 'auto',
|
||||
ci = { lines = 10000 },
|
||||
sources = {},
|
||||
keys = {
|
||||
|
|
@ -538,15 +540,25 @@ local function extract_author(entry, field)
|
|||
return tostring(v or '')
|
||||
end
|
||||
|
||||
local function hl(group, text)
|
||||
local utils = require('fzf-lua.utils')
|
||||
return utils.ansi_from_hl(group, text)
|
||||
---@param secs integer
|
||||
---@return string
|
||||
local function format_duration(secs)
|
||||
if secs < 0 then
|
||||
secs = 0
|
||||
end
|
||||
if secs >= 3600 then
|
||||
return ('%dh%dm'):format(math.floor(secs / 3600), math.floor(secs % 3600 / 60))
|
||||
end
|
||||
if secs >= 60 then
|
||||
return ('%dm%ds'):format(math.floor(secs / 60), secs % 60)
|
||||
end
|
||||
return ('%ds'):format(secs)
|
||||
end
|
||||
|
||||
---@param entry table
|
||||
---@param fields table
|
||||
---@param show_state boolean
|
||||
---@return string
|
||||
---@return forge.Segment[]
|
||||
function M.format_pr(entry, fields, show_state)
|
||||
local display = M.config().display
|
||||
local icons = display.icons
|
||||
|
|
@ -555,7 +567,7 @@ function M.format_pr(entry, fields, show_state)
|
|||
local title = entry[fields.title] or ''
|
||||
local author = extract_author(entry, fields.author)
|
||||
local age = relative_time(entry[fields.created_at])
|
||||
local prefix = ''
|
||||
local segments = {}
|
||||
if show_state then
|
||||
local state = (entry[fields.state] or ''):lower()
|
||||
local icon, group
|
||||
|
|
@ -566,20 +578,22 @@ function M.format_pr(entry, fields, show_state)
|
|||
else
|
||||
icon, group = icons.closed, 'ForgeClosed'
|
||||
end
|
||||
prefix = hl(group, icon) .. ' '
|
||||
table.insert(segments, { icon, group })
|
||||
table.insert(segments, { ' ' })
|
||||
end
|
||||
return prefix
|
||||
.. hl('ForgeNumber', ('#%-5s'):format(num))
|
||||
.. ' '
|
||||
.. pad_or_truncate(title, widths.title)
|
||||
.. ' '
|
||||
.. hl('ForgeDim', pad_or_truncate(author, widths.author) .. (' %3s'):format(age))
|
||||
table.insert(segments, { ('#%-5s'):format(num), 'ForgeNumber' })
|
||||
table.insert(segments, { ' ' .. pad_or_truncate(title, widths.title) .. ' ' })
|
||||
table.insert(segments, {
|
||||
pad_or_truncate(author, widths.author) .. (' %3s'):format(age),
|
||||
'ForgeDim',
|
||||
})
|
||||
return segments
|
||||
end
|
||||
|
||||
---@param entry table
|
||||
---@param fields table
|
||||
---@param show_state boolean
|
||||
---@return string
|
||||
---@return forge.Segment[]
|
||||
function M.format_issue(entry, fields, show_state)
|
||||
local display = M.config().display
|
||||
local icons = display.icons
|
||||
|
|
@ -588,7 +602,7 @@ function M.format_issue(entry, fields, show_state)
|
|||
local title = entry[fields.title] or ''
|
||||
local author = extract_author(entry, fields.author)
|
||||
local age = relative_time(entry[fields.created_at])
|
||||
local prefix = ''
|
||||
local segments = {}
|
||||
if show_state then
|
||||
local state = (entry[fields.state] or ''):lower()
|
||||
local icon, group
|
||||
|
|
@ -597,18 +611,20 @@ function M.format_issue(entry, fields, show_state)
|
|||
else
|
||||
icon, group = icons.closed, 'ForgeClosed'
|
||||
end
|
||||
prefix = hl(group, icon) .. ' '
|
||||
table.insert(segments, { icon, group })
|
||||
table.insert(segments, { ' ' })
|
||||
end
|
||||
return prefix
|
||||
.. hl('ForgeNumber', ('#%-5s'):format(num))
|
||||
.. ' '
|
||||
.. pad_or_truncate(title, widths.title)
|
||||
.. ' '
|
||||
.. hl('ForgeDim', pad_or_truncate(author, widths.author) .. (' %3s'):format(age))
|
||||
table.insert(segments, { ('#%-5s'):format(num), 'ForgeNumber' })
|
||||
table.insert(segments, { ' ' .. pad_or_truncate(title, widths.title) .. ' ' })
|
||||
table.insert(segments, {
|
||||
pad_or_truncate(author, widths.author) .. (' %3s'):format(age),
|
||||
'ForgeDim',
|
||||
})
|
||||
return segments
|
||||
end
|
||||
|
||||
---@param check table
|
||||
---@return string
|
||||
---@return forge.Segment[]
|
||||
function M.format_check(check)
|
||||
local display = M.config().display
|
||||
local icons = display.icons
|
||||
|
|
@ -632,23 +648,18 @@ function M.format_check(check)
|
|||
local ok_s, ts = pcall(vim.fn.strptime, '%Y-%m-%dT%H:%M:%SZ', check.startedAt)
|
||||
local ok_e, te = pcall(vim.fn.strptime, '%Y-%m-%dT%H:%M:%SZ', check.completedAt)
|
||||
if ok_s and ok_e and ts > 0 and te > 0 then
|
||||
local secs = te - ts
|
||||
if secs >= 60 then
|
||||
elapsed = ('%dm%ds'):format(math.floor(secs / 60), secs % 60)
|
||||
else
|
||||
elapsed = ('%ds'):format(secs)
|
||||
elapsed = format_duration(te - ts)
|
||||
end
|
||||
end
|
||||
end
|
||||
return hl(group, icon)
|
||||
.. ' '
|
||||
.. pad_or_truncate(name, widths.name)
|
||||
.. ' '
|
||||
.. hl('ForgeDim', elapsed)
|
||||
return {
|
||||
{ icon, group },
|
||||
{ ' ' .. pad_or_truncate(name, widths.name) .. ' ' },
|
||||
{ elapsed, 'ForgeDim' },
|
||||
}
|
||||
end
|
||||
|
||||
---@param run forge.CIRun
|
||||
---@return string
|
||||
---@return forge.Segment[]
|
||||
function M.format_run(run)
|
||||
local display = M.config().display
|
||||
local icons = display.icons
|
||||
|
|
@ -670,19 +681,18 @@ function M.format_run(run)
|
|||
local age = relative_time(run.created_at)
|
||||
if run.branch ~= '' then
|
||||
local name_w = widths.name - widths.branch + 10
|
||||
return hl(group, icon)
|
||||
.. ' '
|
||||
.. pad_or_truncate(run.name, name_w)
|
||||
.. ' '
|
||||
.. hl('ForgeBranch', pad_or_truncate(run.branch, widths.branch))
|
||||
.. ' '
|
||||
.. hl('ForgeDim', ('%-6s'):format(event) .. ' ' .. age)
|
||||
return {
|
||||
{ icon, group },
|
||||
{ ' ' .. pad_or_truncate(run.name, name_w) .. ' ' },
|
||||
{ pad_or_truncate(run.branch, widths.branch), 'ForgeBranch' },
|
||||
{ ' ' .. ('%-6s'):format(event) .. ' ' .. age, 'ForgeDim' },
|
||||
}
|
||||
end
|
||||
return hl(group, icon)
|
||||
.. ' '
|
||||
.. pad_or_truncate(run.name, widths.name)
|
||||
.. ' '
|
||||
.. hl('ForgeDim', ('%-6s'):format(event) .. ' ' .. age)
|
||||
return {
|
||||
{ icon, group },
|
||||
{ ' ' .. pad_or_truncate(run.name, widths.name) .. ' ' },
|
||||
{ ('%-6s'):format(event) .. ' ' .. age, 'ForgeDim' },
|
||||
}
|
||||
end
|
||||
|
||||
---@param checks table[]
|
||||
|
|
@ -715,6 +725,9 @@ function M.config()
|
|||
cfg.keys = false
|
||||
end
|
||||
|
||||
vim.validate('forge.picker', cfg.picker, function(v)
|
||||
return v == 'auto' or v == 'fzf-lua' or v == 'telescope' or v == 'snacks'
|
||||
end, "'auto', 'fzf-lua', 'telescope', or 'snacks'")
|
||||
vim.validate('forge.sources', cfg.sources, 'table')
|
||||
vim.validate('forge.keys', cfg.keys, function(v)
|
||||
return v == false or type(v) == 'table'
|
||||
|
|
|
|||
76
lua/forge/picker/fzf.lua
Normal file
76
lua/forge/picker/fzf.lua
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
local M = {}
|
||||
|
||||
local fzf_args = (vim.env.FZF_DEFAULT_OPTS or '')
|
||||
:gsub('%-%-bind=[^%s]+', '')
|
||||
:gsub('%-%-color=[^%s]+', '')
|
||||
|
||||
---@param key string
|
||||
---@return string
|
||||
local function to_fzf_key(key)
|
||||
if key == '<cr>' then
|
||||
return 'default'
|
||||
end
|
||||
local result = key:gsub('<c%-(%a)>', function(ch)
|
||||
return 'ctrl-' .. ch:lower()
|
||||
end)
|
||||
return result
|
||||
end
|
||||
|
||||
---@param segments forge.Segment[]
|
||||
---@return string
|
||||
local function render(segments)
|
||||
local utils = require('fzf-lua.utils')
|
||||
local parts = {}
|
||||
for _, seg in ipairs(segments) do
|
||||
if seg[2] then
|
||||
table.insert(parts, utils.ansi_from_hl(seg[2], seg[1]))
|
||||
else
|
||||
table.insert(parts, seg[1])
|
||||
end
|
||||
end
|
||||
return table.concat(parts)
|
||||
end
|
||||
|
||||
---@param opts forge.PickerOpts
|
||||
function M.pick(opts)
|
||||
local cfg = require('forge').config()
|
||||
local keys = cfg.keys
|
||||
if keys == false then
|
||||
keys = {}
|
||||
end
|
||||
local bindings = keys[opts.picker_name] or {}
|
||||
|
||||
local lines = {}
|
||||
for i, entry in ipairs(opts.entries) do
|
||||
lines[i] = ('%d\t%s'):format(i, render(entry.display))
|
||||
end
|
||||
|
||||
local fzf_actions = {}
|
||||
for _, def in ipairs(opts.actions) do
|
||||
local key = def.name == 'default' and '<cr>' or bindings[def.name]
|
||||
if key then
|
||||
fzf_actions[to_fzf_key(key)] = function(selected)
|
||||
if not selected[1] then
|
||||
def.fn(nil)
|
||||
return
|
||||
end
|
||||
local idx = tonumber(selected[1]:match('^(%d+)'))
|
||||
def.fn(idx and opts.entries[idx] or nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require('fzf-lua').fzf_exec(lines, {
|
||||
fzf_args = fzf_args,
|
||||
prompt = opts.prompt or '',
|
||||
fzf_opts = {
|
||||
['--ansi'] = '',
|
||||
['--no-multi'] = '',
|
||||
['--with-nth'] = '2..',
|
||||
['--delimiter'] = '\t',
|
||||
},
|
||||
actions = fzf_actions,
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
80
lua/forge/picker/init.lua
Normal file
80
lua/forge/picker/init.lua
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
local M = {}
|
||||
|
||||
---@alias forge.Segment {[1]: string, [2]: string?}
|
||||
|
||||
---@class forge.PickerEntry
|
||||
---@field display forge.Segment[]
|
||||
---@field value any
|
||||
---@field ordinal string?
|
||||
|
||||
---@class forge.PickerActionDef
|
||||
---@field name string
|
||||
---@field fn fun(entry: forge.PickerEntry?)
|
||||
|
||||
---@class forge.PickerOpts
|
||||
---@field prompt string?
|
||||
---@field entries forge.PickerEntry[]
|
||||
---@field actions forge.PickerActionDef[]
|
||||
---@field picker_name string
|
||||
|
||||
---@type table<string, string>
|
||||
local backends = {
|
||||
['fzf-lua'] = 'forge.picker.fzf',
|
||||
telescope = 'forge.picker.telescope',
|
||||
snacks = 'forge.picker.snacks',
|
||||
}
|
||||
|
||||
---@return string
|
||||
local function detect()
|
||||
local cfg = require('forge').config()
|
||||
local name = cfg.picker or 'auto'
|
||||
if name ~= 'auto' then
|
||||
return name
|
||||
end
|
||||
if pcall(require, 'fzf-lua') then
|
||||
return 'fzf-lua'
|
||||
end
|
||||
if pcall(require, 'snacks') then
|
||||
return 'snacks'
|
||||
end
|
||||
if pcall(require, 'telescope') then
|
||||
return 'telescope'
|
||||
end
|
||||
return 'fzf-lua'
|
||||
end
|
||||
|
||||
---@param entry forge.PickerEntry
|
||||
---@return string
|
||||
function M.ordinal(entry)
|
||||
if entry.ordinal then
|
||||
return entry.ordinal
|
||||
end
|
||||
local parts = {}
|
||||
for _, seg in ipairs(entry.display) do
|
||||
table.insert(parts, seg[1])
|
||||
end
|
||||
return table.concat(parts)
|
||||
end
|
||||
|
||||
---@return string
|
||||
function M.backend()
|
||||
return detect()
|
||||
end
|
||||
|
||||
---@param opts forge.PickerOpts
|
||||
function M.pick(opts)
|
||||
local name = detect()
|
||||
local mod_path = backends[name]
|
||||
if not mod_path then
|
||||
vim.notify('[forge]: unknown picker backend: ' .. name, vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
local ok, backend = pcall(require, mod_path)
|
||||
if not ok then
|
||||
vim.notify('[forge]: picker backend ' .. name .. ' not available', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
backend.pick(opts)
|
||||
end
|
||||
|
||||
return M
|
||||
64
lua/forge/picker/snacks.lua
Normal file
64
lua/forge/picker/snacks.lua
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
local M = {}
|
||||
|
||||
---@param opts forge.PickerOpts
|
||||
function M.pick(opts)
|
||||
local Snacks = require('snacks')
|
||||
local picker_mod = require('forge.picker')
|
||||
|
||||
local cfg = require('forge').config()
|
||||
local keys = cfg.keys
|
||||
if keys == false then
|
||||
keys = {}
|
||||
end
|
||||
local bindings = keys[opts.picker_name] or {}
|
||||
|
||||
local items = {}
|
||||
for i, entry in ipairs(opts.entries) do
|
||||
items[i] = {
|
||||
idx = i,
|
||||
text = picker_mod.ordinal(entry),
|
||||
value = entry,
|
||||
}
|
||||
end
|
||||
|
||||
local snacks_actions = {}
|
||||
local input_keys = {}
|
||||
local list_keys = {}
|
||||
for _, def in ipairs(opts.actions) do
|
||||
local key = def.name == 'default' and '<cr>' or bindings[def.name]
|
||||
if key then
|
||||
local action_name = 'forge_' .. def.name
|
||||
snacks_actions[action_name] = function(picker)
|
||||
local item = picker:current()
|
||||
picker:close()
|
||||
def.fn(item and item.value or nil)
|
||||
end
|
||||
if key == '<cr>' then
|
||||
snacks_actions['confirm'] = snacks_actions[action_name]
|
||||
else
|
||||
-- selene: allow(mixed_table)
|
||||
input_keys[key] = { action_name, mode = { 'i', 'n' } }
|
||||
list_keys[key] = action_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Snacks.picker({
|
||||
items = items,
|
||||
prompt = opts.prompt,
|
||||
format = function(item)
|
||||
local ret = {}
|
||||
for _, seg in ipairs(item.value.display) do
|
||||
table.insert(ret, { seg[1], seg[2] or 'Normal' })
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
actions = snacks_actions,
|
||||
win = {
|
||||
input = { keys = input_keys },
|
||||
list = { keys = list_keys },
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
69
lua/forge/picker/telescope.lua
Normal file
69
lua/forge/picker/telescope.lua
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
local M = {}
|
||||
|
||||
---@param opts forge.PickerOpts
|
||||
function M.pick(opts)
|
||||
local pickers = require('telescope.pickers')
|
||||
local finders = require('telescope.finders')
|
||||
local conf = require('telescope.config').values
|
||||
local actions = require('telescope.actions')
|
||||
local action_state = require('telescope.actions.state')
|
||||
local picker_mod = require('forge.picker')
|
||||
|
||||
local cfg = require('forge').config()
|
||||
local keys = cfg.keys
|
||||
if keys == false then
|
||||
keys = {}
|
||||
end
|
||||
local bindings = keys[opts.picker_name] or {}
|
||||
|
||||
local finder = finders.new_table({
|
||||
results = opts.entries,
|
||||
entry_maker = function(entry)
|
||||
return {
|
||||
value = entry,
|
||||
ordinal = picker_mod.ordinal(entry),
|
||||
display = function(tbl)
|
||||
local text = ''
|
||||
local hl_list = {}
|
||||
for _, seg in ipairs(tbl.value.display) do
|
||||
local start = #text
|
||||
text = text .. seg[1]
|
||||
if seg[2] then
|
||||
table.insert(hl_list, { { start, #text }, seg[2] })
|
||||
end
|
||||
end
|
||||
return text, hl_list
|
||||
end,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
pickers
|
||||
.new({}, {
|
||||
prompt_title = (opts.prompt or ''):gsub('[>%s]+$', ''),
|
||||
finder = finder,
|
||||
sorter = conf.generic_sorter({}),
|
||||
attach_mappings = function(prompt_bufnr, map)
|
||||
for _, def in ipairs(opts.actions) do
|
||||
local key = def.name == 'default' and '<cr>' or bindings[def.name]
|
||||
if key then
|
||||
local function action_fn()
|
||||
local entry = action_state.get_selected_entry()
|
||||
actions.close(prompt_bufnr)
|
||||
def.fn(entry and entry.value or nil)
|
||||
end
|
||||
if key == '<cr>' then
|
||||
actions.select_default:replace(action_fn)
|
||||
else
|
||||
map('i', key, action_fn)
|
||||
map('n', key, action_fn)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end,
|
||||
})
|
||||
:find()
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
local M = {}
|
||||
|
||||
local picker = require('forge.picker')
|
||||
|
||||
---@param result { code: integer, stdout: string?, stderr: string? }
|
||||
---@param fallback string
|
||||
---@return string
|
||||
|
|
@ -15,36 +17,6 @@ local function cmd_error(result, fallback)
|
|||
return msg
|
||||
end
|
||||
|
||||
local fzf_args = (vim.env.FZF_DEFAULT_OPTS or '')
|
||||
:gsub('%-%-bind=[^%s]+', '')
|
||||
:gsub('%-%-color=[^%s]+', '')
|
||||
|
||||
local function to_fzf_key(key)
|
||||
if key == '<cr>' then
|
||||
return 'default'
|
||||
end
|
||||
return key:gsub('<c%-(%a)>', function(ch)
|
||||
return 'ctrl-' .. ch:lower()
|
||||
end)
|
||||
end
|
||||
|
||||
local function build_actions(picker_name, action_defs)
|
||||
local cfg = require('forge').config()
|
||||
local keys = cfg.keys
|
||||
if keys == false then
|
||||
keys = {}
|
||||
end
|
||||
local bindings = keys[picker_name] or {}
|
||||
local actions = {}
|
||||
for _, def in ipairs(action_defs) do
|
||||
local key = bindings[def.name]
|
||||
if key then
|
||||
actions[to_fzf_key(key)] = def.fn
|
||||
end
|
||||
end
|
||||
return actions
|
||||
end
|
||||
|
||||
---@param kind string
|
||||
---@param num string
|
||||
---@param label string
|
||||
|
|
@ -79,13 +51,10 @@ end
|
|||
---@param f forge.Forge
|
||||
---@param num string
|
||||
---@return table<string, function>
|
||||
local function pr_actions(f, num)
|
||||
local function pr_action_fns(f, num)
|
||||
local kind = f.labels.pr_one
|
||||
|
||||
local defs = {
|
||||
{
|
||||
name = 'checkout',
|
||||
fn = function()
|
||||
return {
|
||||
checkout = function()
|
||||
local forge_mod = require('forge')
|
||||
forge_mod.log_now(('checking out %s #%s...'):format(kind, num))
|
||||
vim.system(f:checkout_cmd(num), { text = true }, function(result)
|
||||
|
|
@ -99,16 +68,10 @@ local function pr_actions(f, num)
|
|||
end)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function()
|
||||
browse = function()
|
||||
f:view_web(f.kinds.pr, num)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'worktree',
|
||||
fn = function()
|
||||
worktree = function()
|
||||
local forge_mod = require('forge')
|
||||
local fetch_cmd = f:fetch_pr(num)
|
||||
local branch = fetch_cmd[#fetch_cmd]:match(':(.+)$')
|
||||
|
|
@ -119,29 +82,19 @@ local function pr_actions(f, num)
|
|||
local wt_path = vim.fs.normalize(root .. '/../' .. branch)
|
||||
forge_mod.log_now(('fetching %s #%s into worktree...'):format(kind, num))
|
||||
vim.system(fetch_cmd, { text = true }, function()
|
||||
vim.system(
|
||||
{ 'git', 'worktree', 'add', wt_path, branch },
|
||||
{ text = true },
|
||||
function(result)
|
||||
vim.system({ 'git', 'worktree', 'add', wt_path, branch }, { text = true }, function(result)
|
||||
vim.schedule(function()
|
||||
if result.code == 0 then
|
||||
vim.notify(('[forge]: worktree at %s'):format(wt_path))
|
||||
else
|
||||
vim.notify(
|
||||
'[forge]: ' .. cmd_error(result, 'worktree failed'),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
vim.notify('[forge]: ' .. cmd_error(result, 'worktree failed'), vim.log.levels.ERROR)
|
||||
end
|
||||
vim.cmd.redraw()
|
||||
end)
|
||||
end
|
||||
)
|
||||
end)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'diff',
|
||||
fn = function()
|
||||
diff = function()
|
||||
local forge_mod = require('forge')
|
||||
local review = require('forge.review')
|
||||
local repo_root = vim.trim(vim.fn.system('git rev-parse --show-toplevel'))
|
||||
|
|
@ -171,10 +124,7 @@ local function pr_actions(f, num)
|
|||
end)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'ci',
|
||||
fn = function()
|
||||
ci = function()
|
||||
if f.capabilities.per_pr_checks then
|
||||
M.checks(f, num)
|
||||
else
|
||||
|
|
@ -184,25 +134,10 @@ local function pr_actions(f, num)
|
|||
M.ci(f)
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'manage',
|
||||
fn = function()
|
||||
manage = function()
|
||||
M.pr_manage(f, num)
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
---@type table<string, function>
|
||||
local name_to_fn = {}
|
||||
for _, def in ipairs(defs) do
|
||||
name_to_fn[def.name] = def.fn
|
||||
end
|
||||
|
||||
local actions = build_actions('pr', defs)
|
||||
---@type table<string, function>
|
||||
actions._by_name = name_to_fn
|
||||
return actions
|
||||
end
|
||||
|
||||
---@param f forge.Forge
|
||||
|
|
@ -223,7 +158,10 @@ local function pr_manage_picker(f, num)
|
|||
local action_map = {}
|
||||
|
||||
local function add(label, fn)
|
||||
table.insert(entries, label)
|
||||
table.insert(entries, {
|
||||
display = { { label } },
|
||||
value = label,
|
||||
})
|
||||
action_map[label] = fn
|
||||
end
|
||||
|
||||
|
|
@ -267,17 +205,20 @@ local function pr_manage_picker(f, num)
|
|||
end)
|
||||
end
|
||||
|
||||
require('fzf-lua').fzf_exec(entries, {
|
||||
fzf_args = fzf_args,
|
||||
picker.pick({
|
||||
prompt = ('%s #%s Actions> '):format(kind, num),
|
||||
fzf_opts = { ['--no-multi'] = '' },
|
||||
entries = entries,
|
||||
actions = {
|
||||
['default'] = function(selected)
|
||||
if selected[1] and action_map[selected[1]] then
|
||||
action_map[selected[1]]()
|
||||
{
|
||||
name = 'default',
|
||||
fn = function(entry)
|
||||
if entry and action_map[entry.value] then
|
||||
action_map[entry.value]()
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
picker_name = '_menu',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -291,18 +232,13 @@ function M.checks(f, num, filter, cached_checks)
|
|||
|
||||
local function open_picker(checks)
|
||||
local filtered = forge_mod.filter_checks(checks, filter)
|
||||
local lines = {}
|
||||
for i, c in ipairs(filtered) do
|
||||
local line = ('%d\t%s'):format(i, forge_mod.format_check(c))
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local function get_check(selected)
|
||||
if not selected[1] then
|
||||
return nil
|
||||
end
|
||||
local idx = tonumber(selected[1]:match('^(%d+)'))
|
||||
return idx and filtered[idx] or nil
|
||||
local entries = {}
|
||||
for _, c in ipairs(filtered) do
|
||||
table.insert(entries, {
|
||||
display = forge_mod.format_check(c),
|
||||
value = c,
|
||||
ordinal = c.name or '',
|
||||
})
|
||||
end
|
||||
|
||||
local labels = {
|
||||
|
|
@ -312,14 +248,17 @@ function M.checks(f, num, filter, cached_checks)
|
|||
pending = 'running',
|
||||
}
|
||||
|
||||
local check_actions = build_actions('ci', {
|
||||
picker.pick({
|
||||
prompt = ('Checks (#%s, %s)> '):format(num, labels[filter] or filter),
|
||||
entries = entries,
|
||||
actions = {
|
||||
{
|
||||
name = 'log',
|
||||
fn = function(selected)
|
||||
local c = get_check(selected)
|
||||
if not c then
|
||||
fn = function(entry)
|
||||
if not entry then
|
||||
return
|
||||
end
|
||||
local c = entry.value
|
||||
local run_id = (c.link or ''):match('/actions/runs/(%d+)')
|
||||
if not run_id then
|
||||
return
|
||||
|
|
@ -346,10 +285,9 @@ function M.checks(f, num, filter, cached_checks)
|
|||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
local c = get_check(selected)
|
||||
if c and c.link then
|
||||
vim.ui.open(c.link)
|
||||
fn = function(entry)
|
||||
if entry and entry.value.link then
|
||||
vim.ui.open(entry.value.link)
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
|
@ -377,18 +315,8 @@ function M.checks(f, num, filter, cached_checks)
|
|||
M.checks(f, num, 'all', checks)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
require('fzf-lua').fzf_exec(lines, {
|
||||
fzf_args = fzf_args,
|
||||
prompt = ('Checks (#%s, %s)> '):format(num, labels[filter] or filter),
|
||||
fzf_opts = {
|
||||
['--ansi'] = '',
|
||||
['--no-multi'] = '',
|
||||
['--with-nth'] = '2..',
|
||||
['--delimiter'] = '\t',
|
||||
},
|
||||
actions = check_actions,
|
||||
picker_name = 'ci',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -412,16 +340,7 @@ function M.checks(f, num, filter, cached_checks)
|
|||
end)
|
||||
end)
|
||||
else
|
||||
require('fzf-lua').fzf_exec(f:checks_cmd(num), {
|
||||
fzf_args = fzf_args,
|
||||
prompt = ('Checks (#%s)> '):format(num),
|
||||
fzf_opts = { ['--ansi'] = '' },
|
||||
actions = {
|
||||
['ctrl-r'] = function()
|
||||
M.checks(f, num, filter)
|
||||
end,
|
||||
},
|
||||
})
|
||||
vim.notify('[forge]: structured checks not available for this forge', vim.log.levels.INFO)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -430,33 +349,32 @@ end
|
|||
function M.ci(f, branch)
|
||||
local forge_mod = require('forge')
|
||||
|
||||
local function open_picker(runs)
|
||||
local function open_ci_picker(runs)
|
||||
local normalized = {}
|
||||
for _, entry in ipairs(runs) do
|
||||
table.insert(normalized, f:normalize_run(entry))
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for i, run in ipairs(normalized) do
|
||||
table.insert(lines, ('%d\t%s'):format(i, forge_mod.format_run(run)))
|
||||
local entries = {}
|
||||
for _, run in ipairs(normalized) do
|
||||
table.insert(entries, {
|
||||
display = forge_mod.format_run(run),
|
||||
value = run,
|
||||
ordinal = run.name .. ' ' .. run.branch,
|
||||
})
|
||||
end
|
||||
|
||||
local function get_run(selected)
|
||||
if not selected[1] then
|
||||
return nil
|
||||
end
|
||||
local idx = tonumber(selected[1]:match('^(%d+)'))
|
||||
return idx and normalized[idx] or nil
|
||||
end
|
||||
|
||||
local ci_actions = build_actions('ci', {
|
||||
picker.pick({
|
||||
prompt = ('%s (%s)> '):format(f.labels.ci, branch or 'all'),
|
||||
entries = entries,
|
||||
actions = {
|
||||
{
|
||||
name = 'log',
|
||||
fn = function(selected)
|
||||
local run = get_run(selected)
|
||||
if not run then
|
||||
fn = function(entry)
|
||||
if not entry then
|
||||
return
|
||||
end
|
||||
local run = entry.value
|
||||
forge_mod.log_now('fetching CI/CD logs...')
|
||||
local s = run.status:lower()
|
||||
local cmd
|
||||
|
|
@ -481,10 +399,9 @@ function M.ci(f, branch)
|
|||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
local run = get_run(selected)
|
||||
if run and run.url ~= '' then
|
||||
vim.ui.open(run.url)
|
||||
fn = function(entry)
|
||||
if entry and entry.value.url ~= '' then
|
||||
vim.ui.open(entry.value.url)
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
|
@ -494,18 +411,8 @@ function M.ci(f, branch)
|
|||
M.ci(f, branch)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
require('fzf-lua').fzf_exec(lines, {
|
||||
fzf_args = fzf_args,
|
||||
prompt = ('%s (%s)> '):format(f.labels.ci, branch or 'all'),
|
||||
fzf_opts = {
|
||||
['--ansi'] = '',
|
||||
['--no-multi'] = '',
|
||||
['--with-nth'] = '2..',
|
||||
['--delimiter'] = '\t',
|
||||
},
|
||||
actions = ci_actions,
|
||||
picker_name = 'ci',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -515,7 +422,7 @@ function M.ci(f, branch)
|
|||
vim.schedule(function()
|
||||
local ok, runs = pcall(vim.json.decode, result.stdout or '[]')
|
||||
if ok and runs and #runs > 0 then
|
||||
open_picker(runs)
|
||||
open_ci_picker(runs)
|
||||
else
|
||||
vim.notify('[forge]: no CI runs found', vim.log.levels.INFO)
|
||||
vim.cmd.redraw()
|
||||
|
|
@ -523,11 +430,7 @@ function M.ci(f, branch)
|
|||
end)
|
||||
end)
|
||||
elseif f.list_runs_cmd then
|
||||
require('fzf-lua').fzf_exec(f:list_runs_cmd(branch), {
|
||||
fzf_args = fzf_args,
|
||||
prompt = f.labels.ci .. '> ',
|
||||
fzf_opts = { ['--ansi'] = '' },
|
||||
})
|
||||
vim.notify('[forge]: structured CI data not available for this forge', vim.log.levels.INFO)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -535,6 +438,27 @@ end
|
|||
function M.commits(f)
|
||||
local forge_mod = require('forge')
|
||||
local review = require('forge.review')
|
||||
|
||||
if picker.backend() ~= 'fzf-lua' then
|
||||
vim.notify('[forge]: commits picker requires fzf-lua', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local fzf_args = (vim.env.FZF_DEFAULT_OPTS or '')
|
||||
:gsub('%-%-bind=[^%s]+', '')
|
||||
:gsub('%-%-color=[^%s]+', '')
|
||||
|
||||
local function to_fzf_key(key)
|
||||
if key == '<cr>' then
|
||||
return 'default'
|
||||
end
|
||||
return key:gsub('<c%-(%a)>', function(ch)
|
||||
return 'ctrl-' .. ch:lower()
|
||||
end)
|
||||
end
|
||||
|
||||
local cfg = require('forge').config()
|
||||
local keys = type(cfg.keys) == 'table' and cfg.keys.commits or {}
|
||||
local log_cmd =
|
||||
'git log --color --pretty=format:"%C(yellow)%h%Creset %Cgreen(%><(12)%cr%><|(12))%Creset %s %C(blue)<%an>%Creset"'
|
||||
|
||||
|
|
@ -545,10 +469,9 @@ function M.commits(f)
|
|||
end
|
||||
end
|
||||
|
||||
local defs = {
|
||||
{
|
||||
name = 'checkout',
|
||||
fn = function(selected)
|
||||
local fzf_actions = {}
|
||||
if keys.checkout then
|
||||
fzf_actions[to_fzf_key(keys.checkout)] = function(selected)
|
||||
with_sha(selected, function(sha)
|
||||
forge_mod.log_now('checking out ' .. sha .. '...')
|
||||
vim.system({ 'git', 'checkout', sha }, { text = true }, function(result)
|
||||
|
|
@ -556,20 +479,16 @@ function M.commits(f)
|
|||
if result.code == 0 then
|
||||
vim.notify(('[forge]: checked out %s (detached)'):format(sha))
|
||||
else
|
||||
vim.notify(
|
||||
'[forge]: ' .. cmd_error(result, 'checkout failed'),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
vim.notify('[forge]: ' .. cmd_error(result, 'checkout failed'), vim.log.levels.ERROR)
|
||||
end
|
||||
vim.cmd.redraw()
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'diff',
|
||||
fn = function(selected)
|
||||
end
|
||||
end
|
||||
if keys.diff then
|
||||
fzf_actions[to_fzf_key(keys.diff)] = function(selected)
|
||||
with_sha(selected, function(sha)
|
||||
local range = sha .. '^..' .. sha
|
||||
review.start(range)
|
||||
|
|
@ -579,30 +498,25 @@ function M.commits(f)
|
|||
end
|
||||
forge_mod.log_now('reviewing ' .. sha)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
end
|
||||
end
|
||||
if keys.browse then
|
||||
fzf_actions[to_fzf_key(keys.browse)] = function(selected)
|
||||
with_sha(selected, function(sha)
|
||||
if f then
|
||||
f:browse_commit(sha)
|
||||
end
|
||||
end)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'yank',
|
||||
fn = function(selected)
|
||||
end
|
||||
end
|
||||
if keys.yank then
|
||||
fzf_actions[to_fzf_key(keys.yank)] = function(selected)
|
||||
with_sha(selected, function(sha)
|
||||
vim.fn.setreg('+', sha)
|
||||
vim.notify('[forge]: copied ' .. sha)
|
||||
end)
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
local commit_actions = build_actions('commits', defs)
|
||||
end
|
||||
end
|
||||
|
||||
require('fzf-lua').fzf_exec(log_cmd, {
|
||||
fzf_args = fzf_args,
|
||||
|
|
@ -612,7 +526,7 @@ function M.commits(f)
|
|||
['--no-multi'] = '',
|
||||
['--preview'] = 'git show --color {1}',
|
||||
},
|
||||
actions = commit_actions,
|
||||
actions = fzf_actions,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -621,10 +535,26 @@ function M.branches(f)
|
|||
local forge_mod = require('forge')
|
||||
local review = require('forge.review')
|
||||
|
||||
local defs = {
|
||||
{
|
||||
name = 'diff',
|
||||
fn = function(selected)
|
||||
if picker.backend() ~= 'fzf-lua' then
|
||||
vim.notify('[forge]: branches picker requires fzf-lua', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local function to_fzf_key(key)
|
||||
if key == '<cr>' then
|
||||
return 'default'
|
||||
end
|
||||
return key:gsub('<c%-(%a)>', function(ch)
|
||||
return 'ctrl-' .. ch:lower()
|
||||
end)
|
||||
end
|
||||
|
||||
local cfg = require('forge').config()
|
||||
local keys = type(cfg.keys) == 'table' and cfg.keys.branches or {}
|
||||
local fzf_actions = {}
|
||||
|
||||
if keys.diff then
|
||||
fzf_actions[to_fzf_key(keys.diff)] = function(selected)
|
||||
if not selected[1] then
|
||||
return
|
||||
end
|
||||
|
|
@ -638,11 +568,10 @@ function M.branches(f)
|
|||
commands.greview(br)
|
||||
end
|
||||
forge_mod.log_now('reviewing ' .. br)
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
end
|
||||
end
|
||||
if keys.browse then
|
||||
fzf_actions[to_fzf_key(keys.browse)] = function(selected)
|
||||
if not selected[1] then
|
||||
return
|
||||
end
|
||||
|
|
@ -650,12 +579,10 @@ function M.branches(f)
|
|||
if br and f then
|
||||
f:browse_branch(br)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local branch_actions = build_actions('branches', defs)
|
||||
require('fzf-lua').git_branches({ actions = branch_actions })
|
||||
require('fzf-lua').git_branches({ actions = fzf_actions })
|
||||
end
|
||||
|
||||
---@param state 'all'|'open'|'closed'
|
||||
|
|
@ -669,64 +596,66 @@ function M.pr(state, f)
|
|||
local show_state = state ~= 'open'
|
||||
|
||||
local function open_pr_list(prs)
|
||||
local lines = {}
|
||||
local entries = {}
|
||||
for _, pr in ipairs(prs) do
|
||||
table.insert(lines, forge_mod.format_pr(pr, pr_fields, show_state))
|
||||
end
|
||||
local function with_pr_num(selected, fn)
|
||||
local num = selected[1] and selected[1]:match('[#!](%d+)')
|
||||
if num then
|
||||
fn(num)
|
||||
end
|
||||
local num = tostring(pr[pr_fields.number] or '')
|
||||
table.insert(entries, {
|
||||
display = forge_mod.format_pr(pr, pr_fields, show_state),
|
||||
value = num,
|
||||
ordinal = (pr[pr_fields.title] or '') .. ' #' .. num,
|
||||
})
|
||||
end
|
||||
|
||||
local list_actions = build_actions('pr', {
|
||||
picker.pick({
|
||||
prompt = ('%s (%s)> '):format(f.labels.pr, state),
|
||||
entries = entries,
|
||||
actions = {
|
||||
{
|
||||
name = 'checkout',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
pr_actions(f, num)._by_name['checkout']()
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
pr_action_fns(f, entry.value).checkout()
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'diff',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
pr_actions(f, num)._by_name['diff']()
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
pr_action_fns(f, entry.value).diff()
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'worktree',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
pr_actions(f, num)._by_name['worktree']()
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
pr_action_fns(f, entry.value).worktree()
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'ci',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
pr_actions(f, num)._by_name['ci']()
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
pr_action_fns(f, entry.value).ci()
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
f:view_web(cli_kind, num)
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
f:view_web(cli_kind, entry.value)
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'manage',
|
||||
fn = function(selected)
|
||||
with_pr_num(selected, function(num)
|
||||
pr_actions(f, num)._by_name['manage']()
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
pr_action_fns(f, entry.value).manage()
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
|
|
@ -748,16 +677,8 @@ function M.pr(state, f)
|
|||
M.pr(state, f)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
require('fzf-lua').fzf_exec(lines, {
|
||||
fzf_args = fzf_args,
|
||||
prompt = ('%s (%s)> '):format(f.labels.pr, state),
|
||||
fzf_opts = {
|
||||
['--ansi'] = '',
|
||||
['--no-multi'] = '',
|
||||
},
|
||||
actions = list_actions,
|
||||
picker_name = 'pr',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -795,35 +716,36 @@ function M.issue(state, f)
|
|||
end)
|
||||
local state_field = issue_fields.state
|
||||
local state_map = {}
|
||||
local lines = {}
|
||||
local entries = {}
|
||||
for _, issue in ipairs(issues) do
|
||||
local n = tostring(issue[num_field] or '')
|
||||
local s = (issue[state_field] or ''):lower()
|
||||
state_map[n] = s == 'open' or s == 'opened'
|
||||
table.insert(lines, forge_mod.format_issue(issue, issue_fields, issue_show_state))
|
||||
end
|
||||
local function with_issue_num(selected, fn)
|
||||
local num = selected[1] and selected[1]:match('[#!](%d+)')
|
||||
if num then
|
||||
fn(num)
|
||||
end
|
||||
table.insert(entries, {
|
||||
display = forge_mod.format_issue(issue, issue_fields, issue_show_state),
|
||||
value = n,
|
||||
ordinal = (issue[issue_fields.title] or '') .. ' #' .. n,
|
||||
})
|
||||
end
|
||||
|
||||
local issue_actions = build_actions('issue', {
|
||||
picker.pick({
|
||||
prompt = ('%s (%s)> '):format(f.labels.issue, state),
|
||||
entries = entries,
|
||||
actions = {
|
||||
{
|
||||
name = 'browse',
|
||||
fn = function(selected)
|
||||
with_issue_num(selected, function(num)
|
||||
f:view_web(cli_kind, num)
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
f:view_web(cli_kind, entry.value)
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
name = 'close',
|
||||
fn = function(selected)
|
||||
with_issue_num(selected, function(num)
|
||||
issue_toggle_state(f, num, state_map[num] ~= false)
|
||||
end)
|
||||
fn = function(entry)
|
||||
if entry then
|
||||
issue_toggle_state(f, entry.value, state_map[entry.value] ~= false)
|
||||
end
|
||||
end,
|
||||
},
|
||||
{
|
||||
|
|
@ -839,16 +761,8 @@ function M.issue(state, f)
|
|||
M.issue(state, f)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
require('fzf-lua').fzf_exec(lines, {
|
||||
fzf_args = fzf_args,
|
||||
prompt = ('%s (%s)> '):format(f.labels.issue, state),
|
||||
fzf_opts = {
|
||||
['--ansi'] = '',
|
||||
['--no-multi'] = '',
|
||||
},
|
||||
actions = issue_actions,
|
||||
picker_name = 'issue',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -891,7 +805,7 @@ end
|
|||
---@param num string
|
||||
---@return table<string, function>
|
||||
function M.pr_actions(f, num)
|
||||
return pr_actions(f, num)
|
||||
return pr_action_fns(f, num)
|
||||
end
|
||||
|
||||
function M.git()
|
||||
|
|
@ -913,11 +827,14 @@ function M.git()
|
|||
local branch = vim.trim(vim.fn.system('git branch --show-current'))
|
||||
|
||||
local items = {}
|
||||
local actions = {}
|
||||
local action_map = {}
|
||||
|
||||
local function add(label, action)
|
||||
table.insert(items, label)
|
||||
actions[label] = action
|
||||
table.insert(items, {
|
||||
display = { { label } },
|
||||
value = label,
|
||||
})
|
||||
action_map[label] = action
|
||||
end
|
||||
|
||||
if f then
|
||||
|
|
@ -968,21 +885,29 @@ function M.git()
|
|||
end)
|
||||
|
||||
add('Worktrees', function()
|
||||
if picker.backend() == 'fzf-lua' then
|
||||
require('fzf-lua').git_worktrees()
|
||||
else
|
||||
vim.notify('[forge]: worktrees picker requires fzf-lua', vim.log.levels.WARN)
|
||||
end
|
||||
end)
|
||||
|
||||
local prompt = f and (f.name:sub(1, 1):upper() .. f.name:sub(2)) .. '> ' or 'Git> '
|
||||
|
||||
require('fzf-lua').fzf_exec(items, {
|
||||
fzf_args = fzf_args,
|
||||
picker.pick({
|
||||
prompt = prompt,
|
||||
entries = items,
|
||||
actions = {
|
||||
['default'] = function(selected)
|
||||
if selected[1] and actions[selected[1]] then
|
||||
actions[selected[1]]()
|
||||
{
|
||||
name = 'default',
|
||||
fn = function(entry)
|
||||
if entry and action_map[entry.value] then
|
||||
action_map[entry.value]()
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
picker_name = '_menu',
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue