- Visual representation of open buffers
- Highlights for active, alternate, inactive, and modified buffers
- List mode for quick buffer navigation or closing by typing characters
- Filter mode with fuzzy search for finding buffers quickly
- Buffer preview while navigating with configurable display modes
- Custom action functions for building buffer pickers
- Configurable positioning and appearance
- Transparent background support
- Persists highlight configuration across colorscheme changes
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: nil/true = filename, false = no title, "string" = custom (default: nil/filename)
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()
-- Check if buffer list is visible
local visible = BufferSticks.is_visible()
-- 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:
- Call
BufferSticks.list({ action = "open" })orBufferSticks.jump() - Selection starts at the currently active buffer
- Use
Up/Downarrows (configurable) to navigate through buffers - Type the first character of the buffer you want to jump to
- If multiple buffers match, continue typing more characters
- Press
/(configurable) to enter filter mode for fuzzy search - Press
Ctrl-Q(configurable) to close the current active buffer - Press
EscorCtrl-Cto cancel
Close buffers:
- Call
BufferSticks.list({ action = "close" })orBufferSticks.close() - Selection starts at the currently active buffer
- Use
Up/Downarrows (configurable) to navigate through buffers - Type the first character of the buffer you want to close
- If multiple buffers match, continue typing more characters
- Press
/(configurable) to enter filter mode for fuzzy search - Press
Ctrl-Q(configurable) to close the current active buffer - Press
EscorCtrl-Cto cancel
Filter buffers using fuzzy matching:
- While in list mode, press
/(configurable) to enter filter mode - Type to fuzzy search through buffers in real-time
- Use
Up/Downarrows (configurable) to navigate filtered results - Press
Enterto select the highlighted buffer - Press
Escto exit filter mode back to list mode (previous selection is restored)
Custom action function (buffer picker):
- Call
BufferSticks.list({ action = function(buffer, leave) ... end }) - Selection starts at the currently active buffer
- Use
Up/Downarrows or type the first character to select a buffer - If multiple buffers match, continue typing more characters
- When a match is found (by typing) or when you press
Enter(with arrow selection), your function is called with:buffer: The selected buffer info (withid,name,label, etc.)leave: Function to call when you're done to exit list mode
- You control when to exit by calling
leave()
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 labels
List Mode Display Options:
- Default:
list = { show = { "filename", "space", "label" } }
Available elements:
"filename"- Full filename"space"- Spaces between elements"label"- Unique character"stick"- Active/inactive character
Buffer 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- Configurable position:
"right","left", or"below" - Configurable size as fraction of screen
- Customizable border, title, and footer
- Default: right side, 50% width, 80% height, single border, filename as title
- Configurable position:
-
"current"- Switches buffer in the current window- Shows immediate preview as you navigate
- Press
Escto restore original buffer (cancel) - Press
Enteror type label to confirm selection
-
"last_window"- Previews in the window that was focused before entering list mode- Useful when you activate list mode from a split
- Preview appears in your previous window while you navigate
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 = nil, -- nil/true = show filename (default), false = no title, "string" = custom title
title_pos = "center", -- "left", "center", "right"
footer = nil, -- Window footer (optional)
footer_pos = "center", -- "left", "center", "right"
},
}Title options:
title = nilortitle = true- Shows the buffer filename (default behavior)title = false- No title displayedtitle = " Custom "- Shows custom text
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 forlist({ action = "open" }))is_visible()- Returns the visibility statusclose()- Enter list mode to close buffers (alias forlist({ action = "close" }))
PRs are welcome! If you have ideas or find bugs, open an issue.