feat: expand cli

This commit is contained in:
Barrett Ruth 2026-03-28 12:12:04 -04:00
parent b4db29e77a
commit c677feee2f
No known key found for this signature in database
GPG key ID: A6C96C9349D2FC81
5 changed files with 142 additions and 387 deletions

View file

@ -14,7 +14,7 @@ Requirements: ~
- `gh` for GitHub - `gh` for GitHub
- `glab` for GitLab - `glab` for GitLab
- `tea` for Codeberg/Gitea/Forgejo - `tea` for Codeberg/Gitea/Forgejo
- vim-fugitive (optional, for fugitive keymaps and split review) - vim-fugitive (optional, for split review)
- diffs.nvim (optional, for review mode) - diffs.nvim (optional, for review mode)
Run |:checkhealth| forge to verify CLIs and dependencies. Run |:checkhealth| forge to verify CLIs and dependencies.
@ -51,87 +51,47 @@ Top-level keys: ~
`keys` *forge-config-keys* `keys` *forge-config-keys*
`table|false` (default shown below) `table|false` (default shown below)
Global keymaps. Set to `false` to disable all global keymaps. Setting Per-picker action bindings. Set to `false` to disable all picker-level
an individual key to `nil` or `false` disables that single keymap. actions. Use `"<cr>"` to bind to enter. Values use vim keymap notation
(e.g. `"<c-d>"`), which is translated to fzf-lua format internally.
Defaults: >lua Defaults: >lua
keys = { keys = {
picker = '<c-g>',
next_qf = ']q',
prev_qf = '[q',
next_loc = ']l',
prev_loc = '[l',
review_toggle = 's',
terminal_open = 'gx',
fugitive = {
create = 'cpr',
create_draft = 'cpd',
create_fill = 'cpf',
create_web = 'cpw',
},
}
<
`keys.picker` Open the main forge picker (|forge-picker|).
`keys.next_qf` Navigate to next quickfix entry (wraps).
`keys.prev_qf` Navigate to previous quickfix entry (wraps).
`keys.next_loc` Navigate to next loclist entry (wraps).
`keys.prev_loc` Navigate to previous loclist entry (wraps).
`keys.review_toggle` Toggle unified/split review (|forge-review|).
`keys.terminal_open` Open URL in browser from log terminal buffers.
`keys.fugitive.create` Create PR via compose buffer.
`keys.fugitive.create_draft` Create draft PR via compose buffer.
`keys.fugitive.create_fill` Create PR instantly (skip compose buffer).
`keys.fugitive.create_web` Push and open create-PR page in browser.
Set `keys.fugitive` to `false` to disable all fugitive-buffer keymaps.
`picker_keys` *forge-config-picker-keys*
`table|false` (default shown below)
Per-picker action bindings. Set to `false` to disable all picker-level
actions. Use `"default"` to bind to `<enter>`. Other values use fzf-lua
binding syntax (e.g. `"ctrl-d"`).
Defaults: >lua
picker_keys = {
pr = { pr = {
checkout = 'default', checkout = '<cr>',
diff = 'ctrl-d', diff = '<c-d>',
worktree = 'ctrl-w', worktree = '<c-w>',
checks = 'ctrl-t', ci = '<c-t>',
browse = 'ctrl-x', browse = '<c-x>',
manage = 'ctrl-e', manage = '<c-e>',
create = 'ctrl-a', create = '<c-a>',
toggle = 'ctrl-o', filter = '<c-o>',
refresh = 'ctrl-r', refresh = '<c-r>',
}, },
issue = { issue = {
browse = 'default', browse = '<cr>',
close_reopen = 'ctrl-s', close = '<c-s>',
toggle = 'ctrl-o', filter = '<c-o>',
refresh = 'ctrl-r', refresh = '<c-r>',
},
checks = {
log = 'default',
browse = 'ctrl-x',
failed = 'ctrl-f',
passed = 'ctrl-p',
running = 'ctrl-n',
all = 'ctrl-a',
}, },
ci = { ci = {
log = 'default', log = '<cr>',
browse = 'ctrl-x', browse = '<c-x>',
refresh = 'ctrl-r', failed = '<c-f>',
passed = '<c-p>',
running = '<c-n>',
all = '<c-a>',
refresh = '<c-r>',
}, },
commits = { commits = {
checkout = 'default', checkout = '<cr>',
diff = 'ctrl-d', diff = '<c-d>',
browse = 'ctrl-x', browse = '<c-x>',
yank = 'ctrl-y', yank = '<c-y>',
}, },
branches = { branches = {
diff = 'ctrl-d', diff = '<c-d>',
browse = 'ctrl-x', browse = '<c-x>',
}, },
} }
< <
@ -174,8 +134,7 @@ Top-level keys: ~
COMMANDS *:Forge* COMMANDS *:Forge*
`:Forge` *:Forge-no-args* `:Forge` *:Forge-no-args*
Open the main forge picker (|forge-picker|). Same as pressing Open the main forge picker (|forge-picker|).
`keys.picker`.
`:Forge pr` [{flags}] *:Forge-pr* `:Forge pr` [{flags}] *:Forge-pr*
Open the PR list picker. Defaults to open PRs. Open the PR list picker. Defaults to open PRs.
@ -200,8 +159,8 @@ COMMANDS *:Forge*
`:Forge pr worktree` {num} *:Forge-pr-worktree* `:Forge pr worktree` {num} *:Forge-pr-worktree*
Fetch PR `{num}` and create a git worktree. Fetch PR `{num}` and create a git worktree.
`:Forge pr checks` {num} *:Forge-pr-checks* `:Forge pr ci` {num} *:Forge-pr-ci*
Open the checks picker for PR `{num}`. Open the CI checks picker for PR `{num}`.
`:Forge pr browse` {num} *:Forge-pr-browse* `:Forge pr browse` {num} *:Forge-pr-browse*
Open PR `{num}` in the browser. Open PR `{num}` in the browser.
@ -272,50 +231,11 @@ COMMANDS *:Forge*
`:Forge review toggle` *:Forge-review-toggle* `:Forge review toggle` *:Forge-review-toggle*
Toggle between unified and split view (|forge-review|). Toggle between unified and split view (|forge-review|).
`:Forge cache clear` *:Forge-cache-clear* `:Forge clear` *:Forge-clear*
Clear all internal caches (forge detection, repo info, list data). Clear all internal caches (forge detection, repo info, list data).
All subcommands support tab completion. All subcommands support tab completion.
==============================================================================
KEYMAPS *forge-keymaps*
GLOBAL KEYMAPS ~
All global keymaps are configured via `keys` (|forge-config-keys|).
Default Mode Description ~
`<c-g>` n, v Open the main forge picker
`]q` n Next quickfix entry (wraps to first)
`[q` n Previous quickfix entry (wraps to last)
`]l` n Next loclist entry (wraps to first)
`[l` n Previous loclist entry (wraps to last)
FUGITIVE KEYMAPS ~
Active in `fugitive` filetype buffers when a forge is detected. Configured
via `keys.fugitive`. Set `keys.fugitive = false` to disable.
Default Description ~
`cpr` Create PR via compose buffer
`cpd` Create draft PR via compose buffer
`cpf` Create PR instantly (fill from commits)
`cpw` Push and open create-PR page in browser
REVIEW KEYMAPS ~
Active during a review session (|forge-review|).
Default Description ~
`s` Toggle unified/split view
TERMINAL KEYMAPS ~
Active in log terminal buffers opened by the checks or CI pickers.
Default Description ~
`gx` Open the associated check/run URL in the browser
============================================================================== ==============================================================================
PICKERS *forge-pickers* PICKERS *forge-pickers*
@ -332,65 +252,58 @@ PR PICKER ~
Lists PRs/MRs with number, title, author, and relative time. Lists PRs/MRs with number, title, author, and relative time.
Action Default Key Description ~ Action Default Key Description ~
`checkout` `<enter>` Check out the PR branch `checkout` `<cr>` Check out the PR branch
`diff` `ctrl-d` Start review (|forge-review|) `diff` `<c-d>` Start review (|forge-review|)
`worktree` `ctrl-w` Create worktree from PR `worktree` `<c-w>` Create worktree from PR
`checks` `ctrl-t` Open checks picker for this PR `ci` `<c-t>` Open CI checks picker for this PR
`browse` `ctrl-x` Open PR in browser `browse` `<c-x>` Open PR in browser
`manage` `ctrl-e` Open management picker `manage` `<c-e>` Open management picker
`create` `ctrl-a` Create new PR (|forge-compose|) `create` `<c-a>` Create new PR (|forge-compose|)
`toggle` `ctrl-o` Cycle state: open -> closed -> all `filter` `<c-o>` Cycle state: open -> closed -> all
`refresh` `ctrl-r` Clear cache and re-fetch `refresh` `<c-r>` Clear cache and re-fetch
*forge-picker-issue* *forge-picker-issue*
ISSUE PICKER ~ ISSUE PICKER ~
Lists issues with number, title, author, and relative time. Lists issues with number, title, author, and relative time.
Action Default Key Description ~
`browse` `<enter>` Open issue in browser
`close_reopen` `ctrl-s` Close or reopen the issue
`toggle` `ctrl-o` Cycle state: all -> open -> closed
`refresh` `ctrl-r` Clear cache and re-fetch
*forge-picker-checks*
CHECKS PICKER ~
Lists PR check runs with status icon, name, and elapsed time.
Action Default Key Description ~ Action Default Key Description ~
`log` `<enter>` View log (tail for running, full otherwise) `browse` `<cr>` Open issue in browser
`browse` `ctrl-x` Open check URL in browser `close` `<c-s>` Close or reopen the issue
`failed` `ctrl-f` Filter to failed checks `filter` `<c-o>` Cycle state: all -> open -> closed
`passed` `ctrl-p` Filter to passed checks `refresh` `<c-r>` Clear cache and re-fetch
`running` `ctrl-n` Filter to running checks
`all` `ctrl-a` Show all checks
*forge-picker-ci* *forge-picker-ci*
CI PICKER ~ CI PICKER ~
Lists CI/CD runs for the current branch (or all branches with `--all`). Used for both per-PR checks (`:Forge pr ci {num}`) and repo-wide CI runs
(`:Forge ci`). Both share the `keys.ci` bindings.
Action Default Key Description ~ Action Default Key Description ~
`log` `<enter>` View log (tail for running, failed-only for `log` `<cr>` View log (tail for running, failed-only for
failures, full otherwise) failures, full otherwise)
`browse` `ctrl-x` Open run URL in browser `browse` `<c-x>` Open run/check URL in browser
`refresh` `ctrl-r` Re-fetch runs `failed` `<c-f>` Filter to failed
`passed` `<c-p>` Filter to passed
`running` `<c-n>` Filter to running
`all` `<c-a>` Show all
`refresh` `<c-r>` Re-fetch runs
*forge-picker-commits* *forge-picker-commits*
COMMITS PICKER ~ COMMITS PICKER ~
Git log with colored output and commit preview. Git log with colored output and commit preview.
Action Default Key Description ~ Action Default Key Description ~
`checkout` `<enter>` Checkout commit (detached HEAD) `checkout` `<cr>` Checkout commit (detached HEAD)
`diff` `ctrl-d` Review the commit diff `diff` `<c-d>` Review the commit diff
`browse` `ctrl-x` Open commit on forge `browse` `<c-x>` Open commit on forge
`yank` `ctrl-y` Yank commit hash to `+` register `yank` `<c-y>` Yank commit hash to `+` register
*forge-picker-branches* *forge-picker-branches*
BRANCHES PICKER ~ BRANCHES PICKER ~
Uses fzf-lua's `git_branches` with additional actions. Uses fzf-lua's `git_branches` with additional actions.
Action Default Key Description ~ Action Default Key Description ~
`diff` `ctrl-d` Review diff against branch `diff` `<c-d>` Review diff against branch
`browse` `ctrl-x` Open branch on forge `browse` `<c-x>` Open branch on forge
*forge-picker-manage* *forge-picker-manage*
MANAGE PICKER ~ MANAGE PICKER ~
@ -405,8 +318,8 @@ Review mode provides unified and split diff viewing for PRs and commits.
Requires diffs.nvim for unified view and vim-fugitive for split view. Requires diffs.nvim for unified view and vim-fugitive for split view.
Starting a review: ~ Starting a review: ~
- `ctrl-d` on a PR in the PR picker - `<c-d>` on a PR in the PR picker
- `ctrl-d` on a commit in the commit picker - `<c-d>` on a commit in the commit picker
- `:Forge pr diff {num}` - `:Forge pr diff {num}`
- `:Forge commit diff {sha}` - `:Forge commit diff {sha}`
- `:Forge branch diff {name}` - `:Forge branch diff {name}`
@ -416,13 +329,8 @@ Unified view (default): ~
a quickfix list of changed files. a quickfix list of changed files.
Split view: ~ Split view: ~
Press the `review_toggle` key (default `s`) to switch to a side-by-side `:Forge review toggle` switches to a side-by-side fugitive split
fugitive split (`:Gvdiffsplit`). Press again to return to unified. (`:Gvdiffsplit`). Run again to return to unified.
Navigation: ~
`]q` / `[q` navigate quickfix entries. In split mode, the split is
automatically closed and reopened around the new file. Navigation wraps
at list boundaries.
Ending a review: ~ Ending a review: ~
`:Forge review end` or wipe the `diffs://review:*` buffer. `:Forge review end` or wipe the `diffs://review:*` buffer.
@ -622,7 +530,6 @@ Reports on: ~
- Forge CLI availability (`gh`, `glab`, `tea`) - Forge CLI availability (`gh`, `glab`, `tea`)
- `fzf-lua` installation (required) - `fzf-lua` installation (required)
- `diffs.nvim` installation (review mode) - `diffs.nvim` installation (review mode)
- `vim-fugitive` availability (fugitive keymaps, split review)
- Custom registered sources and their CLI availability - Custom registered sources and their CLI availability
============================================================================== ==============================================================================
@ -640,7 +547,7 @@ PUBLIC API: `require('forge')` ~
`config()` `config()`
Returns `table`. The resolved configuration, merging `vim.g.forge` Returns `table`. The resolved configuration, merging `vim.g.forge`
over defaults. If `vim.g.forge.keys` is `false`, `cfg.keys` is over defaults. If `vim.g.forge.keys` is `false`, `cfg.keys` is
`false`. Same for `picker_keys`. `false`.
*forge.register()* *forge.register()*
`register(name, source)` `register(name, source)`
@ -749,7 +656,7 @@ PUBLIC API: `require('forge.pickers')` ~
`pr_actions(f, num)` `pr_actions(f, num)`
Returns `table<string, function>`. Action functions for PR `num`, Returns `table<string, function>`. Action functions for PR `num`,
keyed by fzf binding. Also has `_by_name` table keyed by action keyed by fzf binding. Also has `_by_name` table keyed by action
name (`"checkout"`, `"diff"`, `"worktree"`, `"browse"`, `"checks"`, name (`"checkout"`, `"diff"`, `"worktree"`, `"browse"`, `"ci"`,
`"manage"`). `"manage"`).
*forge.pickers.issue_close()* *forge.pickers.issue_close()*
@ -769,8 +676,7 @@ PUBLIC API: `require('forge.review')` ~
*forge.review.stop()* *forge.review.stop()*
`stop()` `stop()`
Ends the current review session. Clears state and removes the Ends the current review session. Clears state.
`review_toggle` keymap.
*forge.review.toggle()* *forge.review.toggle()*
`toggle()` `toggle()`

