Skip to content

Gee19/dropbar.nvim

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dropbar.nvim

IDE-like breadcrumbs, out of the box

A polished, IDE-like, highly-customizable winbar for Neovim
with drop-down menu support and mutiple backends

Features

dropbar-demo-light.mp4
  • Opening drop-down menus or go to definition with a single mouse click

    mouse-click

  • Pick mode for quickly selecting a component in the winbar with shortcuts

    pick-mode

  • Automatically truncating long components

    auto-truncate

  • 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

Requirements

  • 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

Installation

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

Usage

  • 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 or require('dropbar.api').pick(<idx>) to directly select a component at idx.
    • Inside interactive pick mode, press the corresponding pivot shown before each component to select it
  • Default keymaps in drop-down menu
    • <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 callback
    • To disable, remap or add new keymaps in the drop-down menu, see menu options

Configuration

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

General

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

Icons

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

Bar

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

Menu

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 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().
    • 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,
      }

Sources

These options live under opts.sources and are used to control the behavior of each sources.

Path
  • 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
Treesitter
  • 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',
      }
LSP
  • 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
Markdown
  • opts.sources.markdown.parse.look_ahead: number
    • Number of lines to update when cursor moves out of the parsed range
    • Default: 200

API

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, see dropbar_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, see dropbar_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 or nil, 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 of count 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 is nil, enter interactive pick mode to select a component
    • If idx is a number, directly pick the component at that index if it exists

Highlighting

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

Developers

Architecture

                                    ┌──────────────────┐
                                    │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

Classes

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.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 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 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 idxth component directly
dropbar_t:__tostring(): 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
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

dropbar_symbol_tree_t

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

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()
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 symboldropbar_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

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

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, cursor: integer[]): dropbar_symbol_t[] returns the symbolsdropbar_symbol_t[] to show in the winbar given buffer number buf and cursor position cursor

Making a New Source

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

Similar Projects

About

IDE-like breadcrumbs, out of the box

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Lua 99.8%
  • Makefile 0.2%