I want to manage Neovim buffers easily, without the mental overhead of remembering its ids or partial names. Buffer management includes moving to a buffer and deleting/adding one or more buffers.
Use a buffer-like floating window where all the open buffers are listed. To select one buffer, just hit its line number, or move to it and press <CR>. To delete the buffer, delete it from the list. To add it (predictably) add a filename to the list.
- Neovim 0.5.0+ required
- Install
buffer_managerusing your favorite plugin manager. E.g.Packer.nvim:
use 'nvim-lua/plenary.nvim' -- basic dependency
use 'j-morano/buffer_manager.nvim':lua require("buffer_manager.ui").toggle_quick_menu()Then, move to one of them, and open it with <CR>.
Alternative: press the key corresponding to its line number (notice that, in this case, 0 maps to 10, since there is no 0 line).
Write the filename of the new buffer.
(Some people will find this useless, but I often use this functionality together with an autocomplete for files.)
Tip: you can use the Neovim built-in file autocomplete functionality (<C-x><C-f>) to ease the opening of new files.
If the file does not exist, a new empty buffer will be created, which will be written to the specified file when it is saved.
Delete it in the buffer menu.
Note: the plugin does not remove terminal buffers or modified buffers.
The buffers can be reorganized in any way. To do it, just move the name of the buffer to the chosen line.
:lua require("buffer_manager.ui").nav_next()
:lua require("buffer_manager.ui").nav_prev()Introduce the filename interactively:
:lua require'buffer_manager.ui'.save_menu_to_file()
:lua require'buffer_manager.ui'.load_menu_from_file()Introduce the filename directly as a function argument:
:lua require'buffer_manager.ui'.save_menu_to_file('bm')
:lua require'buffer_manager.ui'.load_menu_from_file('bm')The plugin can be configured through the setup function:
require("buffer_manager").setup({ })select_menu_item_commands(table): Lua table containing the keys and the correspondingcommandto run for the buffer under the cursor.line_keys(string): keys bound to each line of the buffer menu, in order.focus_alternate_buffer(boolean): place the cursor over the alternate buffer instead of the current buffer.width(number|nil): Width in columns (if > 1) or relative to window width (if <= 1). When relative, the value is treated as a percentage of the window width. For example,0.5means 50% of the window width.height(number|nil): Height in rows (if > 1) or relative to window height (if <= 1). When relative, the value is treated as a percentage of the window height. For example,0.5means 50% of the window height.short_file_names(boolean): Shorten buffer names: filename+extension, preceded by the number of levels under the current dir and a slash.show_depth(boolean): Show the number of levels under the current dir before the filename (n|filename).short_term_names(boolean): Shorten terminal buffer names.loop_nav(boolean): Loop or not the files when usingnav_nextandnav_prev. Whenfalse,nav_prevdoes nothing when at first buffer, and either doesnav_nextwhen at last one. Whentrue,nav_nextgoes to the first buffer when at last one, andnav_prevgoes to the last buffer when at first one.highlight(string): highlight for the window. Format:from1:to1,from2:to2. E.g.Normal:MyCustomNormal. (See:help winhighlight.)win_extra_options(table): extra options for the menu window. E.g.{ relativenumber = true }. (See:help option-list.)borderchars(table): border characters for the menu window.format_function(function|nil): support for custom function to format buffer names. The function should receive a string and return a string. This option is incompatible withshort_file_names. To use it,short_file_namesmust be set tofalse. By default, the function isnil, which means no special formatting is applied.order_buffers(string|nil): order the buffers in the menu. Options are"filename","bufnr","lastused"and"fullpath". By default, it isnil, which means the buffers are not automatically ordered. Ifreverseis added to the option, the buffers are ordered in reverse order. For example,order_buffers = 'filename:reverse'.show_indicators(string|nil): show indicators for buffers in virtual text. See:help lsfor more information about indicators. Possible values are"before"(before filename) and"after"(after filename). When set tonil, no indicators are shown.toggle_key_bindings(table): table with the keys to toggle the menu. The default is{ "q", "<ESC>" }, which means that the menu can be closed withqor<ESC>.use_shortcuts(boolean): whether to use characters from filenames to navigate to them. Iftrue, the first character of the filename is used as a shortcut (i.e. as a key for the line). If the character is already used by another filename, the next character is used, and so on. It is important to note that this mode overrides the default Vim keybindings. E.g.,d, if there is file namesdata.lua, would behave as a shortcut in normal mode, instead of Vim'sdelete. To avoid this, and enter the regularNormalmode with default keybindings, just press<space>. The default is for this optionfalse.
In addition, you can specify a custom color for the modified buffers, the indicators, and the shortcut characters, by setting the corresponding highlight groups to the desired color. For example:
vim.api.nvim_set_hl(0, "BufferManagerModified", { fg = "#0000af" })
vim.api.nvim_set_hl(0, "BufferManagerShortcut", { fg = "#cc0000", bold = true })
vim.api.nvim_set_hl(0, "BufferManagerIndicator", { fg = "#999999", italic = true }) local default_config = {
line_keys = "1234567890",
select_menu_item_commands = {
edit = {
key = "<CR>",
command = "edit"
}
},
focus_alternate_buffer = false,
width = nil,
height = nil,
short_file_names = false,
show_depth = true,
short_term_names = false,
loop_nav = true,
highlight = "",
win_extra_options = {},
borderchars = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" },
format_function = nil,
order_buffers = nil,
show_indicators = nil,
toggle_key_bindings = { "q", "<ESC>" },
use_shortcuts = false,
}local opts = {noremap = true}
local map = vim.keymap.set
---- Setup
require("buffer_manager").setup({
select_menu_item_commands = {
v = {
key = "<C-v>",
command = "vsplit"
},
h = {
key = "<C-h>",
command = "split"
}
},
focus_alternate_buffer = false,
short_file_names = true,
short_term_names = true,
loop_nav = false,
highlight = 'Normal:BufferManagerBorder',
win_extra_options = {
winhighlight = 'Normal:BufferManagerNormal',
},
use_shortcuts = true,
})
---- Navigate buffers bypassing the menu
local bmui = require("buffer_manager.ui")
local keys = '1234567890'
for i = 1, #keys do
local key = keys:sub(i,i)
map(
'n',
string.format('<leader>%s', key),
function () bmui.nav_file(i) end,
opts
)
end
---- Just the menu
map({ 't', 'n' }, '<M-Space>', bmui.toggle_quick_menu, opts)
---- Open menu and search
map({ 't', 'n' }, '<M-m>', function ()
bmui.toggle_quick_menu()
-- wait for the menu to open
vim.defer_fn(function ()
vim.fn.feedkeys('/')
end, 50)
end, opts)
---- Next/Prev
map('n', '<M-j>', bmui.nav_next, opts)
map('n', '<M-k>', bmui.nav_prev, opts)
---- Navigate to the first terminal buffer
local function string_starts(string, start)
return string.sub(string, 1, string.len(start)) == start
end
local function nav_term()
-- Go to the first terminal buffer
bmui.update_marks()
for idx, mark in pairs(bm.marks) do
if string_starts(mark.filename, "term://") then
bmui.nav_file(idx)
return
end
end
-- If no terminal buffer is found, create a new one
vim.cmd('terminal')
end
map('n', '<leader>t', nav_term, opts)Since the buffer menu is just a buffer with the specific file type buffer_manager, you can define your own remaps using an autocmd for this filetype. For example, the following remaps allow to move a line up and down in visual mode with capital K and J, respectively.
autocmd FileType buffer_manager vnoremap J :m '>+1<CR>gv=gv
autocmd FileType buffer_manager vnoremap K :m '<-2<CR>gv=gvThis is very useful for reorganizing the buffers.
You can also set the previous autocmds with Lua as follows:
vim.api.nvim_command([[
autocmd FileType buffer_manager vnoremap J :m '>+1<CR>gv=gv
autocmd FileType buffer_manager vnoremap K :m '<-2<CR>gv=gv
]])- Logs are written to
buffer_manager.logwithin the nvim cache path (:echo stdpath("cache")) - Available log levels are
trace,debug,info,warn,error, orfatal.warnis default - Log level can be set with
vim.g.buffer_manager_log_level(must be beforesetup()) - Launching nvim with
BUFFER_MANAGER_LOG=debug nvimtakes precedence overvim.g.buffer_manager_log_level. - Invalid values default back to
warn.
All contributions are welcome! Just open a pull request.
Furthermore, feel free to open an issue if something is not working as expected.
All feedback is appreciated!
This plugin is based on Harpoon, an amazing plugin written by ThePrimeagen to easily navigate previously marked terminals and files.
Also, special thanks to bufdelete.nvim, for showing how to remove buffers correctly.
