ahkohd/buffer-sticks.nvim

github github
utility
stars 57
issues 0
subscribers 0
forks 4
CREATED

UPDATED


buffer-sticks.nvim

A neovim plugin that displays a vertical indicator showing open buffers and doubles as a customizable buffer picker.

Neovim Lua

https://github.com/user-attachments/assets/7936371a-cb8e-452f-8c08-e1907e1f1b89

Features

  • 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

Installation

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
}

Quick Setup

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,
}

Configuration

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 },
  }
})

Usage

-- 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

List mode allows you to quickly navigate to or close buffers by typing their first character(s):

Navigate to buffers:

  1. Call BufferSticks.list({ action = "open" }) or BufferSticks.jump()
  2. Selection starts at the currently active buffer
  3. Use Up/Down arrows (configurable) to navigate through buffers
  4. Type the first character of the buffer you want to jump to
  5. If multiple buffers match, continue typing more characters
  6. Press / (configurable) to enter filter mode for fuzzy search
  7. Press Ctrl-Q (configurable) to close the current active buffer
  8. Press Esc or Ctrl-C to cancel

Close buffers:

  1. Call BufferSticks.list({ action = "close" }) or BufferSticks.close()
  2. Selection starts at the currently active buffer
  3. Use Up/Down arrows (configurable) to navigate through buffers
  4. Type the first character of the buffer you want to close
  5. If multiple buffers match, continue typing more characters
  6. Press / (configurable) to enter filter mode for fuzzy search
  7. Press Ctrl-Q (configurable) to close the current active buffer
  8. Press Esc or Ctrl-C to cancel

Filter mode:

  1. While in list mode, press / (configurable) to enter filter mode
  2. Type to fuzzy search through buffers in real-time
  3. Use Up/Down arrows (configurable) to navigate filtered results
  4. Press Enter to select the highlighted buffer
  5. Press Esc to exit filter mode back to list mode (previous selection is restored)

Custom action function (buffer picker):

  1. Call BufferSticks.list({ action = function(buffer, leave) ... end })
  2. Selection starts at the currently active buffer
  3. Use Up/Down arrows or type the first character to select a buffer
  4. If multiple buffers match, continue typing more characters
  5. 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 (with id, name, label, etc.)
    • leave: Function to call when you're done to exit list mode
  6. You control when to exit by calling leave()

Label Display Options:

  • label = { show = "always" } - Always show buffer name labels
  • label = { 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

Preview

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
  • "current" - Switches buffer in the current window

    • Shows immediate preview as you navigate
    • Press Esc to restore original buffer (cancel)
    • Press Enter or 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 = "Preview",   -- Window title (optional)
    title_pos = "center", -- "left", "center", "right"
    footer = nil,        -- Window footer (optional)
    footer_pos = "center", -- "left", "center", "right"
  },
}

Highlight Options

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" },
}

API

  • setup(opts) - Initialize the plugin with configuration
  • toggle() - Toggle buffer sticks visibility
  • show() - Show buffer sticks
  • hide() - Hide buffer sticks
  • list(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" }))