IDE-like breadcrumbs, out of the box
A polished, IDE-like, highly-customizable winbar for Neovim
with drop-down menu support and mutiple backends
dropbar-demo-light.mp4
-
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 backbends that support fall-backs
dropbar.nvim
comes with four 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
To make a new source yourself, see making a new source.
For source fall-backs support, see bar bptions.
-
-
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. -
Show highlights in the drop-down menu according to current mouse/cursor position, see
:h mousemev
and:h <MouseMove>
-
Preview symbol ranges in original window when hovering over them in the drop-down menu
- Neovim Nightly (>= 0.10.0-dev)
- Optional
- nvim-web-devicons, if you want to see icons for different filetypes
- Working language server installation for the lsp source to work
- Working treesitter parser installation for the treesitter source to work
-
Using lazy.nvim
require('lazy').setup({ { 'Bekaboo/dropbar.nvim' } })
-
Using packer.nvim
require('packer').startup(function(use) use('Bekaboo/dropbar.nvim') 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.
- Basics
- Moves the cursor around and see the winbar reflects your current context
- Mouse support
- Click on a component in the winbar to open a drop-down menu of its siblings
- Click on an entry in the drop-down menu to go to its location
- Click on the indicator in the drop-down menu to open a sub-menu of its children
- Pick mode
- Use
require('dropbar.api').pick()
to enter interactive pick mode orrequire('dropbar.api').pick(<idx>)
to directly select a component atidx
. - Inside interactive pick mode, press the corresponding pivot shown before each component to select it
- Use
- Default keymaps in drop-down menu
<LeftMouse>
: call theon_click
callback of the symbol at the mouse click<CR>
: find the first clickable symbol in the current drop-down menu entry and call itson_click
callback- To disable, remap or add new keymaps in the drop-down menu, see menu options
A full list of all available options and their default values:
---@class dropbar_configs_t
local opts = {
general = {
---@type boolean|fun(buf: integer, win: integer): boolean
enable = function(buf, win)
return not vim.api.nvim_win_get_config(win).zindex
and vim.bo[buf].buftype == ''
and vim.api.nvim_buf_get_name(buf) ~= ''
and not vim.wo[win].diff
end,
update_events = {
'CursorMoved',
'CursorMovedI',
'DirChanged',
'FileChangedShellPost',
'TextChanged',
'TextChangedI',
'VimResized',
'WinResized',
'WinScrolled',
},
},
icons = {
kinds = {
use_devicons = true,
symbols = {
Array = ' ',
Boolean = ' ',
BreakStatement = ' ',
Call = ' ',
CaseStatement = ' ',
Class = ' ',
Color = ' ',
Constant = ' ',
Constructor = ' ',
ContinueStatement = '→ ',
Copilot = ' ',
Declaration = ' ',
Delete = ' ',
DoStatement = ' ',
Enum = ' ',
EnumMember = ' ',
Event = ' ',
Field = ' ',
File = ' ',
Folder = ' ',
ForStatement = ' ',
Function = ' ',
Identifier = ' ',
IfStatement = ' ',
Interface = ' ',
Keyword = ' ',
List = ' ',
Log = ' ',
Lsp = ' ',
Macro = ' ',
MarkdownH1 = ' ',
MarkdownH2 = ' ',
MarkdownH3 = ' ',
MarkdownH4 = ' ',
MarkdownH5 = ' ',
MarkdownH6 = ' ',
Method = ' ',
Module = ' ',
Namespace = ' ',
Null = ' ',
Number = ' ',
Object = ' ',
Operator = ' ',
Package = ' ',
Property = ' ',
Reference = ' ',
Regex = ' ',
Repeat = ' ',
Scope = ' ',
Snippet = ' ',
Specifier = ' ',
Statement = ' ',
String = ' ',
Struct = ' ',
SwitchStatement = ' ',
Text = ' ',
Type = ' ',
TypeParameter = ' ',
Unit = ' ',
Value = ' ',
Variable = ' ',
WhileStatement = ' ',
},
},
ui = {
bar = {
separator = ' ',
extends = '…',
},
menu = {
separator = ' ',
indicator = ' ',
},
},
},
bar = {
---@type dropbar_source_t[]|fun(buf: integer, win: integer): dropbar_source_t[]
sources = function(_, _)
local sources = require('dropbar.sources')
return {
sources.path,
{
get_symbols = function(buf, cursor)
if vim.bo[buf].ft == 'markdown' then
return sources.markdown.get_symbols(buf, cursor)
end
for _, source in ipairs({
sources.lsp,
sources.treesitter,
}) do
local symbols = source.get_symbols(buf, cursor)
if not vim.tbl_isempty(symbols) then
return symbols
end
end
return {}
end,
},
}
end,
padding = {
left = 1,
right = 1,
},
pick = {
pivots = 'abcdefghijklmnopqrstuvwxyz',
},
truncate = true,
},
menu = {
entry = {
padding = {
left = 1,
right = 1,
},
},
---@type table<string, string|function|table<string, string|function>>
keymaps = {
['<LeftMouse>'] = function()
local api = require('dropbar.api')
local menu = api.get_current_dropbar_menu()
if not menu then
return
end
local mouse = vim.fn.getmousepos()
if mouse.winid ~= menu.win then
local parent_menu = api.get_dropbar_menu(mouse.winid)
if parent_menu and parent_menu.sub_menu then
parent_menu.sub_menu:close()
end
if vim.api.nvim_win_is_valid(mouse.winid) then
vim.api.nvim_set_current_win(mouse.winid)
end
return
end
menu:click_at({ mouse.line, mouse.column }, nil, 1, 'l')
end,
['<CR>'] = function()
local menu = require('dropbar.api').get_current_dropbar_menu()
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,
},
---@alias dropbar_menu_win_config_opts_t any|fun(menu: dropbar_menu_t):any
---@type table<string, dropbar_menu_win_config_opts_t>
---@see vim.api.nvim_open_win
win_configs = {
border = 'none',
style = 'minimal',
row = function(menu)
return menu.parent_menu
and menu.parent_menu.clicked_at
and menu.parent_menu.clicked_at[1] - vim.fn.line('w0')
or 1
end,
col = function(menu)
return menu.parent_menu and menu.parent_menu._win_configs.width or 0
end,
relative = function(menu)
return menu.parent_menu and 'win' or 'mouse'
end,
win = function(menu)
return menu.parent_menu and menu.parent_menu.win
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,
},
},
sources = {
path = {
---@type string|fun(buf: integer): string
relative_to = function(_)
return vim.fn.getcwd()
end,
---Can be used to filter out files or directories
---based on their name
---@type fun(name: string): boolean
filter = function(_)
return true
end,
},
treesitter = {
-- Lua pattern used to extract a short name from the node text
-- Be aware that the match result must not be nil!
name_pattern = string.rep('[#~%w%._%->!]*', 4, '%s*'),
-- The order matters! The first match is used as the type
-- of the treesitter symbol and used to show the icon
-- Types listed below must have corresponding icons
-- in the `icons.kinds.symbols` table for the icon to be shown
valid_types = {
'array',
'boolean',
'break_statement',
'call',
'case_statement',
'class',
'constant',
'constructor',
'continue_statement',
'delete',
'do_statement',
'enum',
'enum_member',
'event',
'for_statement',
'function',
'if_statement',
'interface',
'keyword',
'list',
'macro',
'method',
'module',
'namespace',
'null',
'number',
'operator',
'package',
'property',
'reference',
'repeat',
'scope',
'specifier',
'string',
'struct',
'switch_statement',
'type',
'type_parameter',
'unit',
'value',
'variable',
'while_statement',
'declaration',
'field',
'identifier',
'object',
'statement',
'text',
},
},
lsp = {
request = {
-- Times to retry a request before giving up
ttl_init = 60,
interval = 1000, -- in ms
},
},
markdown = {
parse = {
-- Number of lines to update when cursor moves out of the parsed range
look_ahead = 200,
},
},
},
}
These options live under opts.general
and are used to configure the
general behavior of the plugin:
opts.general.enable
:boolean|fun(buf: integer, win: integer): boolean
- Controls whether to enable the plugin for the current buffer and window
- If a function is provided, it will be called with the current bufnr and winid and should return a boolean
- Default:
function(buf, win) return not vim.api.nvim_win_get_config(win).zindex and vim.bo[buf].buftype == '' and vim.api.nvim_buf_get_name(buf) ~= '' and not vim.wo[win].diff end
opts.general.update_events
:string[]
- List of events that should trigger an update of the dropbar
- Default:
{ 'CursorMoved', 'CursorMovedI', 'DirChanged', 'FileChangedShellPost', 'TextChanged', 'TextChangedI', 'VimResized', 'WinResized', 'WinScrolled', }
These options live under opts.icons
and are used to configure the icons
used by the plugin:
opts.icons.kinds.use_devicons
:boolean
- Whether to use nvim-web-devicons to show icons for different filetypes
- Default:
true
opts.icons.kinds.symbols
:table<string, string>
- Table mapping the different kinds of symbols to their corresponding icons
- Default:
{ Array = ' ', Boolean = ' ', BreakStatement = ' ', Call = ' ', CaseStatement = ' ', Class = ' ', Color = ' ', Constant = ' ', Constructor = ' ', ContinueStatement = '→ ', Copilot = ' ', Declaration = ' ', Delete = ' ', DoStatement = ' ', Enum = ' ', EnumMember = ' ', Event = ' ', Field = ' ', File = ' ', Folder = ' ', ForStatement = ' ', Function = ' ', Identifier = ' ', IfStatement = ' ', Interface = ' ', Keyword = ' ', List = ' ', Log = ' ', Lsp = ' ', Macro = ' ', MarkdownH1 = ' ', MarkdownH2 = ' ', MarkdownH3 = ' ', MarkdownH4 = ' ', MarkdownH5 = ' ', MarkdownH6 = ' ', Method = ' ', Module = ' ', Namespace = ' ', Null = ' ', Number = ' ', Object = ' ', Operator = ' ', Package = ' ', Property = ' ', Reference = ' ', Regex = ' ', Repeat = ' ', Scope = ' ', Snippet = ' ', Specifier = ' ', Statement = ' ', String = ' ', Struct = ' ', SwitchStatement = ' ', Terminal = ' ', Text = ' ', Type = ' ', TypeParameter = ' ', Unit = ' ', Value = ' ', Variable = ' ', WhileStatement = ' ', }
opts.icons.ui.bar
:table<string, string>
- Controls the icons used in the winbar UI
- Default:
{ separator = ' ', extends = '…', }
opts.icons.ui.menu
:table<string, string>
- Controls the icons used in the menu UI
- Default:
{ separator = ' ', indicator = ' ', }
These options live under opts.bar
and are used to control the behavior of the
winbar:
opts.bar.sources
:dropbar_source_t[]|fun(buf: integer, win: integer): dropbar_source_t[]
- List of sources to show in the winbar
- If a function is provided, it will be called with the current bufnr and winid and should return a list of sources
- Default:
function(_, _) local sources = require('dropbar.sources') return { sources.path, { get_symbols = function(buf, cursor) if vim.bo[buf].ft == 'markdown' then return sources.markdown.get_symbols(buf, cursor) end for _, source in ipairs({ sources.lsp, sources.treesitter, }) do local symbols = source.get_symbols(buf, cursor) if not vim.tbl_isempty(symbols) then return symbols end end return {} end, }, } end
- Notice that in the default config we register the second source as an
aggregation of LSP, treesitter, and markdown sources, so that we dynamically
choose the best source for the current buffer or window.
For more information about sources, see
dropbar_source_t
.
opts.bar.padding
:{ left: number, right: number }
- Padding to use between the winbar and the window border
- Default:
{ left = 1, right = 1 }
opts.bar.pick.pivots
:string
- Pivots to use in pick mode
- Default:
'abcdefghijklmnopqrstuvwxyz'
opts.bar.truncate
:boolean
- Whether to truncate the winbar if it doesn't fit in the window
- Default:
true
These options live under opts.menu
and are used to control the behavior of the
menu:
opts.menu.entry.padding
:{ left: number, right: number }
- Padding to use between the menu entry and the menu border
- Default:
{ left = 1, right = 1 }
opts.menu.keymaps
:table<string, function|string|table<string, function>|table<string, string>>
- Buffer-local keymaps in the menu
- Use
<key> = <function|string>
to map a key in normal mode and visual mode in the menu buffer, or use<key> = table<mode, function|string>
to map a key in specific modes. - Default:
{ ['<LeftMouse>'] = function() local api = require('dropbar.api') local menu = api.get_current_dropbar_menu() if not menu then return end local mouse = vim.fn.getmousepos() if mouse.winid ~= menu.win then local parent_menu = api.get_dropbar_menu(mouse.winid) if parent_menu and parent_menu.sub_menu then parent_menu.sub_menu:close() end if vim.api.nvim_win_is_valid(mouse.winid) then vim.api.nvim_set_current_win(mouse.winid) end return end menu:click_at({ mouse.line, mouse.column }, nil, 1, 'l') end, ['<CR>'] = function() local menu = require('dropbar.api').get_current_dropbar_menu() 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, }
opts.menu.win_configs
:table<string, dropbar_menu_win_config_opts_t>
- Window configurations for the menu, see
:h nvim_open_win()
- Each config key in
opts.menu.win_configs
accepts either a plain value which will be passes directly tonvim_open_win()
, or a function that takes the current menu (seedropbar_menu_t
) as an argument and returns a value to be passed tonvim_open_win()
. - Default:
{ border = 'none', style = 'minimal', row = function(menu) return menu.parent_menu and menu.parent_menu.clicked_at and menu.parent_menu.clicked_at[1] - vim.fn.line('w0') or 1 end, col = function(menu) return menu.parent_menu and menu.parent_menu._win_configs.width or 0 end, relative = function(menu) return menu.parent_menu and 'win' or 'mouse' end, win = function(menu) return menu.parent_menu and menu.parent_menu.win 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, }
- Window configurations for the menu, see
These options live under opts.sources
and are used to control the behavior of
each sources.
opts.sources.path.relative_to
:string|fun(buf: integer): string
- The path to use as the root of the relative path
- If a function is provided, it will be called with the current buffer number as an argument and should return a string to use as the root of the relative path
- Notice: currently does not support
..
relative paths - Default:
function(_) return vim.fn.getcwd() end
opts.sources.path.filter
:function(name: string): boolean
- A function that takes a file name and returns whether to include it in the results shown in the drop-down menu
- Default:
function(_) return true end
opts.sources.treesitter.name_pattern
:string
- Lua pattern used to extract a short name from the node text
- Be aware! The matching result must not be nil
- Default:
string.rep('[#~%w%._%->!]*', 4, '%s*')
opts.sources.treesitter.valid_types:
string[]
- A list of treesitter node types to include in the results
- Default:
{ 'array', 'boolean', 'break_statement', 'call', 'case_statement', 'class', 'constant', 'constructor', 'continue_statement', 'delete', 'do_statement', 'enum', 'enum_member', 'event', 'for_statement', 'function', 'if_statement', 'interface', 'keyword', 'list', 'macro', 'method', 'module', 'namespace', 'null', 'number', 'operator', 'package', 'property', 'reference', 'repeat', 'scope', 'specifier', 'string', 'struct', 'switch_statement', 'type', 'type_parameter', 'unit', 'value', 'variable', 'while_statement', 'declaration', 'field', 'identifier', 'object', 'statement', 'text', }
opts.sources.lsp.request.ttl_init
:number
- Number of times to retry a request before giving up
- Default:
60
opts.sources.lsp.request.interval
:number
- Number of milliseconds to wait between retries
- Default:
1000
opts.sources.markdown.parse.look_ahead
:number
- Number of lines to update when cursor moves out of the parsed range
- Default:
200
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?
- Get the dropbar associated with the given buffer and window
- For more information about the
dropbar_t
type, seedropbar_t
get_current_dropbar(): dropbar_t?
- Get the dropbar associated with the current buffer and window
get_dropbar_menu(win: integer): dropbar_menu_t?
- Get the drop-down menu associated with the given window
- For more information about the
dropbar_menu_t
type, seedropbar_menu_t
get_current_dropbr_menu(): dropbar_menu_t?
- Get the drop-down menu associated with the current window
goto_context_start(count: integer?)
- Move the cursor to the start of the current context
- If
count
is 0 ornil
, go to the start of current context, or the start at previous context if cursor is already at the start of current context - If
count
is positive, goto the start ofcount
previous context
select_next_context()
- Open the menu of current context to select the next context
pick(idx: integer?)
- Pick a component from current winbar
- If
idx
isnil
, enter interactive pick mode to select a component - If
idx
is a number, directly pick the component at that index if it exists
dropbar.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 groups
Highlight group | Attributes |
---|---|
DropBarIconKindArray | { link = 'Array' } |
DropBarIconKindBoolean | { link = 'Boolean' } |
DropBarIconKindBreakStatement | { link = 'Error' } |
DropBarIconKindCall | { link = 'Function' } |
DropBarIconKindCaseStatement | { link = 'Conditional' } |
DropBarIconKindClass | { link = 'CmpItemKindClass' } |
DropBarIconKindConstant | { link = 'Constant' } |
DropBarIconKindConstructor | { link = 'CmpItemKindConstructor' } |
DropBarIconKindContinueStatement | { link = 'Repeat' } |
DropBarIconKindDeclaration | { link = 'CmpItemKindSnippet' } |
DropBarIconKindDelete | { link = 'Error' } |
DropBarIconKindDoStatement | { link = 'Repeat' } |
DropBarIconKindElseStatement | { link = 'Conditional' } |
DropBarIconKindEnum | { link = 'CmpItemKindEnum' } |
DropBarIconKindEnumMember | { link = 'CmpItemKindEnumMember' } |
DropBarIconKindEvent | { link = 'CmpItemKindEvent' } |
DropBarIconKindField | { link = 'CmpItemKindField' } |
DropBarIconKindFile | { link = 'NormalFloat' } |
DropBarIconKindFolder | { link = 'Directory' } |
DropBarIconKindForStatement | { link = 'Repeat' } |
DropBarIconKindFunction | { link = 'Function' } |
DropBarIconKindIdentifier | { link = 'CmpItemKindVariable' } |
DropBarIconKindIfStatement | { link = 'Conditional' } |
DropBarIconKindInterface | { link = 'CmpItemKindInterface' } |
DropBarIconKindKeyword | { link = 'Keyword' } |
DropBarIconKindList | { link = 'SpecialChar' } |
DropBarIconKindMacro | { link = 'Macro' } |
DropBarIconKindMarkdownH1 | { link = 'markdownH1' } |
DropBarIconKindMarkdownH2 | { link = 'markdownH2' } |
DropBarIconKindMarkdownH3 | { link = 'markdownH3' } |
DropBarIconKindMarkdownH4 | { link = 'markdownH4' } |
DropBarIconKindMarkdownH5 | { link = 'markdownH5' } |
DropBarIconKindMarkdownH6 | { link = 'markdownH6' } |
DropBarIconKindMethod | { link = 'CmpItemKindMethod' } |
DropBarIconKindModule | { link = 'CmpItemKindModule' } |
DropBarIconKindNamespace | { link = 'NameSpace' } |
DropBarIconKindNull | { link = 'Constant' } |
DropBarIconKindNumber | { link = 'Number' } |
DropBarIconKindObject | { link = 'Statement' } |
DropBarIconKindOperator | { link = 'Operator' } |
DropBarIconKindPackage | { link = 'CmpItemKindModule' } |
DropBarIconKindProperty | { link = 'CmpItemKindProperty' } |
DropBarIconKindReference | { link = 'CmpItemKindReference' } |
DropBarIconKindRepeat | { link = 'Repeat' } |
DropBarIconKindScope | { link = 'NameSpace' } |
DropBarIconKindSpecifier | { link = 'Specifier' } |
DropBarIconKindStatement | { link = 'Statement' } |
DropBarIconKindString | { link = 'String' } |
DropBarIconKindStruct | { link = 'CmpItemKindStruct' } |
DropBarIconKindSwitchStatement | { link = 'Conditional' } |
DropBarIconKindType | { link = 'CmpItemKindClass' } |
DropBarIconKindTypeParameter | { link = 'CmpItemKindTypeParameter' } |
DropBarIconKindUnit | { link = 'CmpItemKindUnit' } |
DropBarIconKindValue | { link = 'Number' } |
DropBarIconKindVariable | { link = 'CmpItemKindVariable' } |
DropBarIconKindWhileStatement | { link = 'Repeat' } |
DropBarIconUIIndicator | { link = 'SpecialChar' } |
DropBarIconUIPickPivot | { link = 'Error' } |
DropBarIconUISeparator | { link = 'SpecialChar' } |
DropBarIconUISeparatorMenu | { link = 'DropBarIconUISeparator' } |
DropBarMenuCurrentContext | { link = 'PmenuSel' } |
┌──────────────────┐
│winbar at win 1000│ {k}th symbol clicked
│ contaning buf 1 ├──────────────────────┐
└───────┬─▲────────┘ │
▼ │ │
_G.dropbar.get_dropbar_str() │
│ ▲ │
┌──────────────┐ ┌──────▼─┴──────┐ │
│sources │ │_G.dropbar.bars│ │
│ ┌───┐ │ └──────┬─▲──────┘ │
│ │lsp│ │ ┌───────┬──▼─┴──┬───────┐ │
│ └───┘ │ ┌─▼─┐ ┌─┴─┐ ┌─┴─┐ ... │
│ ┌──────────┐ │ │[1]│ │[2]│ │[3]│ │
│ │treesitter│ │ └─┬─┘ └─┬─┘ └─┬─┘ │
│ └──────────┘ │ │ ... ... │
│ ... │ └──┬─▲─────────────┬──────┐ │
└─────┬─▲──────┘ ┌─▼─┴──┐ ┌──┴───┐ ... │
│ │ │[1000]│ │[1015]│ │
│ │ └─┬─▲──┘ └──────┘ │
│ │ __tostring() │ │ return string cache │
│ │ ┌───▼─┴───┐ ┌──────────────▼──────────────┐
│ │ │dropbar_t├────────────────────▶_G.dropbar.on_click_callbacks│
│ │ On update events └───┬─▲───┘ register symbol └──────────────┬──────────────┘
│ │ get_symbols(1, <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
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.on_click_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 |
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 components dropbar_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 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:__tostring(): string |
meta method to convert dropbar_t to its string representation |
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 |
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 |
symbol |
dropbar_symbol_tree_t? |
the dropbar tree symboldropbar_symbol_tree_t associated with the dropbar symbol |
data |
table? |
any extra data associated with the symbol |
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 |
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 |
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: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:goto_start() |
moves the cursor to the start of the range of the dropbar tree symboldropbar_symbol_tree_t associated with the dropbar symbol |
dropbar_symbol_t:swap_field(field: string, new_val: any) |
temporarily change the content of a dropbar symbol *does not support replacing nil values |
dropbar_symbol_t:restore() |
restore the content of a dropbar symbol after dropbar_symbol_t:swap_field() is called *does not support restoring nil values |
Declared in lua/dropbar/sources/utils.lua
.
dropbar_symbol_tree_t
is a class that represents a tree structure in a dropbar.
The main purpose of this class is to provide a common structure for symbols got
from different sources, so that it is more convenient to convert them into
dropbar_symbol_t
that supports opening a drop-down menu
on click.
┌─────────┐
│file path├────┐
└─────────┘ │
┌──────┐ │
│TSNode├──┐ │
└──────┘ │ │
┌─────────────────────┐ │ │ ┌─────────────────────┐ ┌────────────────┐
│lsp_document_symbol_t├──┼─┼──▶dropbar_symbol_tree_t├───▶dropbar_symbol_t│
└─────────────────────┘ │ │ └──────────┬──────────┘ └────────────────┘
┌────────────────────────┐ │ │ │
│lsp_symbol_information_t├──┘ │ provides a unified interface
└────────────────────────┘ │ to get the children, siblings,
┌─────────────────────────┐ │ range, or other information
│markdown_heading_symbol_t├────┘ used to generate a drop-down menu
└─────────────────────────┘ on click
A dropbar_symbol_tree_t
instance should have the following fields:
Field | Type | Description |
---|---|---|
name |
string |
name of the symbol |
kind |
string |
kind/type of the symbol, used to determine the icon used for the symbol when it is converted to dropbar_symbol_t |
children |
dropbar_symbol_tree_t[]? |
children of the symbol |
siblings |
dropbar_symbol_tree_t[]? |
siblings of the symbol |
idx |
integer? |
indtex of the symbol in its siblings |
range |
dropbar_symbol_range_t? |
range of the symbol |
data |
table? |
any extra data associated with the tree symbol |
where dropbar_symbol_range_t
is a table with the following fields:
Field | Type | Description |
---|---|---|
start |
{ line: integer, character: integer } |
start of the range, 0-indexed |
end |
{ line: integer, character: integer } |
end of the range, 0-indexed |
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() |
sub_menu |
dropbar_menu_t? |
submenu, assigned when calling new() or automatically determined when a new menu opens |
parent_menu |
dropbar_menu_t? |
parent menu, assigned when calling new() or automatically determined in open() |
clicked_at |
integer[]? |
last position where the menu was clicked |
dropbar_menu_t
has the following methods:
Method | Description |
---|---|
dropbar_menu_t:new(opts: dropbar_menu_opts_t?): dropbar_menu_t |
constructor of dropbar_menu_t |
dropbar_menu_t:del() |
destructor of dropbar_menu_t |
dropbar_menu_t:eval_win_config() |
evaluate window configuration and store the result in _win_configs |
dropbar_menu_t:get_component_at(pos: integer[]): dropbar_symbol_t |
get the componentdropbar_symbol_t at position pos |
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:hl_line_range(line: integer, hl_info: dropbar_menu_hl_info_t) |
add highlight to a range in the menu buffer according to the line number and the highlight infodropbar_menu_hl_info_t |
dropbar_menu_t:hl_line_single(line: integer, hlgroup: string?) |
add highlight to a single line in the menu buffer; hlgroups defaults to 'DropBarMenuCurrentContext' *all other highlights added by this functions before will be cleared |
dropbar_menu_t:make_buf() |
create the menu buffer from the entriesdropbar_menu_entry_t |
dropbar_menu_t:open() |
open the menu |
dropbar_menu_t:close() |
close the menu |
dropbar_menu_t:toggle() |
toggle the menu |
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 |
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? |
get the first clickable componentdropbar_symbol_t in the dropbar menu entry starting from offset , which defaults to 0 |
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 |
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, cursor: integer[]): dropbar_symbol_t[] |
returns the symbolsdropbar_symbol_t[] to show in the winbar given buffer number buf and cursor position cursor |
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 a buffer number and a
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 table with its
field get_symbols
set to a function that gets symbols from either the
markdown, LSP, or treesitter sources to achieve fall-back behavior.
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'
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,
},
},
})