diff --git a/doc/pending.txt b/doc/pending.txt index 994afc6..2883664 100644 --- a/doc/pending.txt +++ b/doc/pending.txt @@ -38,7 +38,7 @@ Features: ~ - Multi-level undo (up to 20 `:w` saves, persisted across sessions) - Quick-add from the command line with `:Pending add` - Quickfix list of overdue/due-today tasks via `:Pending due` -- Foldable category sections (`zc`/`zo`) in category view +- Configurable category folds (`zc`/`zo`) with custom foldtext - Omnifunc completion for `cat:`, `due:`, and `rec:` tokens (``) - Google Calendar one-way push via OAuth PKCE - Google Tasks bidirectional sync via OAuth PKCE @@ -274,8 +274,8 @@ Default buffer-local keys: ~ `U` Undo the last `:w` save (`undo`) `o` Insert a new task line below (`open_line`) `O` Insert a new task line above (`open_line_above`) - `zc` Fold the current category section (category view only) - `zo` Unfold the current category section (category view only) + `zc` Fold the current category section (requires `folding`) + `zo` Unfold the current category section (requires `folding`) Text objects (operator-pending and visual): ~ @@ -595,6 +595,7 @@ loads: >lua date_syntax = 'due', recur_syntax = 'rec', someday_date = '9999-12-30', + folding = true, category_order = {}, keymaps = { close = 'q', @@ -684,6 +685,35 @@ Fields: ~ given order. Categories not in the list are appended after the ordered ones in their natural order. + {folding} (boolean|table, default: true) *pending.FoldingConfig* + Controls category-level folds in category view. When + `true`, folds are enabled with the default foldtext + `'%c (%n tasks)'`. When `false`, folds are disabled + entirely. When a table, folds are enabled and the + table may contain: + + {foldtext} (string|false, default: '%c (%n tasks)') + Custom foldtext format string. Set to + `false` to use Vim's built-in + foldtext. Two specifiers are + available: + `%c` category name + `%n` number of tasks in the fold + The category icon is prepended + automatically. When `false`, the + default Vim foldtext is used. + + Folds only apply to category view; priority view + is always fold-free regardless of this setting. + + Examples: >lua + vim.g.pending = { folding = true } + vim.g.pending = { folding = false } + vim.g.pending = { + folding = { foldtext = '%c (%n tasks)' }, + } +< + {keymaps} (table, default: see below) *pending.Keymaps* Buffer-local key bindings. Each field maps an action name to a key string. Set a field to `false` to diff --git a/lua/pending/buffer.lua b/lua/pending/buffer.lua index adcf2dc..a54388b 100644 --- a/lua/pending/buffer.lua +++ b/lua/pending/buffer.lua @@ -236,8 +236,30 @@ local function setup_highlights() vim.api.nvim_set_hl(0, 'PendingFilter', { link = 'DiagnosticWarn', default = true }) end +---@return string +function M.get_foldtext() + local folding = config.resolve_folding() + if not folding.foldtext then + return vim.fn.foldtext() + end + local line = vim.fn.getline(vim.v.foldstart) + local cat = line:match('^#%s+(.+)$') or line + local task_count = vim.v.foldend - vim.v.foldstart + local icons = config.get().icons + local result = folding.foldtext + :gsub('%%c', cat) + :gsub('%%n', tostring(task_count)) + :gsub('(%d+) (%w+)s%)', function(n, word) + if n == '1' then + return n .. ' ' .. word .. ')' + end + return n .. ' ' .. word .. 's)' + end) + return icons.category .. ' ' .. result +end + local function snapshot_folds(bufnr) - if current_view ~= 'category' then + if current_view ~= 'category' or not config.resolve_folding().enabled then return end for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do @@ -256,7 +278,7 @@ local function snapshot_folds(bufnr) end local function restore_folds(bufnr) - if current_view ~= 'category' then + if current_view ~= 'category' or not config.resolve_folding().enabled then return end for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do @@ -328,12 +350,18 @@ function M.render(bufnr) setup_syntax(bufnr) apply_extmarks(bufnr, line_meta) + local folding = config.resolve_folding() for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do - if current_view == 'category' then + if current_view == 'category' and folding.enabled then vim.wo[winid].foldmethod = 'expr' vim.wo[winid].foldexpr = 'v:lua.require("pending.buffer").get_fold()' vim.wo[winid].foldlevel = 99 vim.wo[winid].foldenable = true + if folding.foldtext then + vim.wo[winid].foldtext = 'v:lua.require("pending.buffer").get_foldtext()' + else + vim.wo[winid].foldtext = 'foldtext()' + end else vim.wo[winid].foldmethod = 'manual' vim.wo[winid].foldenable = false diff --git a/lua/pending/config.lua b/lua/pending/config.lua index 9f1c760..d926037 100644 --- a/lua/pending/config.lua +++ b/lua/pending/config.lua @@ -1,3 +1,10 @@ +---@class pending.FoldingConfig +---@field foldtext? string|false + +---@class pending.ResolvedFolding +---@field enabled boolean +---@field foldtext string|false + ---@class pending.Icons ---@field pending string ---@field done string @@ -55,6 +62,7 @@ ---@field drawer_height? integer ---@field debug? boolean ---@field keymaps pending.Keymaps +---@field folding? boolean|pending.FoldingConfig ---@field sync? pending.SyncConfig ---@field icons pending.Icons @@ -70,6 +78,7 @@ local defaults = { date_syntax = 'due', recur_syntax = 'rec', someday_date = '9999-12-30', + folding = true, category_order = {}, keymaps = { close = 'q', @@ -119,4 +128,15 @@ function M.reset() _resolved = nil end +---@return pending.ResolvedFolding +function M.resolve_folding() + local raw = M.get().folding + if raw == false then + return { enabled = false, foldtext = false } + elseif raw == true or raw == nil then + return { enabled = true, foldtext = '%c (%n tasks)' } + end + return { enabled = true, foldtext = raw.foldtext or false } +end + return M