https://github.com/Bekaboo/dropbar.nvim/assets/76579810/e8c1ac26-0321-4762-9975-b20fc3098c5a
Opening drop-down menus or go to definition with a single mouse click
Pick mode for quickly selecting a component in the winbar with shortcuts
Automatically truncating long components
Multiple backends that support fall-backs
dropbar.nvim
comes with five builtin sources:
lsp: gets symbols from language servers using nvim's builtin LSP framework
markdown: a custom incremental parser that gets symbol information about markdown headings
path: gets current file path
treesitter: gets symbols from treesitter parsers using nvim's builtin treesitter integration
terminal: easily switch terminal buffers using the dropdown menu
To make a new source yourself, see making a new source.
For source fall-backs support, see bar options.
Zero config & Zero dependency
dropbar.nvim
does not require nvim-lspconfig, nvim-treesitter
or any third-party UI libraries to work.
As long as the language server or the treesitter parser is installed,
it should work just fine.
Optionally, you can install telescope-fzf-native to add fuzzy search support to dropbar menus.
Drop-down menu components and winbar symbols that response to
mouse/cursor hovering:
:h mousemoveevent
to be enabled.Preview symbols in their source windows when hovering over them in the drop-down menu
Reorient the source window on previewing or after jumping to a symbol
Add scrollbar to the menu when the symbol list is too long
Using lazy.nvim
require('lazy').setup({
{
'Bekaboo/dropbar.nvim',
-- optional, but required for fuzzy finder support
dependencies = {
'nvim-telescope/telescope-fzf-native.nvim',
build = 'make'
},
config = function()
local dropbar_api = require('dropbar.api')
vim.keymap.set('n', '<Leader>;', dropbar_api.pick, { desc = 'Pick symbols in winbar' })
vim.keymap.set('n', '[;', dropbar_api.goto_context_start, { desc = 'Go to start of current context' })
vim.keymap.set('n', '];', dropbar_api.select_next_context, { desc = 'Select next context' })
end
}
})
Using packer.nvim
require('packer').startup(function(use)
use({
'Bekaboo/dropbar.nvim',
requires = {
'nvim-telescope/telescope-fzf-native.nvim',
run = 'make'
},
config = function ()
local dropbar_api = require('dropbar.api')
vim.keymap.set('n', '<Leader>;', dropbar_api.pick, { desc = 'Pick symbols in winbar' })
vim.keymap.set('n', '[;', dropbar_api.goto_context_start, { desc = 'Go to start of current context' })
vim.keymap.set('n', '];', dropbar_api.select_next_context, { desc = 'Select next context' })
end
})
end)
Using native package manager
mkdir -p ~/.local/share/nvim/site/pack/packages/
git clone https://github.com/Bekaboo/dropbar.nvim ~/.local/share/nvim/site/pack/packages/start/dropbar.nvim
Lazy-loading is unneeded as it is already done in plugin/dropbar.lua.
require('dropbar.api').pick()
to enter interactive pick mode or
require('dropbar.api').pick(<idx>)
to directly select a component at
idx
.dropbar_menu_t:fuzzy_find_open()
to interactively
filter, select and preview entries using fzf<Esc>
: exit fzf mode<Up>/<Down>
: move the cursor in fzf mode<CR>
: call the on_click callback of the symbol under the cursor<LeftMouse>
: call the on_click
callback of the symbol at the mouse
click<CR>
: find the first clickable symbol in the current drop-down menu
entry and call its on_click
callbacki
: enter fzf mode from the menuq
/ <Esc>
: close current menuvim.ui.select
Dropbar can be used as a drop-in replacement for Neovim's builtin vim.ui.select
menu.
To enable this functionality, simply replace vim.ui.select
with dropbar.utils.menu.select
:
vim.ui.select = require('dropbar.utils.menu').select
For all available options and their default values, see lua/dropbar/configs.lua.
Below are the detailed explanation of the options.
These options live under opts.bar
and are used to control the behavior of the
winbar:
opts.bar.enable
: boolean|fun(buf: integer?, win: integer?, info: table?): boolean
function(buf, win, _)
if
not vim.api.nvim_buf_is_valid(buf)
or not vim.api.nvim_win_is_valid(win)
or vim.fn.win_gettype(win) ~= ''
or vim.wo[win].winbar ~= ''
or vim.bo[buf].ft == 'help'
then
return false
end
local stat = vim.uv.fs_stat(vim.api.nvim_buf_get_name(buf))
if stat and stat.size > 1024 * 1024 then
return false
end
return vim.bo[buf].ft == 'markdown'
or pcall(vim.treesitter.get_parser, buf)
or not vim.tbl_isempty(vim.lsp.get_clients({
bufnr = buf,
method = 'textDocument/documentSymbol',
}))
end,
opts.bar.attach_events
: string[]
enable()
function and attach the plugin
to corresponding buffer or window{
'OptionSet',
'BufWinEnter',
'BufWritePost',
}
opts.bar.update_debounce
: number
'j'
) to scroll the window1000 / key_repeat_rate
32
opts.bar.update_events.win
: string[]
{
'CursorMoved',
'WinEnter',
'WinResized',
}
opts.bar.update_events.buf
: string[]
{
'BufModifiedSet',
'FileChangedShellPost',
'TextChanged',
'ModeChanged',
}
opts.bar.update_events.global
: string[]
{
'DirChanged',
'VimResized',
}
opts.bar.hover
: boolean
'mousemoveevent'
to be enabledtrue
opts.bar.sources
: dropbar_source_t[]|fun(buf: integer, win: integer): dropbar_source_t[]
function(buf, _)
local sources = require('dropbar.sources')
local utils = require('dropbar.utils')
if vim.bo[buf].ft == 'markdown' then
return {
sources.path,
sources.markdown,
}
end
if vim.bo[buf].buftype == 'terminal' then
return {
sources.terminal,
}
end
return {
sources.path,
utils.source.fallback({
sources.lsp,
sources.treesitter,
}),
}
end
dropbar_source_t
.opts.bar.padding
: { left: number, right: number }
{ left = 1, right = 1 }
opts.bar.pick.pivots
: string
'abcdefghijklmnopqrstuvwxyz'
opts.bar.truncate
: boolean
true
These options live under opts.menu
and are used to control the behavior of the
menu:
opts.menu.quick_navigation
: boolean
CursorMoved
true
opts.menu.entry.padding
: { left: number, right: number }
{ left = 1, right = 1 }
opts.menu.preview
: boolean
true
opts.menu.hover
: boolean
'mousemoveevent'
to be enabledtrue
opts.menu.keymaps
: table<string, function|string|table<string, function>|table<string, string>>
<key> = <function|string>
to map a key in normal mode in the menu
buffer, or use <key> = table<mode, function|string>
to map
a key in specific modes.{
['q'] = '<C-w>q',
['<Esc>'] = '<C-w>q',
['<LeftMouse>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
local clicked_menu = utils.menu.get({ win = mouse.winid })
-- If clicked on a menu, invoke the corresponding click action,
-- else close all menus and set the cursor to the clicked window
if clicked_menu then
clicked_menu:click_at({ mouse.line, mouse.column - 1 }, nil, 1, 'l')
return
end
utils.menu.exec('close')
utils.bar.exec('update_current_context_hl')
if vim.api.nvim_win_is_valid(mouse.winid) then
vim.api.nvim_set_current_win(mouse.winid)
end
end,
['<CR>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local cursor = vim.api.nvim_win_get_cursor(menu.win)
local component = menu.entries[cursor[1]]:first_clickable(cursor[2])
if component then
menu:click_on(component, nil, 1, 'l')
end
end,
['<MouseMove>'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
if M.opts.menu.hover then
utils.menu.update_hover_hl(mouse)
end
if M.opts.menu.preview then
utils.menu.update_preview(mouse)
end
end,
['i'] = function()
local menu = utils.menu.get_current()
if not menu then
return
end
menu:fuzzy_find_open()
end,
},
opts.menu.scrollbar
: table<string, boolean>
{
enable = true,
-- if false, only the scrollbar thumb will be shown
background = true
}
opts.menu.win_configs
: table<string, dropbar_menu_win_config_opts_t>
:h nvim_open_win()
opts.menu.win_configs
accepts either a plain value
which will be passes directly to nvim_open_win()
, or a function that
takes the current menu (see dropbar_menu_t
) as an
argument and returns a value to be passed to nvim_open_win()
.{
border = 'none',
style = 'minimal',
row = function(menu)
return menu.prev_menu
and menu.prev_menu.clicked_at
and menu.prev_menu.clicked_at[1] - vim.fn.line('w0')
or 0
end,
---@param menu dropbar_menu_t
col = function(menu)
if menu.prev_menu then
return menu.prev_menu._win_configs.width
+ (menu.prev_menu.scrollbar and 1 or 0)
end
local mouse = vim.fn.getmousepos()
local bar = require('dropbar.api').get_dropbar(
vim.api.nvim_win_get_buf(menu.prev_win),
menu.prev_win
)
if not bar then
return mouse.wincol
end
local _, range = bar:get_component_at(math.max(0, mouse.wincol - 1))
return range and range.start or mouse.wincol
end,
relative = 'win',
win = function(menu)
return menu.prev_menu and menu.prev_menu.win
or vim.fn.getmousepos().winid
end,
height = function(menu)
return math.max(
1,
math.min(
#menu.entries,
vim.go.pumheight ~= 0 and vim.go.pumheight
or math.ceil(vim.go.lines / 4)
)
)
end,
width = function(menu)
local min_width = vim.go.pumwidth ~= 0 and vim.go.pumwidth or 8
if vim.tbl_isempty(menu.entries) then
return min_width
end
return math.max(
min_width,
math.max(unpack(vim.tbl_map(function(entry)
return entry:displaywidth()
end, menu.entries)))
)
end,
zindex = function(menu)
if menu.prev_menu then
if menu.prev_menu.scrollbar and menu.prev_menu.scrollbar.thumb then
return vim.api.nvim_win_get_config(menu.prev_menu.scrollbar.thumb).zindex
end
return vim.api.nvim_win_get_config(menu.prev_win).zindex
end
end,
}
These options live under opts.fzf
and are used to control the behavior and
appearance of the fuzzy finder interface.
opts.fzf.keymaps
keymaps = {
['<LeftMouse>'] = function()
---@type dropbar_menu_t
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
if not mouse then
return
end
if mouse.winid ~= menu.win then
local default_func = M.opts.menu.keymaps['<LeftMouse>']
if type(default_func) == 'function' then
default_func()
end
menu:fuzzy_find_close(false)
return
elseif mouse.winrow > vim.api.nvim_buf_line_count(menu.buf) then
return
end
vim.api.nvim_win_set_cursor(menu.win, { mouse.line, mouse.column - 1 })
menu:fuzzy_find_click_on_entry(function(entry)
return entry:get_component_at(mouse.column - 1, true)
end)
end,
['<MouseMove>'] = function()
---@type dropbar_menu_t
local menu = utils.menu.get_current()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
if not mouse then
return
end
-- If mouse is not in the menu window or on the border, end preview
-- and clear hover highlights
if
mouse.winid ~= menu.win
or mouse.line <= 0
or mouse.column <= 0
or mouse.winrow > #menu.entries
then
menu = menu:root() --[[@as dropbar_menu_t]]
if menu then
menu:finish_preview(true)
if M.opts.menu.hover then
menu:update_hover_hl()
end
end
return
end
if M.opts.menu.preview then
menu:preview_symbol_at({ mouse.line, mouse.column - 1 }, true)
end
if M.opts.menu.hover then
menu:update_hover_hl({ mouse.line, mouse.column - 1 })
end
end,
['<Up>'] = api.fuzzy_find_prev,
['<Down>'] = api.fuzzy_find_next,
['<C-k>'] = api.fuzzy_find_prev,
['<C-j>'] = api.fuzzy_find_next,
['<C-p>'] = api.fuzzy_find_prev,
['<C-n>'] = api.fuzzy_find_next,
['<CR>'] = api.fuzzy_find_click,
['<S-Enter>'] = function()
api.fuzzy_find_click(-1)
end,
}
opts.fzf.win_configs
:h nvim_open_win
. The fuzzy finder will use its
parent window's config by default, but options set here will override those.win_configs = {
relative = 'win',
anchor = 'NW',
height = 1,
win = function(menu)
return menu.win
end,
width = function(menu)
local function border_width(border)
if type(border) == 'string' then
if border == 'none' or border == 'shadow' then
return 0
end
return 2 -- left and right border
end
local left, right = 1, 1
if
(#border == 1 and border[1] == '')
or (#border == 4 and border[4] == '')
or (#border == 8 and border[8] == '')
then
left = 0
end
if
(#border == 1 and border[1] == '')
or (#border == 4 and border[4] == '')
or (#border == 8 and border[4] == '')
then
right = 0
end
return left + right
end
local menu_width = menu._win_configs.width
+ border_width(menu._win_configs.border)
local self_width = menu._win_configs.width
local self_border = border_width(
(
M.opts.fzf.win_configs
and M.eval(M.opts.fzf.win_configs.border, menu)
)
or (menu.fzf_win_configs and M.eval(
menu.fzf_win_configs.border,
menu
))
or menu._win_configs.border
)
if self_width + self_border > menu_width then
return self_width - self_border
else
return menu_width - self_border
end
end,
row = function(menu)
local menu_border = menu._win_configs.border
if
type(menu_border) == 'string'
and menu_border ~= 'shadow'
and menu_border ~= 'none'
then
return menu._win_configs.height + 1
elseif menu_border == 'none' then
return menu._win_configs.height
end
local len_menu_border = #menu_border
if
len_menu_border == 1 and menu_border[1] ~= ''
or (len_menu_border == 2 or len_menu_border == 4) and menu_border[2] ~= ''
or len_menu_border == 8 and menu_border[8] ~= ''
then
return menu._win_configs.height + 1
else
return menu._win_configs.height
end
end,
col = function(menu)
local menu_border = menu._win_configs.border
if
type(menu_border) == 'string'
and menu_border ~= 'shadow'
and menu_border ~= 'none'
then
return -1
end
if
type(menu_border) == 'table' and menu_border[#menu_border] ~= ''
then
return -1
end
return 0
end,
},
opts.fzf.prompt
prompt = '%#htmlTag# '
opts.fzf.char_pattern
char_pattern = '[%w%p]'
opts.fzf.retain_inner_spaces
retain_inner_spaces = true
opts.fzf.fuzzy_find_on_click
fuzzy_find_on_click = true
These options live under opts.icons
and are used to configure the icons
used by the plugin:
opts.icons.enable
: boolean
true
opts.icons.kinds.dir_icon
: fun(path: string): string, string?|string?
function(_)
return M.opts.icons.kinds.symbols.Folder, 'DropBarIconKindFolder'
end
opts.icons.kinds.file_icon
: fun(path: string): string, string?|string?
function(path)
return M.opts.icons.kinds.symbols.File, 'DropBarIconKindFile'
end
opts.icons.kinds.symbols
: table<string, string>
{
Array = ' ',
Boolean = ' ',
BreakStatement = ' ',
Call = ' ',
CaseStatement = ' ',
Class = ' ',
Color = ' ',
Constant = ' ',
Constructor = ' ',
ContinueStatement = '→ ',
Copilot = ' ',
Declaration = ' ',
Delete = ' ',
DoStatement = ' ',
Enum = ' ',
EnumMember = ' ',
Event = ' ',
Field = ' ',
File = ' ',
Folder = ' ',
ForStatement = ' ',
Function = ' ',
H1Marker = ' ', -- Used by markdown treesitter parser
H2Marker = ' ',
H3Marker = ' ',
H4Marker = ' ',
H5Marker = ' ',
H6Marker = ' ',
Identifier = ' ',
IfStatement = ' ',
Interface = ' ',
Keyword = ' ',
List = ' ',
Log = ' ',
Lsp = ' ',
Macro = ' ',
MarkdownH1 = ' ', -- Used by builtin markdown source
MarkdownH2 = ' ',
MarkdownH3 = ' ',
MarkdownH4 = ' ',
MarkdownH5 = ' ',
MarkdownH6 = ' ',
Method = ' ',
Module = ' ',
Namespace = ' ',
Null = ' ',
Number = ' ',
Object = ' ',
Operator = ' ',
Package = ' ',
Pair = ' ',
Property = ' ',
Reference = ' ',
Regex = ' ',
Repeat = ' ',
Scope = ' ',
Snippet = ' ',
Specifier = ' ',
Statement = ' ',
String = ' ',
Struct = ' ',
SwitchStatement = ' ',
Table = ' ',
Terminal = ' ',
Text = ' ',
Type = ' ',
TypeParameter = ' ',
Unit = ' ',
Value = ' ',
Variable = ' ',
WhileStatement = ' ',
}
opts.icons.ui.bar
: table<string, string>
{
separator = ' ',
extends = '…',
}
opts.icons.ui.menu
: table<string, string>
{
separator = ' ',
indicator = ' ',
}
These options live under opts.symbol
and are used to control the behavior of
the symbols:
opts.symbol.on_click()
: fun(symbol: dropbar_symbol_t, min_width: integer?, n_clicks: integer?, button: string?, modifiers: string?)|false?
<CR>
on the symbolfunction(symbol)
-- Update current context highlights if the symbol
-- is shown inside a menu
if symbol.entry and symbol.entry.menu then
symbol.entry.menu:update_current_context_hl(symbol.entry.idx)
elseif symbol.bar then
symbol.bar:update_current_context_hl(symbol.bar_idx)
end
-- Determine menu configs
local prev_win = nil ---@type integer?
local entries_source = nil ---@type dropbar_symbol_t[]?
local init_cursor = nil ---@type integer[]?
local win_configs = {}
if symbol.bar then -- If symbol inside a dropbar
prev_win = symbol.bar.win
entries_source = symbol.opts.siblings
init_cursor = symbol.opts.sibling_idx
and { symbol.opts.sibling_idx, 0 }
if symbol.bar.in_pick_mode then
---@param tbl number[]
local function tbl_sum(tbl)
local sum = 0
for _, v in ipairs(tbl) do
sum = sum + v
end
return sum
end
win_configs.relative = 'win'
win_configs.win = vim.api.nvim_get_current_win()
win_configs.row = 0
win_configs.col = symbol.bar.padding.left
+ tbl_sum(vim.tbl_map(
function(component)
return component:displaywidth()
+ symbol.bar.separator:displaywidth()
end,
vim.tbl_filter(function(component)
return component.bar_idx < symbol.bar_idx
end, symbol.bar.components)
))
end
elseif symbol.entry and symbol.entry.menu then -- If inside a menu
prev_win = symbol.entry.menu.win
entries_source = symbol.opts.children
end
-- Toggle existing menu
if symbol.menu then
symbol.menu:toggle({
prev_win = prev_win,
win_configs = win_configs,
})
return
end
-- Create a new menu for the symbol
if not entries_source or vim.tbl_isempty(entries_source) then
return
end
local menu = require('dropbar.menu')
local configs = require('dropbar.configs')
symbol.menu = menu.dropbar_menu_t:new({
prev_win = prev_win,
cursor = init_cursor,
win_configs = win_configs,
---@param sym dropbar_symbol_t
entries = vim.tbl_map(function(sym)
local menu_indicator_icon = configs.opts.icons.ui.menu.indicator
local menu_indicator_on_click = nil
if not sym.children or vim.tbl_isempty(sym.children) then
menu_indicator_icon =
string.rep(' ', vim.fn.strdisplaywidth(menu_indicator_icon))
menu_indicator_on_click = false
end
return menu.dropbar_menu_entry_t:new({
components = {
sym:merge({
name = '',
icon = menu_indicator_icon,
icon_hl = 'dropbarIconUIIndicator',
on_click = menu_indicator_on_click,
}),
sym:merge({
on_click = function()
local root_menu = symbol.menu and symbol.menu:root()
if root_menu then
root_menu:close(false)
end
if current_menu then
current_menu:close(false)
end
sym:jump()
end,
}),
},
})
end, entries_source),
})
symbol.menu:toggle()
end,
opts.symbol.preview.reorient
: fun(win: integer, range: {start: {line: integer, character: integer}, end: {line: integer, character: integer}})
win
and the range of the symbol range
function() end
opts.symbol.jump.reorient
: fun(win: integer, range: {start: {line: integer, character: integer}, end: {line: integer, character: integer}})
win
and the range of the symbol range
function() end
These options live under opts.sources
and are used to control the behavior of
each sources.
opts.sources.path.max_depth
: integer
16
opts.sources.path.relative_to
: string|fun(buf: integer, win: integer): string
..
relative pathsfunction(_, win)
-- Workaround for Vim:E5002: Cannot find window number
local ok, cwd = pcall(vim.fn.getcwd, win)
return ok and cwd or vim.fn.getcwd()
end
opts.sources.path.filter
: function(name: string): boolean
function(_)
return true
end
opts.sources.path.modified
: function(sym: dropbar_symbol_t): dropbar_symbol_t
dropbar_symbol_t
in the result got
from the path source and returns an alternative
symboldropbar_symbol_t
to show if the
current buffer is modifiedfunction(sym)
return sym
end
dropbar_symbol_t
function(sym)
return sym:merge({
name = sym.name .. '[+]',
icon = ' ',
name_hl = 'DiffAdded',
icon_hl = 'DiffAdded',
-- ...
})
end
opts.sources.path.preview
: boolean|fun(path: string): boolean?|nil
true
opts.sources.treesitter.max_depth
: integer
16
opts.sources.treesitter.name_regex
: string
[=[[#~!@\*&.]*[[:keyword:]]\+!\?\(\(\(->\)\+\|-\+\|\.\+\|:\+\|\s\+\)\?[#~!@\*&.]*[[:keyword:]]\+!\?\)*]=]
opts.sources.treesitter.valid_types:
string[]
{
'array',
'boolean',
'break_statement',
'call',
'case_statement',
'class',
'constant',
'constructor',
'continue_statement',
'delete',
'do_statement',
'element',
'enum',
'enum_member',
'event',
'for_statement',
'function',
'h1_marker',
'h2_marker',
'h3_marker',
'h4_marker',
'h5_marker',
'h6_marker',
'if_statement',
'interface',
'keyword',
'macro',
'method',
'module',
'namespace',
'null',
'number',
'operator',
'package',
'pair',
'property',
'reference',
'repeat',
'rule_set',
'scope',
'specifier',
'struct',
'switch_statement',
'type',
'type_parameter',
'unit',
'value',
'variable',
'while_statement',
'declaration',
'field',
'identifier',
'object',
'statement',
}
opts.sources.lsp.max_depth
: integer
16
opts.sources.lsp.valid_symbols:
string[]
{
'File',
'Module',
'Namespace',
'Package',
'Class',
'Method',
'Property',
'Field',
'Constructor',
'Enum',
'Interface',
'Function',
'Variable',
'Constant',
'String',
'Number',
'Boolean',
'Array',
'Object',
'Keyword',
'Null',
'EnumMember',
'Struct',
'Event',
'Operator',
'TypeParameter',
}
opts.sources.lsp.request.ttl_init
: number
60
opts.sources.lsp.request.interval
: number
1000
opts.sources.markdown.max_depth
: integer
6
opts.sources.markdown.parse.look_ahead
: number
200
Thanks @willothy for implementing this.
opts.sources.terminal.icon
: string|fun(buf: integer): string
function(_)
return M.opts.icons.kinds.symbols.Terminal or ' '
end
opts.sources.terminal.name
: string|fun(buf: integer): string
vim.api.nvim_buf_get_name
name = function(buf)
local name = vim.api.nvim_buf_get_name(buf)
-- the second result val is the terminal object
local term = select(2, require("toggleterm.terminal").indentify(name))
if term then
return term.display_name or term.name
else
return name
end
end
opts.sources.terminal.show_current: boolean
true
dropbar.nvim
exposes a few functions in lua/dropbar/api.lua
that can be
used to interact with the winbar or the drop-down menu:
get_dropbar(buf: integer?, win: integer): dropbar_t?
utils.bar.get()
dropbar_t
type, see
dropbar_t
get_current_dropbar(): dropbar_t?
utils.bar.get_current()
get_dropbar_menu(win: integer): dropbar_menu_t?
utils.menu.get()
dropbar_menu_t
type, see
dropbar_menu_t
get_current_dropbr_menu(): dropbar_menu_t?
utils.menu.get_current()
goto_context_start(count: integer?)
count
is 0 or nil
, go to the start of current context, or the start
at previous context if cursor is already at the start of current contextcount
is positive, goto the start of count
previous contextselect_next_context()
pick(idx: integer?)
idx
is nil
, enter interactive pick mode to select a componentidx
is a number, directly pick the component at that index if it existsfuzzy_find_toggle(opts: table?)
fuzzy_find_click(component: number | (fun(entry: dropbar_menu_entry_t):dropbar_symbol_t)?)
component
is a number
, the component
-nth symbol is selected,
unless 0
or -1
is supplied, in which case the first or last
clickable component is selected, respectively.function
, it receives the dropbar_menu_entry_t
as an argument
and should return the dropbar_symbol_t
that is to be clicked.fuzzy_find_navigate(direction: 'up'|'down'|integer)
fuzzy_find_prev()
fuzzy_find_next()
Here are some utility functions that can be handy when writing your customize your config:
Defined in lua/dropbar/utils/bar.lua
.
utils.bar.get(opts?): (dropbar_t?)|table<integer, dropbar_t>|table<integer, table<integer, dropbar_t>>
opts.win
is specified, return the dropbar attached the window;opts.buf
is specified, return all dropbars attached the buffer;opts.win
and opts.buf
are specified, return the dropbar
attached the window that contains the buffer;opts.win
nor opts.buf
is specified, return all dropbars in
the form of table<buf, table<win, dropbar_t>>
utils.bar.get_current(): dropbar_t?
Defined in lua/dropbar/utils/menu.lua
.
utils.menu.get(opts): (dropbar_menu_t?)|table<integer, dropbar_menu_t>
opts.win
is specified, return the dropbar menu attached the window;opts.win
is not specified, return all opened dropbar menusutils.menu.get_current(): dropbar_menu_t?
vim.ui.select
opts.format_item
callbackdropbar.nvim
defines the following highlight groups that, override them in
your colorscheme to change the appearance of the drop-down menu, the names
should be self-explanatory:
Highlight group | Attributes |
---|---|
DropBarCurrentContext | { link = 'Visual' } |
DropBarFzfMatch | { link = 'Special' } |
DropBarHover | { link = 'Visual' } |
DropBarIconKindDefault | { link = 'Special' } |
DropBarIconKindArray | { link = 'Operator' } |
DropBarIconKindBoolean | { link = 'Boolean' } |
DropBarIconKindBreakStatement | { link = 'Error' } |
DropBarIconKindCall | { link = 'Function' } |
DropBarIconKindCaseStatement | { link = 'Conditional' } |
DropBarIconKindClass | { link = 'Type' } |
DropBarIconKindConstant | { link = 'Constant' } |
DropBarIconKindConstructor | { link = '@constructor' } |
DropBarIconKindContinueStatement | { link = 'Repeat' } |
DropBarIconKindDeclaration | { link = 'DropBarIconKindDefault' } |
DropBarIconKindDelete | { link = 'Error' } |
DropBarIconKindDoStatement | { link = 'Repeat' } |
DropBarIconKindElseStatement | { link = 'Conditional' } |
DropBarIconKindElement | { link = 'DropBarIconKindDefault' } |
DropBarIconKindEnum | { link = 'Constant' } |
DropBarIconKindEnumMember | { link = 'DropBarIconKindEnumMember' } |
DropBarIconKindEvent | { link = '@lsp.type.event' } |
DropBarIconKindField | { link = 'DropBarIconKindDefault' } |
DropBarIconKindFile | { link = 'DropBarIconKindFolder' } |
DropBarIconKindFolder | { link = 'Directory' } |
DropBarIconKindForStatement | { link = 'Repeat' } |
DropBarIconKindFunction | { link = 'Function' } |
DropBarIconKindH1Marker | { link = 'markdownH1' } |
DropBarIconKindH2Marker | { link = 'markdownH2' } |
DropBarIconKindH3Marker | { link = 'markdownH3' } |
DropBarIconKindH4Marker | { link = 'markdownH4' } |
DropBarIconKindH5Marker | { link = 'markdownH5' } |
DropBarIconKindH6Marker | { link = 'markdownH6' } |
DropBarIconKindIdentifier | { link = 'DropBarIconKindDefault' } |
DropBarIconKindIfStatement | { link = 'Conditional' } |
DropBarIconKindInterface | { link = 'Type' } |
DropBarIconKindKeyword | { link = '@keyword' } |
DropBarIconKindList | { link = 'Operator' } |
DropBarIconKindMacro | { link = 'Macro' } |
DropBarIconKindMarkdownH1 | { link = 'markdownH1' } |
DropBarIconKindMarkdownH2 | { link = 'markdownH2' } |
DropBarIconKindMarkdownH3 | { link = 'markdownH3' } |
DropBarIconKindMarkdownH4 | { link = 'markdownH4' } |
DropBarIconKindMarkdownH5 | { link = 'markdownH5' } |
DropBarIconKindMarkdownH6 | { link = 'markdownH6' } |
DropBarIconKindMethod | { link = 'Function' } |
DropBarIconKindModule | { link = '@module' } |
DropBarIconKindNamespace | { link = '@lsp.type.namespace' } |
DropBarIconKindNull | { link = 'Constant' } |
DropBarIconKindNumber | { link = 'Number' } |
DropBarIconKindObject | { link = 'Statement' } |
DropBarIconKindOperator | { link = 'Operator' } |
DropBarIconKindPackage | { link = '@module' } |
DropBarIconKindPair | { link = 'DropBarIconKindDefault' } |
DropBarIconKindProperty | { link = 'DropBarIconKindDefault' } |
DropBarIconKindReference | { link = 'DropBarIconKindDefault' } |
DropBarIconKindRepeat | { link = 'Repeat' } |
DropBarIconKindRuleSet | { link = '@lsp.type.namespace' } |
DropBarIconKindScope | { link = '@lsp.type.namespace' } |
DropBarIconKindSpecifier | { link = '@keyword' } |
DropBarIconKindStatement | { link = 'Statement' } |
DropBarIconKindString | { link = '@string' } |
DropBarIconKindStruct | { link = 'Type' } |
DropBarIconKindSwitchStatement | { link = 'Conditional' } |
DropBarIconKindTable | { link = 'DropBarIconKindDefault' } |
DropBarIconKindTerminal | { link = 'Number' } |
DropBarIconKindType | { link = 'Type' } |
DropBarIconKindTypeParameter | { link = 'DropBarIconKindDefault' } |
DropBarIconKindUnit | { link = 'DropBarIconKindDefault' } |
DropBarIconKindValue | { link = 'Number' } |
DropBarIconKindVariable | { link = 'DropBarIconKindDefault' } |
DropBarIconKindWhileStatement | { link = 'Repeat' } |
DropBarIconUIIndicator | { link = 'SpecialChar' } |
DropBarIconUIPickPivot | { link = 'Error' } |
DropBarIconUISeparator | { link = 'Comment' } |
DropBarIconUISeparatorMenu | { link = 'DropBarIconUISeparator' } |
DropBarMenuCurrentContext | { link = 'PmenuSel' } |
DropBarMenuFloatBorder | { link = 'FloatBorder' } |
DropBarMenuHoverEntry | { link = 'IncSearch' } |
DropBarMenuHoverIcon | { reverse = true } |
DropBarMenuHoverSymbol | { bold = true } |
DropBarMenuNormalFloat | { link = 'NormalFloat' } |
DropBarMenuSbar | { link = 'PmenuSbar' } |
DropBarMenuThumb | { link = 'PmenuThumb' } |
DropBarPreview | { link = 'Visual' } |
DropBarKindArray | undefined |
DropBarKindBoolean | undefined |
DropBarKindBreakStatement | undefined |
DropBarKindCall | undefined |
DropBarKindCaseStatement | undefined |
DropBarKindClass | undefined |
DropBarKindConstant | undefined |
DropBarKindConstructor | undefined |
DropBarKindContinueStatement | undefined |
DropBarKindDeclaration | undefined |
DropBarKindDelete | undefined |
DropBarKindDoStatement | undefined |
DropBarKindElseStatement | undefined |
DropBarKindElement | undefined |
DropBarKindEnum | undefined |
DropBarKindEnumMember | undefined |
DropBarKindEvent | undefined |
DropBarKindField | undefined |
DropBarKindFile | undefined |
DropBarKindFolder | undefined |
DropBarKindForStatement | undefined |
DropBarKindFunction | undefined |
DropBarKindH1Marker | undefined |
DropBarKindH2Marker | undefined |
DropBarKindH3Marker | undefined |
DropBarKindH4Marker | undefined |
DropBarKindH5Marker | undefined |
DropBarKindH6Marker | undefined |
DropBarKindIdentifier | undefined |
DropBarKindIfStatement | undefined |
DropBarKindInterface | undefined |
DropBarKindKeyword | undefined |
DropBarKindList | undefined |
DropBarKindMacro | undefined |
DropBarKindMarkdownH1 | undefined |
DropBarKindMarkdownH2 | undefined |
DropBarKindMarkdownH3 | undefined |
DropBarKindMarkdownH4 | undefined |
DropBarKindMarkdownH5 | undefined |
DropBarKindMarkdownH6 | undefined |
DropBarKindMethod | undefined |
DropBarKindModule | undefined |
DropBarKindNamespace | undefined |
DropBarKindNull | undefined |
DropBarKindNumber | undefined |
DropBarKindObject | undefined |
DropBarKindOperator | undefined |
DropBarKindPackage | undefined |
DropBarKindPair | undefined |
DropBarKindProperty | undefined |
DropBarKindReference | undefined |
DropBarKindRepeat | undefined |
DropBarKindRuleSet | undefined |
DropBarKindScope | undefined |
DropBarKindSpecifier | undefined |
DropBarKindStatement | undefined |
DropBarKindString | undefined |
DropBarKindStruct | undefined |
DropBarKindSwitchStatement | undefined |
DropBarKindTerminal | undefined |
DropBarKindType | undefined |
DropBarKindTypeParameter | undefined |
DropBarKindUnit | undefined |
DropBarKindValue | undefined |
DropBarKindVariable | undefined |
DropBarKindWhileStatement | undefined |
DropBarFileName
local dropbar = require('dropbar')
local sources = require('dropbar.sources')
local utils = require('dropbar.utils')
vim.api.nvim_set_hl(0, 'DropBarFileName', { fg = '#FFFFFF', italic = true })
local custom_path = {
get_symbols = function(buff, win, cursor)
local symbols = sources.path.get_symbols(buff, win, cursor)
symbols[#symbols].name_hl = 'DropBarFileName'
if vim.bo[buff].modified then
symbols[#symbols].name = symbols[#symbols].name .. ' [+]'
symbols[#symbols].name_hl = 'DiffAdded'
end
return symbols
end,
}
dropbar.setup({
bar = {
sources = function(buf, _)
if vim.bo[buf].ft == 'markdown' then
return {
custom_path,
sources.markdown,
}
end
if vim.bo[buf].buftype == 'terminal' then
return {
sources.terminal,
}
end
return {
custom_path,
utils.source.fallback {
sources.lsp,
sources.treesitter,
},
}
end,
},
})
require('dropbar').setup({
bar = {
enable = function(buf, win, _)
if
not vim.api.nvim_buf_is_valid(buf)
or not vim.api.nvim_win_is_valid(win)
or vim.fn.win_gettype(win) ~= ''
or vim.wo[win].winbar ~= ''
or vim.bo[buf].ft == 'help'
then
return false
end
local stat = vim.uv.fs_stat(vim.api.nvim_buf_get_name(buf))
if stat and stat.size > 1024 * 1024 then
return false
end
return vim.bo[buf].ft == 'markdown'
or vim.bo[buf].ft == 'oil' -- enable in oil buffers
or vim.bo[buf].ft == 'fugitive' -- enable in fugitive buffers
or pcall(vim.treesitter.get_parser, buf)
or not vim.tbl_isempty(vim.lsp.get_clients({
bufnr = buf,
method = 'textDocument/documentSymbol',
}))
end,
},
sources = {
path = {
relative_to = function(buf, win)
-- Show full path in oil or fugitive buffers
local bufname = vim.api.nvim_buf_get_name(buf)
if
vim.startswith(bufname, 'oil://')
or vim.startswith(bufname, 'fugitive://')
then
local root = bufname:gsub('^%S+://', '', 1)
while root and root ~= vim.fs.dirname(root) do
root = vim.fs.dirname(root)
end
return root
end
local ok, cwd = pcall(vim.fn.getcwd, win)
return ok and cwd or vim.fn.getcwd()
end,
},
},
})
┌──────────────────┐
│winbar at win 1000│ {k}th symbol clicked
│ contaning buf 1 ├──────────────────────┐
└───────┬─▲────────┘ │
▼ │ │
_G.dropbar() │
│ ▲ │
┌──────────────┐ ┌──────▼─┴──────┐ │
│sources │ │_G.dropbar.bars│ │
│ ┌───┐ │ └──────┬─▲──────┘ │
│ │lsp│ │ ┌───────┬──▼─┴──┬───────┐ │
│ └───┘ │ ┌─▼─┐ ┌─┴─┐ ┌─┴─┐ ... │
│ ┌──────────┐ │ │[1]│ │[2]│ │[3]│ │
│ │treesitter│ │ └─┬─┘ └─┬─┘ └─┬─┘ │
│ └──────────┘ │ │ ... ... │
│ ... │ └──┬─▲─────────────┬──────┐ │
└─────┬─▲──────┘ ┌─▼─┴──┐ ┌──┴───┐ ... │
│ │ │[1000]│ │[1015]│ │
│ │ └─┬─▲──┘ └──────┘ │
│ │ __call() │ │ return string cache │
│ │ ┌───▼─┴───┐ ┌──────────────▼──────────────┐
│ │ │dropbar_t├────────────────────▶ _G.dropbar.callbacks │
│ │ On update events └───┬─▲───┘ register symbol └──────────────┬──────────────┘
│ │ get_symbols(1, 1000, <cursor>) │ │ on_click() callbacks │
│ └─────────────────────────────────┘ │ ┌──────────┬────▼─────┬─────────┐
└─────────────────────────────────────┘ ┌───▼────┐ ┌───┴────┐ ┌───┴────┐ ...
each source returns dropbar_symbol_t[] │['buf1']│ │['buf2']│ │['buf3']│
dropbar_t adds symbols as its components └───┬────┘ └───┬────┘ └───┬────┘
dropbar_t flushes string cache │ ... ...
└────────┬───────────────┬─────────┐
┌─────▼─────┐ ┌─────┴─────┐ ...
│['win1000']│ │['win1015']│
└─────┬─────┘ └─────┬─────┘
│ ...
┌─────────┬────▼────┬─────────┐
┌───┴───┐ ... ┌────┴────┐ ...
│['fn1']│ │['fn{k}']│
└───────┘ └────┬────┘
▼
invoke _G.dropbar.bars[1][1000].components[k]:on_click()
│
▼
open drop-down menu, goto symbol, etc
dropbar_t
Declared and defined in lua/dropbar/bar.lua
.
dropbar_t
is a class that represents a winbar.
It gets symbolsdropbar_symbol_t[]
from
sourcesdropbar_source_t
and renders them to a
string. It is also responsible for registering on_click
callbacks of each
symbol in the global table _G.dropbar.callbacks
so that nvim knows
which function to call when a symbol is clicked.
dropbar_t
has the following fields:
Field | Type | Description |
---|---|---|
buf |
integer |
the buffer the dropbar is attached to |
win |
integer |
the window the dropbar is attached to |
sources |
dropbar_source_t[] |
sourcesdropbar_source_t[] that provide symbols to the dropbar |
separator |
dropbar_symbol_t |
separatordropbar_symbol_t between symbols |
padding |
{left: integer, right: integer} |
padding to use between the winbar and the window border |
extends |
dropbar_symbol_t |
symboldropbar_symbol_t to use when a symbol is truncated |
components |
dropbar_symbol_t[] |
symbolsdropbar_symbol_t[] to render |
string_cache |
string |
string cache of the dropbar |
in_pick_mode |
boolean? |
whether the dropbar is in pick mode |
symbol_on_hover |
dropbar_symbol_t |
The previous symboldropbar_symbol_t[] under mouse hovering in the dropbar |
last_update_request_time |
float? |
timestamp of the last update request in ms, see :h uv.now() |
dropbar_t
has the following methods:
Method | Description |
---|---|
dropbar_t:new(opts: dropbar_opts_t): dropbar_t |
constructor of dropbar_t |
dropbar_t:del() |
destructor of dropbar_t |
dropbar_t:displaywidth(): integer |
returns the display width of the dropbar |
dropbar_t:truncate() |
truncates the dropbar if it exceeds the display width *side effect: changes dropbar componentsdropbar_symbol_t[] |
dropbar_t:cat(plain: boolean?): string |
concatenates the dropbar components into a string with substrings for highlights and click support if plain is not set; else returns a plain string without substrings for highlights and click support |
dropbar_t:redraw() |
redraws the dropbar |
dropbar_t:update() |
update dropbar componentsdropbar_symbol_t[] and redraw the dropbar afterwards |
dropbar_t:pick_mode_wrap(fn: fun(...): T?, ...): T? |
executes fn with parameters ... in pick mode |
dropbar_t:pick(idx: integer?) |
pick a component from dropbar in interactive pick mode if idx is not given; else pick the idx th component directly |
dropbar_t:update_current_context_hl(bar_idx: integer) |
Update the current context highlight hl-DropBarCurrentContext and hl-DropBarIconCurrentContext assuming the bar_idx th symbol is clicked in the winbar |
dropbar_t:update_hover_hl(col: integer?) |
Highlight the symbol at col as if the mouse is hovering on it |
dropbar_t:__call(): string |
meta method to convert dropbar_t to its string representation |
dropbar_symbol_t
Declared and defined in lua/dropbar/bar.lua
.
dropbar_symbol_t
is a class that represents a symbol in a dropbar. It is the
basic element of dropbar_t
and dropbar_menu_entry_t
.
dropbar_symbol_t
has the following fields:
Field | Type | Description |
---|---|---|
name |
string |
name of the symbol |
icon |
string |
icon of the symbol |
name_hl |
string? |
highlight of the name of the symbol |
icon_hl |
string? |
highlight of the icon of the symbol |
win |
integer? |
the source window the symbol is shown in |
buf |
integer? |
the source buffer the symbol is defined in |
view |
table? |
The original view of the source window, created by winsaveview() , used to restore the view after previewing the symbol |
bar |
dropbar_t? |
the dropbardropbar_t the symbol belongs to, if the symbol is shown inside a winbar |
menu |
dropbar_menu_t? |
menudropbar_menu_t associated with the symbol, if the symbol is shown inside a winbar |
entry |
dropbar_menu_entry_t? |
the dropbar menu entrydropbar_menu_entry_t the symbol belongs to, if the symbol is shown inside a menu |
children |
dropbar_symbol_t[]? |
children of the symbol (e.g. a children of a function symbol can be local variables inside the function) |
siblings |
dropbar_symbol_t[]? |
siblings of the symbol (e.g. a sibling of a symbol that represents a level 4 markdown heading can be other headings with level 4) |
bar_idx |
integer? |
index of the symbol in the dropbardropbar_t |
entry_idx |
integer? |
index of the symbol in the menu entrydropbar_menu_entry_t |
sibling_idx |
integer? |
index of the symbol in the siblings |
range |
{start: {line: integer, character: integer}, end: {line: integer, character: integer}} |
range of the symbol in the source window |
on_click |
fun(this: dropbar_symbol_t, min_width: integer?, n_clicks: integer?, button: string?, modifiers: string?)|false? |
callback to invoke when the symbol is clicked, force disable on_click when the value if set to false |
callback_idx |
integer? |
idx of the on_click callback in _G.dropbar.callbacks[buf][win] , use this to index callback function because bar_idx could change after truncate |
opts |
dropbar_symbol_opts_t? |
options passed to winbar_symbol_t:new() when the symbols is created |
data |
table? |
any extra data associated with the symbol |
dropbar_symbol_t
has the following methods:
Method | Description |
---|---|
dropbar_symbol_t:new(opts: dropbar_symbol_t?): dropbar_symbol_t |
constructor of dropbar_symbol_t |
dropbar_symbol_t:del() |
destructor of dropbar_symbol_t |
dropbar_symbol_t:merge(opts: dropbar_symbol_t) |
create a new dropbar_symbol_t by merging opts into the current dropbar_symbol_t |
dropbar_symbol_t:cat(plain: boolean?): string |
concatenates the symbol into a string with substrings for highlights and click support if plain is not set; else returns a plain string without substrings for highlights and click support |
dropbar_symbol_t:displaywidth(): integer |
returns the display width of the symbol |
dropbar_symbol_t:bytewidth(): integer |
returns the byte width of the symbol |
dropbar_symbol_t:jump() |
jump to the start of the range of the dropbar symbol |
dropbar_symbol_t:preview(orig_view: table?) |
preview the symbol in the source window, use orig_view as the original view of the source window (to restore win view after preview ends) |
dropbar_symbol_t:preview_restore_hl() |
clear the preview highlights in the source window |
dropbar_symbol_t:preview_restore_view() |
restore the view in the source window after previewing the symbol |
dropbar_symbol_t:swap_field(field: string, new_val: any) |
temporarily change the content of a dropbar symbol |
dropbar_symbol_t:restore() |
restore the original value of the fields of a dropbar symbol changed in dropbar_symbol_t:swap_field() |
dropbar_menu_t
Declared and defined in lua/dropbar/menu.lua
.
dropbar_menu_t
is a class that represents a drop-down menu.
dropbar_menu_t
has the following fields:
Field | Type | Description |
---|---|---|
buf |
integer |
buffer number of the menu |
win |
integer |
window id of the menu |
is_opened |
boolean? |
whether the menu is currently opened |
entries |
dropbar_menu_entry_t[] |
entries in the menu |
win_configs |
table |
window configuration, value can be a function, see menu configuration options |
_win_configs |
table? |
evaluated window configuration |
cursor |
integer[]? |
initial cursor position |
prev_win |
integer? |
previous window, assigned when calling new() or automatically determined in open() |
prev_buf |
integer? |
previous buffer, assigned when calling new() or automatically determined in open() |
sub_menu |
dropbar_menu_t? |
submenu, assigned when calling new() or automatically determined when a new menu opens |
prev_menu |
dropbar_menu_t? |
previous menu, assigned when calling new() or automatically determined in open() |
clicked_at |
integer[]? |
last position where the menu was clicked, 1,0-indexed |
prev_cursor |
integer[]? |
previous cursor position in the menu |
symbol_previewed |
dropbar_symbol_t? |
symbol begin previewed in the menu |
dropbar_menu_t
has the following methods:
Method | Description |
---|---|
dropbar_menu_t:new(opts: dropbar_menu_t?): dropbar_menu_t |
constructor of dropbar_menu_t |
dropbar_menu_t:del() |
destructor of dropbar_menu_t |
dropbar_menu_t:root(): dropbar_menu_t? |
get the root menu (menu without prev_menu ) |
dropbar_menu_t:eval_win_configs() |
evaluate window configurations dropbar_menu_t.win_configs and store the result in dropbar_menu_t._win_configs |
dropbar_menu_t:get_component_at(pos: integer[], look_ahead: boolean?): dropbar_symbol_t?, { start: integer, end: integer }? |
get the componentdropbar_symbol_t at position pos and its range it occupies in the entry it belongs to |
dropbar_menu_t:click_at(pos: integer[], min_width: integer?, n_clicks: integer?, button: string?, modifiers: string?) |
simulate a click at pos in the menu |
dropbar_menu_t:click_on(symbol: dropbar_symbol_t, min_width: integer?, n_clicks: integer?, button: string?, modifiers: string?) |
simulate a click at the component symbol dropbar_symbol_t of the menu |
dropbar_menu_t:update_hover_hl(pos: integer[]) |
update the hover highlights (DropBarMenuHover* ) assuming the cursor/mouse is hovering at pos in the menu |
dropbar_menu_t:update_current_context_hl(linenr: integer?) |
update the current context highlight (hl-DropBarMenuCurrentContext ) assuming the cursor is at line linenr in the menu |
dropbar_menu_t:make_buf() |
create the menu buffer from the entriesdropbar_menu_entry_t , must be called after self:eval_win_configs() |
dropbar_menu_t:make_win() |
open the menu window with self._win_configs and set menu options, must be called after self:make_buf() |
dropbar_menu_t:override(opts: dropbar_menu_t?) |
override menu options |
dropbar_menu_t:preview_symbol_at(pos: integer[], look_ahead: boolean?) |
preview the componentdropbar_symbol_t at position pos in the menu |
dropbar_menu_t:finish_preview(restore_view: boolean?) |
finish previewing the symbol, always clear the preview highlights in the source buffer, restore the original view of the source window if restore_view is true or nil |
dropbar_menu_t:quick_navigation(new_cursor: integer[]) |
nagivate the cursor to the neartest clickable component on the current menu entry in the direction of cursor movement |
dropbar_menu_t:open(opts: dropbar_menu_t?) |
open the menu with options opts |
dropbar_menu_t:close(restore_view: boolean?) |
close the menu |
dropbar_menu_t:toggle(opts: dropbar_menu_t?) |
toggle the menu |
dropbar_menu_t:fuzzy_find_restore_entries() |
restore menu buffer and entries in their original order before modified by fuzzy search |
dropbar_menu_t:fuzzy_find_close() |
stop fuzzy finding and clean up allocated memory |
dropbar_menu_t:fuzzy_find_click_on_entry(component: number|fun(dropbar_menu_entry_t):dropbar_symbol_t) |
click on the currently selected fuzzy menu entry, choosing the component to click according to component |
dropbar_menu_t:fuzzy_find_open(opts: table?) |
open the fuzzy search menu, overriding fzf configuration with opts argument |
dropbar_menu_t:fuzzy_find_navigate(direction: 'up'|'down'|integer) |
navigate to the nth previous/next entry while fuzzy finding |
dropbar_menu_entry_t
Declared and defined in lua/dropbar/menu.lua
.
dropbar_menu_entry_t
is a class that represents an entry (row) in a
drop-down menu. A dropbar_menu_t
instance is made up of
multiple dropbar_menu_entry_t
instances while a
dropbar_menu_entry_t
instance can contain multiple
dropbar_symbol_t
instances.
dropbar_menu_entry_t
has the following fields:
Field | Type | Description |
---|---|---|
separator |
dropbar_symbol_t |
separator to use in the entry |
padding |
{left: integer, right: integer} |
padding to use between the menu entry and the menu border |
components |
dropbar_symbol_t[] |
componentsdropbar_symbol_t[] in the entry |
virt_text |
string[][]? |
list of virtual text chunks to display below the entry |
menu |
dropbar_menu_t? |
the menu the entry belongs to |
idx |
integer? |
the index of the entry in the menu |
dropbar_menu_entry_t
has the following methods:
Method | Description |
---|---|
dropbar_menu_entry_t:new(opts: dropbar_menu_entry_t?): dropbar_menu_entry_t |
constructor of dropbar_menu_entry_t |
dropbar_menu_entry_t:del() |
destructor of dropbar_menu_entry_t |
dropbar_menu_entry_t:cat(): string, dropbar_menu_hl_info_t |
concatenate the components into a string, returns the string and highlight infodropbar_menu_hl_info_t |
dropbar_menu_entry_t:displaywidth(): integer |
calculate the display width of the entry |
dropbar_menu_entry_t:bytewidth(): integer |
calculate the byte width of the entry |
dropbar_menu_entry_t:first_clickable(offset: integer?): dropbar_symbol_t?, { start: integer, end: integer }? |
get the first clickable componentdropbar_symbol_t and its range in the dropbar menu entry starting from offset , which defaults to 0 |
dropbar_menu_entry_t:get_component_at(col: integer, look_ahead: boolean?): dropbar_symbol_t?, { start: integer, end: integer }? |
get the componentdropbar_symbol_t at column position col and the range it occupies in the menu entry |
dropbar_menu_entry_t:prev_clickable(col: integer): dropbar_symbol_t?, { start: integer, end: integer }? |
get the previous clickable componentdropbar_symbol_t and its range in the dropbar menu entry given current column position col |
dropbar_menu_entry_t:next_clickable(col: integer): dropbar_symbol_t?, { start: integer, end: integer }? |
get the next clickable componentdropbar_symbol_t and its range in the dropbar menu entry given current column position col |
dropbar_menu_hl_info_t
Declared and defined in lua/dropbar/menu.lua
.
dropbar_menu_hl_info_t
is a class that represents a highlight range in a
single line of a drop-down menu.
dropbar_menu_hl_info_t
has the following fields:
Field | Type | Description |
---|---|---|
start |
integer |
start column of the higlighted range |
end |
integer |
end column of the higlighted range |
hlgroup |
string |
highlight group to use for the range |
ns |
integer? |
namespace to use for the range, nil if using default namespace |
dropbar_source_t
Declared in lua/dropbar/sources/init.lua
.
dropbar_source_t
is a class that represents a source of a drop-down menu.
dropbar_source_t
has the following field:
Field | Type | Description |
---|---|---|
get_symbols |
function(buf: integer, win: integer, cursor: integer[]): dropbar_symbol_t[] |
returns the symbolsdropbar_symbol_t[] to show in the winbar given buffer number buf and cursor position cursor |
dropbar_select_opts_t
Declared in lua/dropbar/utils/menu.lua
.
dropbar_select_opts_t
is a class that represents the options passed to utils.menu.select
(vim.ui.select
with some extensions).
dropbar_select_opts_t
has the following fields:
Field | Type | Description |
---|---|---|
prompt |
string? |
determines what will be shown at the top of the select menu. |
format_item |
fun(item: any): string, string[][]? |
formats the list items for display in the menu, and optionally formats virtual text chunks to be shown below the item. |
preview |
fun(self: dropbar_symbol_t, item: any, idx: integer) |
previews the list item under the cursor. |
preview_close |
fun(self: dropbar_symbol_t, item: any, idx: integer) |
closes the preview when the menu is closed. |
A dropbar_source_t
instance is just a table with
get_symbols
field set to a function that returns an array of
dropbar_symbol_t
instances given the buffer number, the
window id, and the cursor position.
We have seen a simple example of a custom source in the default config of
opts.bar.sources
where the second source is set to a combination
of lsp/treesitter/markdown sources using the utils.source.fallback()
factory
function, which simply returns a table containing a get_symbols()
function
where each source passed to utils.source.fallback()
is queried and the first
non-empty result get from the sources is returned as the result of the combined
source.
Here is another example of a custom source that will always return two symbols
saying 'Hello' and 'dropbar' with highlights 'hl-Keyword'
and 'hl-Title'
and a smiling face shown in 'hl-WarningMsg'
at the start of the first symbol;
clicking on the first symbol will show a notification message saying 'Have you
smiled today?', followed by the smiling face icon used in the in dropbar symbol:
local bar = require('dropbar.bar')
local custom_source = {
get_symbols = function(_, _, _)
return {
bar.dropbar_symbol_t:new({
icon = ' ',
icon_hl = 'WarningMsg',
name = 'Hello',
name_hl = 'Keyword',
on_click = function(self)
vim.notify('Have you smiled today? ' .. self.icon)
end,
}),
bar.dropbar_symbol_t:new({
name = 'dropbar',
name_hl = 'Title',
}),
}
end,
}
Add this source to opts.bar.sources
table to see it in action:
require('dropbar').setup({
bar = {
sources = {
custom_source,
},
},
})
The following example shows how to make a source that returns two symbols with the first symbol having a drop-down menu with a single entry saying 'World':
local bar = require('dropbar.bar')
local menu = require('dropbar.menu')
local custom_source = {
get_symbols = function(_, _, _)
return {
bar.dropbar_symbol_t:new({
icon = ' ',
icon_hl = 'WarningMsg',
name = 'Hello',
name_hl = 'Keyword',
on_click = function(self)
self.menu = menu.dropbar_menu_t:new({
entries = {
menu.dropbar_menu_entry_t:new({
components = {
bar.dropbar_symbol_t:new({
icon = ' ',
icon_hl = 'WarningMsg',
name = 'World',
name_hl = 'Keyword',
on_click = function(sym)
vim.notify('Have you smiled today? ' .. sym.icon)
end,
}),
},
}),
},
})
self.menu:toggle()
end,
}),
bar.dropbar_symbol_t:new({
name = 'dropbar',
icon = ' ',
name_hl = 'Special',
icon_hl = 'Error',
}),
}
end,
}
on_click()
Callbackdropbar_symbol_t:new()
defines a default on_click()
callback if non is provided.
The default on_click()
callback will look for these fields in the symbol
instance and create a drop-down menu accordingly on click, for more information
about these fields see dropbar_symbol_t
.
For creating the drop-down menu:
dropbar_symbol_t.siblings
dropbar_symbol_t.sibling_idx
dropbar_symbol_t.children
For jumping to the symbol or previewing it:
dropbar_symbol_t.range
dropbar_symbol_t.win
dropbar_symbol_t.buf
The following example shows a source that utilizes the default on_click()
callback:
local bar = require('dropbar.bar')
local custom_source = {
get_symbols = function(buf, win, _)
return {
bar.dropbar_symbol_t:new({
name = 'Section 1',
name_hl = 'Keyword',
siblings = {
bar.dropbar_symbol_t:new({
name = 'Section 2',
name_hl = 'WarningMsg',
}),
bar.dropbar_symbol_t:new({
name = 'Section 3',
name_hl = 'Error',
}),
bar.dropbar_symbol_t:new({
name = 'Section 4',
name_hl = 'String',
children = {
bar.dropbar_symbol_t:new({
buf = buf,
win = win,
name = 'Section 4.1',
name_hl = 'String',
-- Will jump to line 3, col 4 (0-indexed) when clicked in the
-- menu
range = {
start = { line = 3, character = 4 },
['end'] = { line = 5, character = 6 },
}
}),
},
}),
},
}),
}
end,
}
To see this source in action add it to opts.bar.sources
table:
require('dropbar').setup({
bar = {
sources = {
custom_source,
},
},
})
If the symbol fields siblings
or children
are expensive to compute, you can
use meta-tables to lazy-load them, so that they are only computed when a menu
is opened:
local bar = require('dropbar.bar')
local custom_source = {
get_symbols = function(_, _, _)
return {
bar.dropbar_symbol_t:new(setmetatable({
name = 'Section 1',
name_hl = 'Keyword',
}, {
__index = function(self, key)
if key == 'siblings' then
self[siblings] = -- [[ compute siblings ]]
return self[siblings]
end
if key == 'children' then
self[children] = -- [[ compute children ]]
return self[children]
end
-- ...
end,
})),
}
end,
}
To see concrete examples of lazy-loading see
lua/dropbar/sources
.