A neovim plugin that displays a vertical indicator showing open buffers and doubles as a customizable buffer picker.
https://github.com/user-attachments/assets/7936371a-cb8e-452f-8c08-e1907e1f1b89
Using lazy.nvim:
{
"ahkohd/buffer-sticks.nvim",
config = function()
require("buffer-sticks").setup()
end,
}
Using packer.nvim:
use {
"ahkohd/buffer-sticks.nvim",
config = function()
require("buffer-sticks").setup()
end
}
return {
"ahkohd/buffer-sticks.nvim",
event = "VeryLazy",
keys = {
{
"<leader>j",
function()
BufferSticks.jump()
end,
desc = "Jump to buffer",
},
{
"<leader>q",
function()
BufferSticks.close()
end,
desc = "Close buffer",
},
{
"<leader>p",
function()
BufferSticks.list({
action = function(buffer, leave)
print("Selected: " .. buffer.name)
leave()
end
})
end,
desc = "Buffer picker",
},
},
config = function()
local sticks = require("buffer-sticks")
sticks.setup({
filter = { buftypes = { "terminal" } },
highlights = {
active = { link = "Statement" },
alternate = { link = "StorageClass" },
inactive = { link = "Whitespace" },
active_modified = { link = "Constant" },
alternate_modified = { link = "Constant" },
inactive_modified = { link = "Constant" },
label = { link = "Comment" },
filter_selected = { link = "Statement" },
filter_title = { link = "Comment" },
list_selected = { link = "Statement" },
},
})
sticks.show()
end,
}
require("buffer-sticks").setup({
offset = { x = 0, y = 0 }, -- Position offset (positive moves inward from right edge)
padding = { top = 0, right = 1, bottom = 0, left = 1 }, -- Padding inside the float
active_char = "──", -- Character for active buffer
inactive_char = " ─", -- Character for inactive buffers
alternate_char = " ─", -- Character for alternate buffer
active_modified_char = "──", -- Character for active modified buffer (unsaved changes)
inactive_modified_char = " ─", -- Character for inactive modified buffers (unsaved changes)
alternate_modified_char = " ─", -- Character for alternate modified buffer (unsaved changes)
transparent = true, -- Remove background color (shows terminal/editor background)
auto_hide = true, -- Auto-hide when cursor is over float (default: true)
label = { show = "list" }, -- Label display: "always", "list", or "never"
list = {
show = { "filename", "space", "label" }, -- List mode display options
active_indicator = "•", -- Symbol for selected item in list mode (arrow navigation)
keys = {
close_buffer = "<C-q>", -- Key to close buffer in list mode
move_up = "<Up>", -- Key to move selection up in list mode
move_down = "<Down>", -- Key to move selection down in list mode
},
filter = {
title = "➜ ", -- Filter prompt title when input is not empty
title_empty = "Filter", -- Filter prompt title when input is empty
active_indicator = "•", -- Symbol for selected item in filter mode
fuzzy_cutoff = 100, -- Cutoff value for fuzzy matching algorithm (default: 100)
keys = {
enter = "/", -- Key to enter filter mode
confirm = "<CR>", -- Key to confirm selection
exit = "<Esc>", -- Key to exit filter mode
move_up = "<Up>", -- Key to move selection up
move_down = "<Down>", -- Key to move selection down
},
},
},
preview = {
enabled = true, -- Enable buffer preview during navigation
mode = "float", -- Preview mode: "float", "current", or "last_window"
float = {
position = "right", -- Float position: "right", "left", or "below"
width = 0.5, -- Width as fraction of screen (0.0 to 1.0)
height = 0.8, -- Height as fraction of screen (0.0 to 1.0)
border = "single", -- Border style: "none", "single", "double", "rounded", "solid", "shadow"
title = nil, -- Window title (string or nil)
title_pos = "center", -- Title position: "left", "center", "right"
footer = nil, -- Window footer (string or nil)
footer_pos = "center", -- Footer position: "left", "center", "right"
},
},
-- winblend = 100, -- Window blend level (0-100, 0=opaque, 100=fully blended)
-- filter = {
-- filetypes = { "help", "qf" }, -- Exclude by filetype (also: "NvimTree", "neo-tree", "Trouble")
-- buftypes = { "terminal" }, -- Exclude by buftype (also: "help", "quickfix", "nofile")
-- names = { ".*%.git/.*", "^/tmp/.*" }, -- Exclude buffers matching lua patterns
-- },
highlights = {
active = { fg = "#bbbbbb" },
alternate = { fg = "#888888" },
inactive = { fg = "#333333" },
active_modified = { fg = "#ffffff" },
alternate_modified = { fg = "#dddddd" },
inactive_modified = { fg = "#999999" },
label = { fg = "#aaaaaa", italic = true },
filter_selected = { fg = "#bbbbbb", italic = true },
filter_title = { fg = "#aaaaaa", italic = true },
list_selected = { fg = "#bbbbbb", italic = true },
}
})
-- Toggle visibility
BufferSticks.toggle()
-- Show
BufferSticks.show()
-- Hide
BufferSticks.hide()
-- Enter list mode to navigate buffers
BufferSticks.list({ action = "open" })
-- Enter list mode to close buffers
BufferSticks.list({ action = "close" })
-- Alias for jumping to buffers (same as list with action="open")
BufferSticks.jump()
-- Alias for closing buffers (same as list with action="close")
BufferSticks.close()
-- Custom action function (buffer picker)
BufferSticks.list({
action = function(buffer, leave)
-- Do something with buffer.id, buffer.name, etc.
print("Selected buffer: " .. buffer.name)
leave() -- Call this to leave list mode
end
})
List mode allows you to quickly navigate to or close buffers by typing their first character(s):
Navigate to buffers:
BufferSticks.list({ action = "open" }) or BufferSticks.jump()Up/Down arrows (configurable) to navigate through buffers/ (configurable) to enter filter mode for fuzzy searchCtrl-Q (configurable) to close the current active bufferEsc or Ctrl-C to cancelClose buffers:
BufferSticks.list({ action = "close" }) or BufferSticks.close()Up/Down arrows (configurable) to navigate through buffers/ (configurable) to enter filter mode for fuzzy searchCtrl-Q (configurable) to close the current active bufferEsc or Ctrl-C to cancelFilter mode:
/ (configurable) to enter filter modeUp/Down arrows (configurable) to navigate filtered resultsEnter to select the highlighted bufferEsc to exit filter mode back to list mode (previous selection is restored)Custom action function (buffer picker):
BufferSticks.list({ action = function(buffer, leave) ... end })Up/Down arrows or type the first character to select a bufferEnter (with arrow selection), your function is called with:buffer: The selected buffer info (with id, name, label, etc.)leave: Function to call when you're done to exit list modeleave()Label Display Options:
label = { show = "always" } - Always show buffer name labelslabel = { show = "list" } - Only show labels when in list mode (default)label = { show = "never" } - Never show labelsList Mode Display Options:
list = { show = { "filename", "space", "label" } }Available elements:
"filename" - Full filename"space" - Spaces between elements"label" - Unique character"stick" - Active/inactive characterBuffer preview shows the content of the selected buffer while navigating in list or filter mode. Preview updates automatically as you move through buffers with arrow keys or type to filter.
Preview Modes:
"float" - Displays buffer in a separate floating window
"right", "left", or "below""current" - Switches buffer in the current window
Esc to restore original buffer (cancel)Enter or type label to confirm selection"last_window" - Previews in the window that was focused before entering list mode
Configuration:
preview = {
enabled = true, -- Enable/disable preview
mode = "float", -- "float", "current", or "last_window"
float = {
position = "right", -- "right", "left", or "below"
width = 0.5, -- 0.0 to 1.0 (fraction of screen)
height = 0.8, -- 0.0 to 1.0 (fraction of screen)
border = "rounded", -- "none", "single", "double", "rounded", "solid", "shadow"
title = "Preview", -- Window title (optional)
title_pos = "center", -- "left", "center", "right"
footer = nil, -- Window footer (optional)
footer_pos = "center", -- "left", "center", "right"
},
}
You can use hex colors:
highlights = {
active = { fg = "#bbbbbb" },
alternate = { fg = "#888888" },
inactive = { fg = "#333333" },
active_modified = { fg = "#ffffff" },
alternate_modified = { fg = "#dddddd" },
inactive_modified = { fg = "#999999" },
label = { fg = "#aaaaaa", italic = true },
filter_selected = { fg = "#bbbbbb", italic = true },
filter_title = { fg = "#aaaaaa", italic = true },
list_selected = { fg = "#bbbbbb", italic = true },
}
Or link to existing highlight groups:
highlights = {
active = { link = "Statement" },
alternate = { link = "StorageClass" },
inactive = { link = "Whitespace" },
active_modified = { link = "Constant" },
alternate_modified = { link = "Constant" },
inactive_modified = { link = "Constant" },
label = { link = "Comment" },
filter_selected = { link = "Statement" },
filter_title = { link = "Comment" },
list_selected = { link = "Statement" },
}
setup(opts) - Initialize the plugin with configurationtoggle() - Toggle buffer sticks visibilityshow() - Show buffer stickshide() - Hide buffer stickslist(opts) - Enter list mode with action ("open", "close", or custom function)jump() - Enter list mode for quick buffer navigation (alias for list({ action = "open" }))close() - Enter list mode to close buffers (alias for list({ action = "close" }))