Skip to content

Commit

Permalink
feat: Add toggle_pin and close_unpinned
Browse files Browse the repository at this point in the history
Adds Ex commands `Hbac toggle_pin` and `Hbac close_unpinned` and their Lua counterparts, renaming `require("hbac)".close_unused()` to `close_unpinned`.

`toggle_pin` adds or removes the current buffer to/from the pinned buffers table. Pinned buffers won't be closed by the autoclose function after reaching the user-defined threshold. Instead, the oldest unpinned/unedited buffers will be closed instead.  Pinning buffers also makes it possible to keep more buffers open than the threshold without having to change hbac's options. Unpinning a buffer makes that buffer eligible for auto-close the next time a new buffer is opened.

Toggling the pin state with a command pins a buffer without having to edit it or enter insert mode to pin it implicitly. Moreover, the user is notified when a buffer is pinned/unpinned, giving the user explicit feedback on a buffer's pin state.

This is a squashed commit with major refactors made by @axkirillov
  • Loading branch information
al-ce authored and axkirillov committed Apr 26, 2023
1 parent c7f7104 commit 377522b
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 97 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Automagically close the unedited buffers in your bufferlist when it becomes too
# description
You like using the buffer list, but you hate it when it has too many buffers, because you loose the overview for what files you are *actually* working on. Indeed, a lot of the times, when browsing code you want to look at some files, that you are not actively working on, like checking the definitions or going down the callstack when debugging. These files then pollute the bufferlist and make it harder to find ones you actually care about.
Reddit user **xmsxms** [posted](https://www.reddit.com/r/neovim/comments/12c4ad8/closing_unused_buffers/?utm_source=share&utm_medium=web2x&context=3) a script that marks all once edited files in a session as important and provides a keybinding to close all the rest. In fact, I used some of his code in this plugin, and you can achieve the same effect as his script using hbac.
The main feature of this plugin, however, is the automatic closing of buffers. If the number of buffers reaches a threschold (default is 10), the oldest unedited buffer will be closed once you open a new one.
The main feature of this plugin, however, is the automatic closing of buffers. If the number of buffers reaches a threshold (default is 10), the oldest unedited buffer will be closed once you open a new one.

# installation

Expand Down Expand Up @@ -37,4 +37,12 @@ Let hbac do its magick 😊

or

use `require("hbac").close_unused()` to close all unedited buffers
- `:Hbac toggle_pin` - toggle a pin of the current buffer to prevent it from being auto-closed
- `:Hbac close_unpinned` - close all unedited/unpinned buffers

or, if you prefer to use lua:

```lua
require("hbac").toggle_pin()
require("hbac").close_unpinned()
```
41 changes: 41 additions & 0 deletions lua/hbac/command.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
local state = require("hbac.state")

local M = {
subcommands = {}
}

M.subcommands.close_unpinned = function()
local curbufnr = vim.api.nvim_get_current_buf()
local buflist = vim.api.nvim_list_bufs()
for _, bufnr in ipairs(buflist) do
if vim.bo[bufnr].buflisted and bufnr ~= curbufnr and not state.is_pinned(bufnr) then
vim.cmd('bd ' .. tostring(bufnr))
end
end
print("Hbac: Closed unpinned buffers")
end


M.subcommands.toggle_pin = function()
local bufnr = vim.api.nvim_get_current_buf()
print("Hbac: Buffer " .. bufnr .. " " .. state.toggle_pin(bufnr))
end

M.vim_cmd_name = "Hbac"

M.vim_cmd_func = function(arg)
if M.subcommands[arg] then
M.subcommands[arg]()
else
print("Unknown Hbac command:", arg)
end
end

M.vim_cmd_opts = {
nargs = 1,
complete = function()
return { unpack(vim.tbl_keys(M.subcommands)) }
end,
}

return M
100 changes: 5 additions & 95 deletions lua/hbac/init.lua
Original file line number Diff line number Diff line change
@@ -1,98 +1,8 @@
local id = vim.api.nvim_create_augroup("hbac", {
clear = false
})

local M = {
persistant_buffers = {},
}

M.persist_buffer = function(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
M.persistant_buffers[bufnr] = true
end

M.setup = function(opts)
opts = opts or {
autoclose = true,
threshold = 10
}

vim.api.nvim_create_autocmd({ "BufRead" }, {
group = id,
pattern = { "*" },
callback = function()
vim.api.nvim_create_autocmd({ "InsertEnter", "BufModifiedSet" }, {
buffer = 0,
once = true,
callback = function()
M.persist_buffer()
end
})
end
})

if not opts.autoclose then
return
end

vim.api.nvim_create_autocmd({ "BufEnter" }, {
group = id,
pattern = { "*" },
callback = function()
local bufnr = vim.api.nvim_get_current_buf()
local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype')
-- if the buffer is not a file - do nothing
if buftype ~= "" then
return
end

if M.persistant_buffers[M.last] then
return
end

local min = 2 ^ 1023
local buffers = vim.api.nvim_list_bufs()
local num_buffers = 0
for _, buf in ipairs(buffers) do
local name = vim.api.nvim_buf_get_name(buf)
local listed = vim.api.nvim_buf_get_option(buf, 'buflisted')
if name ~= "" and listed then
num_buffers = num_buffers + 1
if not M.persistant_buffers[buf] then
min = math.min(min, buf)
end
end
end

if num_buffers <= opts.threshold then
return
end

if min == bufnr then
return
end

if min >= 2 ^ 1023 then
return
end

vim.api.nvim_buf_delete(min, {})
end
})
end


local function close_unused()
local curbufnr = vim.api.nvim_get_current_buf()
local buflist = vim.api.nvim_list_bufs()
for _, bufnr in ipairs(buflist) do
if vim.bo[bufnr].buflisted and bufnr ~= curbufnr and not M.persistant_buffers[bufnr] then
vim.cmd('bd ' .. tostring(bufnr))
end
end
end
local command = require("hbac.command")

return {
close_unused = close_unused,
setup = M.setup,
setup = require("hbac.setup").setup,
cmd = command.vim_cmd_func,
close_unpinned = command.subcommands.close_unpinned,
toggle_pin = command.subcommands.toggle_pin
}
86 changes: 86 additions & 0 deletions lua/hbac/setup.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
local command = require("hbac.command")
local state = require("hbac.state")

local M = {}

local id = vim.api.nvim_create_augroup("hbac", {
clear = false
})

M.setup = function(opts)
opts = opts or {
autoclose = true,
threshold = 10
}

vim.api.nvim_create_autocmd({ "BufRead" }, {
group = id,
pattern = { "*" },
callback = function()
vim.api.nvim_create_autocmd({ "InsertEnter", "BufModifiedSet" }, {
buffer = 0,
once = true,
callback = function()
local bufnr = vim.api.nvim_get_current_buf()
state.toggle_pin(bufnr)
end
})
end
})

vim.api.nvim_create_user_command(
command.vim_cmd_name,
function(args)
command.vim_cmd_func(args.args)
end,
command.vim_cmd_opts
)

if not opts.autoclose then
return
end

vim.api.nvim_create_autocmd({ "BufEnter" }, {
group = id,
pattern = { "*" },
callback = function()
local bufnr = vim.api.nvim_get_current_buf()
local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype')
-- if the buffer is not a file - do nothing
if buftype ~= "" then
return
end

local min = 2 ^ 1023
local buffers = vim.api.nvim_list_bufs()
local num_buffers = 0
for _, buf in ipairs(buffers) do
local name = vim.api.nvim_buf_get_name(buf)
local listed = vim.api.nvim_buf_get_option(buf, 'buflisted')
if name ~= "" and listed then
num_buffers = num_buffers + 1
if not state.is_pinned(buf) then
min = math.min(min, buf)
end
end
end

if num_buffers <= opts.threshold then
return
end

if min == bufnr then
return
end

if min >= 2 ^ 1023 then
return
end

vim.api.nvim_buf_delete(min, {})
end
})

end

return M
19 changes: 19 additions & 0 deletions lua/hbac/state.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
local M = {
pinned_buffers = {},
}

M.toggle_pin = function(bufnr)
if M.pinned_buffers[bufnr] == true then
M.pinned_buffers[bufnr] = false
return "unpinned"
end

M.pinned_buffers[bufnr] = true
return "pinned"
end

M.is_pinned = function(bufnr)
return M.pinned_buffers[bufnr] == true
end

return M

0 comments on commit 377522b

Please sign in to comment.