View file

@ -36,13 +36,6 @@ function M.check()
vim.health.info('diffs.nvim not found (review mode disabled)') vim.health.info('diffs.nvim not found (review mode disabled)')
end end
local has_fugitive = vim.fn.exists(':Git') == 2
if has_fugitive then
vim.health.ok('vim-fugitive found (fugitive keymaps available)')
else
vim.health.info('vim-fugitive not found (fugitive keymaps disabled)')
end
local forge_mod = require('forge') local forge_mod = require('forge')
for name, source in pairs(forge_mod.registered_sources()) do for name, source in pairs(forge_mod.registered_sources()) do
if name ~= 'github' and name ~= 'gitlab' and name ~= 'codeberg' then if name ~= 'github' and name ~= 'gitlab' and name ~= 'codeberg' then

View file

@ -4,7 +4,6 @@ local M = {}
---@field ci forge.CIConfig ---@field ci forge.CIConfig
---@field sources table<string, forge.SourceConfig> ---@field sources table<string, forge.SourceConfig>
---@field keys forge.KeysConfig|false ---@field keys forge.KeysConfig|false
---@field picker_keys forge.PickerKeysConfig|false
---@field display forge.DisplayConfig ---@field display forge.DisplayConfig
---@class forge.CIConfig ---@class forge.CIConfig
@ -14,25 +13,8 @@ local M = {}
---@field hosts string[] ---@field hosts string[]
---@class forge.KeysConfig ---@class forge.KeysConfig
---@field picker string|false
---@field next_qf string|false
---@field prev_qf string|false
---@field next_loc string|false
---@field prev_loc string|false
---@field review_toggle string|false
---@field terminal_open string|false
---@field fugitive forge.FugitiveKeysConfig|false
---@class forge.FugitiveKeysConfig
---@field create string|false
---@field create_draft string|false
---@field create_fill string|false
---@field create_web string|false
---@class forge.PickerKeysConfig
---@field pr forge.PRPickerKeys ---@field pr forge.PRPickerKeys
---@field issue forge.IssuePickerKeys ---@field issue forge.IssuePickerKeys
---@field checks forge.ChecksPickerKeys
---@field ci forge.CIPickerKeys ---@field ci forge.CIPickerKeys
---@field commits forge.CommitsPickerKeys ---@field commits forge.CommitsPickerKeys
---@field branches forge.BranchesPickerKeys ---@field branches forge.BranchesPickerKeys
@ -41,30 +23,26 @@ local M = {}
---@field checkout string|false ---@field checkout string|false
---@field diff string|false ---@field diff string|false
---@field worktree string|false ---@field worktree string|false
---@field checks string|false ---@field ci string|false
---@field browse string|false ---@field browse string|false
---@field manage string|false ---@field manage string|false
---@field create string|false ---@field create string|false
---@field toggle string|false ---@field filter string|false
---@field refresh string|false ---@field refresh string|false
---@class forge.IssuePickerKeys ---@class forge.IssuePickerKeys
---@field browse string|false ---@field browse string|false
---@field close_reopen string|false ---@field close string|false
---@field toggle string|false ---@field filter string|false
---@field refresh string|false ---@field refresh string|false
---@class forge.ChecksPickerKeys ---@class forge.CIPickerKeys
---@field log string|false ---@field log string|false
---@field browse string|false ---@field browse string|false
---@field failed string|false ---@field failed string|false
---@field passed string|false ---@field passed string|false
---@field running string|false ---@field running string|false
---@field all string|false ---@field all string|false
---@class forge.CIPickerKeys
---@field log string|false
---@field browse string|false
---@field refresh string|false ---@field refresh string|false
---@class forge.CommitsPickerKeys ---@class forge.CommitsPickerKeys
@ -108,44 +86,29 @@ local DEFAULTS = {
ci = { lines = 10000 }, ci = { lines = 10000 },
sources = {}, sources = {},
keys = { keys = {
picker = '<c-g>',
next_qf = ']q',
prev_qf = '[q',
next_loc = ']l',
prev_loc = '[l',
review_toggle = 's',
terminal_open = 'gx',
fugitive = {
create = 'cpr',
create_draft = 'cpd',
create_fill = 'cpf',
create_web = 'cpw',
},
},
picker_keys = {
pr = { pr = {
checkout = 'default', checkout = '<cr>',
diff = 'ctrl-d', diff = '<c-d>',
worktree = 'ctrl-w', worktree = '<c-w>',
checks = 'ctrl-t', ci = '<c-t>',
browse = 'ctrl-x', browse = '<c-x>',
manage = 'ctrl-e', manage = '<c-e>',
create = 'ctrl-a', create = '<c-a>',
toggle = 'ctrl-o', filter = '<c-o>',
refresh = 'ctrl-r', refresh = '<c-r>',
}, },
issue = { browse = 'default', close_reopen = 'ctrl-s', toggle = 'ctrl-o', refresh = 'ctrl-r' }, issue = { browse = '<cr>', close = '<c-s>', filter = '<c-o>', refresh = '<c-r>' },
checks = { ci = {
log = 'default', log = '<cr>',
browse = 'ctrl-x', browse = '<c-x>',
failed = 'ctrl-f', failed = '<c-f>',
passed = 'ctrl-p', passed = '<c-p>',
running = 'ctrl-n', running = '<c-n>',
all = 'ctrl-a', all = '<c-a>',
refresh = '<c-r>',
}, },
ci = { log = 'default', browse = 'ctrl-x', refresh = 'ctrl-r' }, commits = { checkout = '<cr>', diff = '<c-d>', browse = '<c-x>', yank = '<c-y>' },
commits = { checkout = 'default', diff = 'ctrl-d', browse = 'ctrl-x', yank = 'ctrl-y' }, branches = { diff = '<c-d>', browse = '<c-x>' },
branches = { diff = 'ctrl-d', browse = 'ctrl-x' },
}, },
display = { display = {
icons = { icons = {
@ -749,9 +712,6 @@ function M.config()
if user.keys == false then if user.keys == false then
cfg.keys = false cfg.keys = false
end end
if user.picker_keys == false then
cfg.picker_keys = false
end
return cfg return cfg
end end

View file

@ -19,11 +19,20 @@ local fzf_args = (vim.env.FZF_DEFAULT_OPTS or '')
:gsub('%-%-bind=[^%s]+', '') :gsub('%-%-bind=[^%s]+', '')
:gsub('%-%-color=[^%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 make_header(bindings) local function make_header(bindings)
local utils = require('fzf-lua.utils') local utils = require('fzf-lua.utils')
local parts = {} local parts = {}
for _, b in ipairs(bindings) do for _, b in ipairs(bindings) do
local key = utils.ansi_from_hl('FzfLuaHeaderBind', '<' .. b[1] .. '>') local key = utils.ansi_from_hl('FzfLuaHeaderBind', b[1])
local desc = utils.ansi_from_hl('FzfLuaHeaderText', b[2]) local desc = utils.ansi_from_hl('FzfLuaHeaderText', b[2])
table.insert(parts, key .. ' to ' .. desc) table.insert(parts, key .. ' to ' .. desc)
end end
@ -32,34 +41,26 @@ end
local function build_actions(picker_name, action_defs) local function build_actions(picker_name, action_defs)
local cfg = require('forge').config() local cfg = require('forge').config()
local pk = cfg.picker_keys local keys = cfg.keys
if pk == false then if keys == false then
pk = {} keys = {}
end end
local bindings = pk[picker_name] or {} local bindings = keys[picker_name] or {}
local actions = {} local actions = {}
local header_entries = {} local header_entries = {}
for _, def in ipairs(action_defs) do for _, def in ipairs(action_defs) do
local key = bindings[def.name] local key = bindings[def.name]
if key then if key then
actions[key] = def.fn local fzf_key = to_fzf_key(key)
actions[fzf_key] = def.fn
if def.label then if def.label then
local display_key = key == 'default' and 'enter' or key:gsub('ctrl%-', 'ctrl-') table.insert(header_entries, { key, def.label })
table.insert(header_entries, { display_key, def.label })
end end
end end
end end
return actions, make_header(header_entries) return actions, make_header(header_entries)
end end
local function terminal_open_key()
local cfg = require('forge').config()
if cfg.keys == false then
return nil
end
return cfg.keys.terminal_open
end
---@param kind string ---@param kind string
---@param num string ---@param num string
---@param label string ---@param label string
@ -188,7 +189,7 @@ local function pr_actions(f, num)
end, end,
}, },
{ {
name = 'checks', name = 'ci',
fn = function() fn = function()
M.checks(f, num) M.checks(f, num)
end, end,
@ -318,7 +319,7 @@ function M.checks(f, num, filter, cached_checks)
pending = 'running', pending = 'running',
} }
local check_actions, check_header = build_actions('checks', { local check_actions, check_header = build_actions('ci', {
{ {
name = 'log', name = 'log',
label = 'log', label = 'log',
@ -346,15 +347,8 @@ function M.checks(f, num, filter, cached_checks)
'n', 'n',
false false
) )
local to_key = terminal_open_key() if c.link then
if c.link and to_key then
vim.b.forge_check_url = c.link vim.b.forge_check_url = c.link
vim.keymap.set('n', to_key, function()
vim.ui.open(vim.b.forge_check_url)
end, {
buffer = true,
desc = 'open check in browser',
})
end end
end, end,
}, },
@ -495,15 +489,8 @@ function M.ci(f, branch)
'n', 'n',
false false
) )
local to_key = terminal_open_key() if run.url ~= '' then
if run.url ~= '' and to_key then
vim.b.forge_run_url = run.url vim.b.forge_run_url = run.url
vim.keymap.set('n', to_key, function()
vim.ui.open(vim.b.forge_run_url)
end, {
buffer = true,
desc = 'open run in browser',
})
end end
end, end,
}, },
@ -745,11 +732,11 @@ function M.pr(state, f)
end, end,
}, },
{ {
name = 'checks', name = 'ci',
label = 'checks', label = 'ci',
fn = function(selected) fn = function(selected)
with_pr_num(selected, function(num) with_pr_num(selected, function(num)
pr_actions(f, num)._by_name.checks() pr_actions(f, num)._by_name.ci()
end) end)
end, end,
}, },
@ -779,8 +766,8 @@ function M.pr(state, f)
end, end,
}, },
{ {
name = 'toggle', name = 'filter',
label = 'toggle', label = 'filter',
fn = function() fn = function()
M.pr(next_state, f) M.pr(next_state, f)
end, end,
@ -865,7 +852,7 @@ function M.issue(state, f)
end, end,
}, },
{ {
name = 'close_reopen', name = 'close',
label = 'close/reopen', label = 'close/reopen',
fn = function(selected) fn = function(selected)
with_issue_num(selected, function(num) with_issue_num(selected, function(num)
@ -874,8 +861,8 @@ function M.issue(state, f)
end, end,
}, },
{ {
name = 'toggle', name = 'filter',
label = 'toggle', label = 'filter',
fn = function() fn = function()
M.issue(next_state, f) M.issue(next_state, f)
end, end,

View file

@ -1,86 +1,3 @@
local cfg = require('forge').config()
if cfg.keys ~= false then
local k = cfg.keys
if k.picker then
vim.keymap.set({ 'n', 'v' }, k.picker, function()
require('forge.pickers').git()
end, { desc = 'forge git picker' })
end
if k.next_qf then
vim.keymap.set(
'n',
k.next_qf,
require('forge.review').nav('cnext'),
{ desc = 'next quickfix entry' }
)
end
if k.prev_qf then
vim.keymap.set(
'n',
k.prev_qf,
require('forge.review').nav('cprev'),
{ desc = 'prev quickfix entry' }
)
end
if k.next_loc then
vim.keymap.set(
'n',
k.next_loc,
require('forge.review').nav('lnext'),
{ desc = 'next loclist entry' }
)
end
if k.prev_loc then
vim.keymap.set(
'n',
k.prev_loc,
require('forge.review').nav('lprev'),
{ desc = 'prev loclist entry' }
)
end
if k.fugitive ~= false then
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fugitive',
callback = function(args)
local forge_mod = require('forge')
local f = forge_mod.detect()
if not f then
return
end
local fk = k.fugitive
local buf = args.buf
if fk.create then
vim.keymap.set('n', fk.create, function()
forge_mod.create_pr({ draft = false })
end, { buffer = buf, desc = 'create PR' })
end
if fk.create_draft then
vim.keymap.set('n', fk.create_draft, function()
forge_mod.create_pr({ draft = true })
end, { buffer = buf, desc = 'create draft PR' })
end
if fk.create_fill then
vim.keymap.set('n', fk.create_fill, function()
forge_mod.create_pr({ instant = true })
end, { buffer = buf, desc = 'create PR (fill)' })
end
if fk.create_web then
vim.keymap.set('n', fk.create_web, function()
forge_mod.create_pr({ web = true })
end, { buffer = buf, desc = 'create PR (web)' })
end
end,
})
end
end
vim.api.nvim_create_autocmd('FileType', { vim.api.nvim_create_autocmd('FileType', {
pattern = 'qf', pattern = 'qf',
callback = function() callback = function()
@ -188,7 +105,7 @@ local function dispatch(args)
pickers.pr_actions(f, num)._by_name.diff() pickers.pr_actions(f, num)._by_name.diff()
elseif action == 'worktree' then elseif action == 'worktree' then
pickers.pr_actions(f, num)._by_name.worktree() pickers.pr_actions(f, num)._by_name.worktree()
elseif action == 'checks' then elseif action == 'ci' then
pickers.checks(f, num) pickers.checks(f, num)
elseif action == 'browse' then elseif action == 'browse' then
f:view_web(f.kinds.pr, num) f:view_web(f.kinds.pr, num)
@ -424,17 +341,9 @@ local function dispatch(args)
return return
end end
if sub == 'cache' then if sub == 'clear' then
if #args < 2 then require('forge').clear_cache()
vim.notify('[forge]: missing cache action (clear)', vim.log.levels.WARN) vim.notify('[forge]: cache cleared')
return
end
if args[2] == 'clear' then
require('forge').clear_cache()
vim.notify('[forge]: cache cleared')
else
vim.notify('[forge]: unknown cache action: ' .. args[2], vim.log.levels.WARN)
end
return return
end end
@ -449,29 +358,20 @@ local function complete(arglead, cmdline, _)
local arg_idx = arglead == '' and #words or #words - 1 local arg_idx = arglead == '' and #words or #words - 1
local subcmds = local subcmds =
{ 'pr', 'issue', 'ci', 'commit', 'branch', 'worktree', 'browse', 'yank', 'review', 'cache' } { 'pr', 'issue', 'ci', 'commit', 'branch', 'worktree', 'browse', 'yank', 'review', 'clear' }
local sub_actions = { local sub_actions = {
pr = { pr = { 'checkout', 'diff', 'worktree', 'ci', 'browse', 'manage', 'create', '--state=' },
'checkout', issue = { 'browse', 'close', 'reopen', '--state=' },
'diff',
'worktree',
'checks',
'browse',
'manage',
'create',
'--state=open',
'--state=closed',
'--state=all',
},
issue = { 'browse', 'close', 'reopen', '--state=open', '--state=closed', '--state=all' },
ci = { '--all' }, ci = { '--all' },
commit = { 'checkout', 'diff', 'browse' }, commit = { 'checkout', 'diff', 'browse' },
branch = { 'diff', 'browse' }, branch = { 'diff', 'browse' },
review = { 'end', 'toggle' }, review = { 'end', 'toggle' },
cache = { 'clear' },
browse = { '--root', '--commit' }, browse = { '--root', '--commit' },
yank = { '--commit' }, yank = { '--commit' },
} }
local flag_values = {
['--state'] = { 'open', 'closed', 'all' },
}
local create_flags = { '--draft', '--fill', '--web' } local create_flags = { '--draft', '--fill', '--web' }
local function filter(candidates) local function filter(candidates)
@ -480,6 +380,15 @@ local function complete(arglead, cmdline, _)
end, candidates) end, candidates)
end end
local flag, value_prefix = arglead:match('^(%-%-[^=]+)=(.*)$')
if flag and flag_values[flag] then
return vim.tbl_map(function(v)
return flag .. '=' .. v
end, vim.tbl_filter(function(v)
return v:find(value_prefix, 1, true) == 1
end, flag_values[flag]))
end
if arg_idx == 1 then if arg_idx == 1 then
return filter(subcmds) return filter(subcmds)
end end