diff --git a/doc/snacks-bufdelete.txt b/doc/snacks-bufdelete.txt index 2f2b49c7..f9844b2e 100644 --- a/doc/snacks-bufdelete.txt +++ b/doc/snacks-bufdelete.txt @@ -3,7 +3,7 @@ ============================================================================== Table of Contents *snacks-bufdelete-table-of-contents* -1. Config |snacks-bufdelete-config| +1. Types |snacks-bufdelete-types| 2. Module |snacks-bufdelete-module| - Snacks.bufdelete() |snacks-bufdelete-module-snacks.bufdelete()| - Snacks.bufdelete.all() |snacks-bufdelete-module-snacks.bufdelete.all()| @@ -16,15 +16,14 @@ discard. ============================================================================== -1. Config *snacks-bufdelete-config* +1. Types *snacks-bufdelete-types* >lua ---@class snacks.bufdelete.Opts - ---@field buf? number Buffer to delete. If not provided, the current buffer is deleted + ---@field buf? number Buffer to delete. Defaults to the current buffer ---@field force? boolean Delete the buffer even if it is modified ---@field filter? fun(buf: number): boolean Filter buffers to delete ---@field wipe? boolean Wipe the buffer instead of deleting it (see `:h :bwipeout`) - {} < @@ -35,7 +34,7 @@ discard. SNACKS.BUFDELETE() *snacks-bufdelete-module-snacks.bufdelete()* >lua - ---@type fun(buf?: number) + ---@type fun(buf?: number|snacks.bufdelete.Opts) Snacks.bufdelete() < @@ -45,7 +44,8 @@ SNACKS.BUFDELETE.ALL() *snacks-bufdelete-module-snacks.bufdelete.all()* Delete all buffers >lua - Snacks.bufdelete.all() + ---@param opts? snacks.bufdelete.Opts + Snacks.bufdelete.all(opts) < @@ -56,8 +56,8 @@ buffer `buf` if it is a number - or every buffer for which `buf` returns true if it is a function >lua - ---@param buf? number | fun(buf: number): boolean - Snacks.bufdelete.delete(buf) + ---@param opts? number|snacks.bufdelete.Opts + Snacks.bufdelete.delete(opts) < @@ -66,7 +66,8 @@ SNACKS.BUFDELETE.OTHER() *snacks-bufdelete-module-snacks.bufdelete.other()* Delete all buffers except the current one >lua - Snacks.bufdelete.other() + ---@param opts? snacks.bufdelete.Opts + Snacks.bufdelete.other(opts) < Generated by panvimdoc diff --git a/docs/bufdelete.md b/docs/bufdelete.md index e13f2b51..6018d572 100644 --- a/docs/bufdelete.md +++ b/docs/bufdelete.md @@ -7,12 +7,22 @@ a prompt will be shown to save or discard. +## 📚 Types + +```lua +---@class snacks.bufdelete.Opts +---@field buf? number Buffer to delete. Defaults to the current buffer +---@field force? boolean Delete the buffer even if it is modified +---@field filter? fun(buf: number): boolean Filter buffers to delete +---@field wipe? boolean Wipe the buffer instead of deleting it (see `:h :bwipeout`) +``` + ## 📦 Module ### `Snacks.bufdelete()` ```lua ----@type fun(buf?: number) +---@type fun(buf?: number|snacks.bufdelete.Opts) Snacks.bufdelete() ``` @@ -21,7 +31,8 @@ Snacks.bufdelete() Delete all buffers ```lua -Snacks.bufdelete.all() +---@param opts? snacks.bufdelete.Opts +Snacks.bufdelete.all(opts) ``` ### `Snacks.bufdelete.delete()` @@ -32,8 +43,8 @@ Delete a buffer: - or every buffer for which `buf` returns true if it is a function ```lua ----@param buf? number | fun(buf: number): boolean -Snacks.bufdelete.delete(buf) +---@param opts? number|snacks.bufdelete.Opts +Snacks.bufdelete.delete(opts) ``` ### `Snacks.bufdelete.other()` @@ -41,5 +52,6 @@ Snacks.bufdelete.delete(buf) Delete all buffers except the current one ```lua -Snacks.bufdelete.other() +---@param opts? snacks.bufdelete.Opts +Snacks.bufdelete.other(opts) ``` diff --git a/lua/snacks/bufdelete.lua b/lua/snacks/bufdelete.lua index babcabec..4647957c 100644 --- a/lua/snacks/bufdelete.lua +++ b/lua/snacks/bufdelete.lua @@ -1,84 +1,98 @@ ---@class snacks.bufdelete ----@overload fun(buf?: number) +---@overload fun(buf?: number|snacks.bufdelete.Opts) local M = setmetatable({}, { __call = function(t, ...) return t.delete(...) end, }) +---@class snacks.bufdelete.Opts +---@field buf? number Buffer to delete. Defaults to the current buffer +---@field force? boolean Delete the buffer even if it is modified +---@field filter? fun(buf: number): boolean Filter buffers to delete +---@field wipe? boolean Wipe the buffer instead of deleting it (see `:h :bwipeout`) + --- Delete a buffer: --- - either the current buffer if `buf` is not provided --- - or the buffer `buf` if it is a number --- - or every buffer for which `buf` returns true if it is a function ----@param buf? number | fun(buf: number): boolean -function M.delete(buf) - if type(buf) == "function" then - for _, b in ipairs(vim.api.nvim_list_bufs()) do - if vim.bo[b].buflisted and buf(b) then - M.delete(b) +---@param opts? number|snacks.bufdelete.Opts +function M.delete(opts) + opts = opts or {} + opts = type(opts) == "number" and { buf = opts } or opts + opts = type(opts) == "function" and { filter = opts } or opts + ---@cast opts snacks.bufdelete.Opts + + if type(opts.filter) == "function" then + for _, b in ipairs(vim.tbl_filter(opts.filter, vim.api.nvim_list_bufs())) do + if vim.bo[b].buflisted then + M.delete(vim.tbl_extend("force", {}, opts, { buf = b, filter = false })) end end return end - buf = buf or 0 + local buf = opts.buf or 0 buf = buf == 0 and vim.api.nvim_get_current_buf() or buf - -- Ensure we always run in the target buffer - if buf ~= vim.api.nvim_get_current_buf() then - return vim.api.nvim_buf_call(buf, M.delete) - end - - if vim.bo.modified then - local choice = vim.fn.confirm(("Save changes to %q?"):format(vim.fn.bufname()), "&Yes\n&No\n&Cancel") - if choice == 0 or choice == 3 then -- 0 for / and 3 for Cancel - return + vim.api.nvim_buf_call(buf, function() + if vim.bo.modified and not opts.force then + local choice = vim.fn.confirm(("Save changes to %q?"):format(vim.fn.bufname()), "&Yes\n&No\n&Cancel") + if choice == 0 or choice == 3 then -- 0 for / and 3 for Cancel + return + end + if choice == 1 then -- Yes + vim.cmd.write() + end end - if choice == 1 then -- Yes - vim.cmd.write() + + for _, win in ipairs(vim.fn.win_findbuf(buf)) do + vim.api.nvim_win_call(win, function() + if not vim.api.nvim_win_is_valid(win) or vim.api.nvim_win_get_buf(win) ~= buf then + return + end + -- Try using alternate buffer + local alt = vim.fn.bufnr("#") + if alt ~= buf and vim.fn.buflisted(alt) == 1 then + vim.api.nvim_win_set_buf(win, alt) + return + end + + -- Try using previous buffer + local has_previous = pcall(vim.cmd, "bprevious") + if has_previous and buf ~= vim.api.nvim_win_get_buf(win) then + return + end + + -- Create new listed buffer + local new_buf = vim.api.nvim_create_buf(true, false) + vim.api.nvim_win_set_buf(win, new_buf) + end) end - end - - for _, win in ipairs(vim.fn.win_findbuf(buf)) do - vim.api.nvim_win_call(win, function() - if not vim.api.nvim_win_is_valid(win) or vim.api.nvim_win_get_buf(win) ~= buf then - return - end - -- Try using alternate buffer - local alt = vim.fn.bufnr("#") - if alt ~= buf and vim.fn.buflisted(alt) == 1 then - vim.api.nvim_win_set_buf(win, alt) - return - end - - -- Try using previous buffer - local has_previous = pcall(vim.cmd, "bprevious") - if has_previous and buf ~= vim.api.nvim_win_get_buf(win) then - return - end - - -- Create new listed buffer - local new_buf = vim.api.nvim_create_buf(true, false) - vim.api.nvim_win_set_buf(win, new_buf) - end) - end - if vim.api.nvim_buf_is_valid(buf) then - pcall(vim.cmd, "bdelete! " .. buf) - end + if vim.api.nvim_buf_is_valid(buf) then + pcall(vim.cmd, opts.wipe and "bwipeout! " or "bdelete! " .. buf) + end + end) end --- Delete all buffers -function M.all() - return M.delete(function() - return true - end) +---@param opts? snacks.bufdelete.Opts +function M.all(opts) + return M.delete(vim.tbl_extend("force", {}, opts or {}, { + filter = function() + return true + end, + })) end --- Delete all buffers except the current one -function M.other() - return M.delete(function(b) - return b ~= vim.api.nvim_get_current_buf() - end) +---@param opts? snacks.bufdelete.Opts +function M.other(opts) + return M.delete(vim.tbl_extend("force", {}, opts or {}, { + filter = function(b) + return b ~= vim.api.nvim_get_current_buf() + end, + })) end return M