feat(priority): add g<C-a> and g<C-x> visual batch priority mappings (#151)

Problem: Incrementing or decrementing priority required operating on one
task at a time with `<C-a>`/`<C-x>`, which is tedious when adjusting
multiple tasks.

Solution: Add `adjust_priority_visual(delta)` that iterates the visual
selection range, updates every task line's priority in one pass, then
re-renders once. Exposed as `increment_priority_visual()` /
`decrement_priority_visual()` with `g<C-a>` / `g<C-x>` defaults, new
`<Plug>` mappings, and config keys `priority_up_visual` /
`priority_down_visual`.
This commit is contained in:
Barrett Ruth 2026-03-12 20:19:08 -04:00 committed by Barrett Ruth
parent 077e4121b4
commit ae44beb32d
4 changed files with 83 additions and 0 deletions

View file

@ -354,6 +354,8 @@ Default buffer-local keys: ~
`O` Insert a new task line above (`open_line_above`)
`<C-a>` Increment priority (clamped at `max_priority`) (`priority_up`)
`<C-x>` Decrement priority (clamped at 0) (`priority_down`)
`g<C-a>` Increment priority for visual selection (`priority_up_visual`)
`g<C-x>` Decrement priority for visual selection (`priority_down_visual`)
`J` Move task down within its category (`move_down`)
`K` Move task up within its category (`move_up`)
`zc` Fold the current category section (requires `folding`)

View file

@ -78,6 +78,8 @@
---@field move_up? string|false
---@field wip? string|false
---@field blocked? string|false
---@field priority_up_visual? string|false
---@field priority_down_visual? string|false
---@class pending.CategoryViewConfig
---@field order? string[]
@ -157,6 +159,8 @@ local defaults = {
blocked = 'gb',
priority_up = '<C-a>',
priority_down = '<C-x>',
priority_up_visual = 'g<C-a>',
priority_down_visual = 'g<C-x>',
},
sync = {},
forge = {

View file

@ -402,6 +402,26 @@ function M._setup_buf_mappings(bufnr)
end
end
---@type table<string, fun()>
local visual_actions = {
priority_up_visual = function()
M.increment_priority_visual()
end,
priority_down_visual = function()
M.decrement_priority_visual()
end,
}
for name, fn in pairs(visual_actions) do
local key = km[name]
if key and key ~= false then
vim.keymap.set('x', key --[[@as string]], function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'nx', false)
fn()
end, opts)
end
end
local textobj = require('pending.textobj')
---@type table<string, { modes: string[], fn: fun(count: integer), visual_fn?: fun(count: integer) }>
@ -711,6 +731,53 @@ function M.decrement_priority()
adjust_priority(-1)
end
---@param delta integer
---@return nil
local function adjust_priority_visual(delta)
local bufnr = buffer.bufnr()
if not bufnr then
return
end
if not require_saved() then
return
end
local start_row = vim.fn.line("'<")
local end_row = vim.fn.line("'>")
local cursor = vim.api.nvim_win_get_cursor(0)
local meta = buffer.meta()
local s = get_store()
local max = require('pending.config').get().max_priority or 3
local changed = false
for row = start_row, end_row do
if meta[row] and meta[row].type == 'task' and meta[row].id then
local task = s:get(meta[row].id)
if task then
local new_priority = math.max(0, math.min(max, task.priority + delta))
if new_priority ~= task.priority then
s:update(meta[row].id, { priority = new_priority })
changed = true
end
end
end
end
if not changed then
return
end
_save_and_notify()
buffer.render(bufnr)
pcall(vim.api.nvim_win_set_cursor, 0, cursor)
end
---@return nil
function M.increment_priority_visual()
adjust_priority_visual(1)
end
---@return nil
function M.decrement_priority_visual()
adjust_priority_visual(-1)
end
---@return nil
function M.prompt_date()
local bufnr = buffer.bufnr()

View file

@ -402,6 +402,16 @@ vim.keymap.set('n', '<Plug>(pending-priority-down)', function()
require('pending').decrement_priority()
end)
vim.keymap.set('x', '<Plug>(pending-priority-up-visual)', function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'nx', false)
require('pending').increment_priority_visual()
end)
vim.keymap.set('x', '<Plug>(pending-priority-down-visual)', function()
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'nx', false)
require('pending').decrement_priority_visual()
end)
vim.keymap.set('n', '<Plug>(pending-filter)', function()
vim.ui.input({ prompt = 'Filter: ' }, function(input)
if input then