Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e38d782
feat(treesitter): drop unnamed nodes
disrupted Feb 22, 2026
711ff36
feat(treesitter): is node name like identifier
disrupted Feb 22, 2026
a7fd968
feat(treesitter): dedup adjacent symbols
disrupted Feb 22, 2026
f786ac3
feat(treesitter): dedupe based on resolved pos
disrupted Feb 22, 2026
85b8ea1
fix(treesitter): exclude child node that is not within cursor pos
disrupted Feb 22, 2026
3c8398f
refactor(treesitter): exclude anonymous nodes from name resolution
disrupted Feb 22, 2026
2d46c09
feat(treesitter): collapse child if name range fully contained in parent
disrupted Feb 22, 2026
9475fc1
test(treesitter): create tests
disrupted Feb 22, 2026
e59c3a1
fix(treesitter): prefer narrower cross-line contained symbol
disrupted Feb 23, 2026
bac7f9f
style: format
disrupted Feb 23, 2026
4d664e3
refactor: use `vim.treesitter`
disrupted Feb 23, 2026
9cb766f
refactor: extract shared symbol resolver
disrupted Feb 23, 2026
f039633
perf: cache symbols per run
disrupted Feb 23, 2026
b42ce90
refactor: reduce redundant containment checks
disrupted Feb 23, 2026
82b845d
refactor(treesitter): merge `extract_short_name` and `get_short_name_…
Bekaboo Feb 28, 2026
d0224e8
refactor(treesitter): extract and rename `has_anonymous_only_children`
Bekaboo Feb 28, 2026
3b21f49
refactor(treesitter): define class `dropbar_ts_cache_t`
Bekaboo Feb 28, 2026
a394a5c
refactor(treesitter): use `false` for invalid node cache val
Bekaboo Feb 28, 2026
f6c743c
refactor(treesitter): extract `utils.range`
Bekaboo Mar 1, 2026
d3538e3
refactor(treesitter): rename param of `should_dedupe_adjacent`
Bekaboo Mar 1, 2026
b971653
refactor(treesitter): rename `dedupe` -> `dedup`
Bekaboo Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lua/dropbar/bar.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ local function str_sanitize(str)
return str and vim.gsplit(str, '\n')()
end

---@alias dropbar_symbol_range_t lsp_range_t
---@alias dropbar_symbol_range_t dropbar_range_t

---Symbol in dropbar, basic element of `dropbar_t` and
---`dropbar_menu_entry_t`
Expand Down
106 changes: 26 additions & 80 deletions lua/dropbar/sources/lsp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ local utils = require('dropbar.utils')
local groupid = vim.api.nvim_create_augroup('dropbar.sources.lsp', {})
local initialized = false

---@type table<integer, lsp_document_symbol_t[]>
---@type table<integer, dropbar_lsp_document_symbol_t[]>
local lsp_buf_symbols = {}
setmetatable(lsp_buf_symbols, {
__index = function(_, k)
Expand All @@ -13,40 +13,36 @@ setmetatable(lsp_buf_symbols, {
end,
})

---@alias lsp_client_t table
---@alias dropbar_lsp_client_t table

---@class lsp_range_t
---@field start {line: integer, character: integer}
---@field end {line: integer, character: integer}

---@class lsp_location_t
---@class dropbar_lsp_location_t
---@field uri string
---@field range lsp_range_t
---@field range dropbar_range_t

---@class lsp_document_symbol_t
---@class dropbar_lsp_document_symbol_t
---@field name string
---@field kind integer
---@field tags? table
---@field deprecated? boolean
---@field detail? string
---@field range? lsp_range_t
---@field selectionRange? lsp_range_t
---@field children? lsp_document_symbol_t[]
---@field range? dropbar_range_t
---@field selectionRange? dropbar_range_t
---@field children? dropbar_lsp_document_symbol_t[]

---@class lsp_symbol_information_t
---@class dropbar_lsp_symbol_information_t
---@field name string
---@field kind integer
---@field tags? table
---@field deprecated? boolean
---@field location? lsp_location_t
---@field location? dropbar_lsp_location_t
---@field containerName? string

---@class lsp_symbol_information_tree_t: lsp_symbol_information_t
---@field parent? lsp_symbol_information_tree_t
---@field children? lsp_symbol_information_tree_t[]
---@field siblings? lsp_symbol_information_tree_t[]
---@class dropbar_lsp_symbol_information_tree_t: dropbar_lsp_symbol_information_t
---@field parent? dropbar_lsp_symbol_information_tree_t
---@field children? dropbar_lsp_symbol_information_tree_t[]
---@field siblings? dropbar_lsp_symbol_information_tree_t[]

---@alias lsp_symbol_t lsp_document_symbol_t|lsp_symbol_information_t
---@alias dropbar_lsp_symbol_t dropbar_lsp_document_symbol_t|dropbar_lsp_symbol_information_t

-- Map symbol number to symbol kind
-- stylua: ignore start
Expand Down Expand Up @@ -87,7 +83,7 @@ local symbol_kind_names = setmetatable({
---@alias lsp_symbol_type_t 'SymbolInformation'|'DocumentSymbol'

---Return type of the symbol table
---@param symbols lsp_symbol_t[] symbol table
---@param symbols dropbar_lsp_symbol_t[] symbol table
---@return lsp_symbol_type_t? type symbol type
local function symbol_type(symbols)
if symbols[1] and symbols[1].location then
Expand All @@ -98,61 +94,11 @@ local function symbol_type(symbols)
end
end

---Check if cursor is in range
---@param cursor integer[] cursor position (line, character); (1, 0)-based
---@param range lsp_range_t 0-based range
---@return boolean
local function cursor_in_range(cursor, range)
local cursor0 = { cursor[1] - 1, cursor[2] }
-- stylua: ignore start
return (
cursor0[1] > range.start.line
or (cursor0[1] == range.start.line
and cursor0[2] >= range.start.character)
)
and (
cursor0[1] < range['end'].line
or (cursor0[1] == range['end'].line
and cursor0[2] <= range['end'].character)
)
-- stylua: ignore end
end

---Check if range1 contains range2
---Strict indexing -- if range1 == range2, return false
---@param range1 lsp_range_t 0-based range
---@param range2 lsp_range_t 0-based range
---@return boolean
local function range_contains(range1, range2)
-- stylua: ignore start
return (
range2.start.line > range1.start.line
or (range2.start.line == range1.start.line
and range2.start.character > range1.start.character)
)
and (
range2.start.line < range1['end'].line
or (range2.start.line == range1['end'].line
and range2.start.character < range1['end'].character)
)
and (
range2['end'].line > range1.start.line
or (range2['end'].line == range1.start.line
and range2['end'].character > range1.start.character)
)
and (
range2['end'].line < range1['end'].line
or (range2['end'].line == range1['end'].line
and range2['end'].character < range1['end'].character)
)
-- stylua: ignore end
end

---Convert LSP DocumentSymbol into winbar symbol
---@param document_symbol lsp_document_symbol_t LSP DocumentSymbol
---@param document_symbol dropbar_lsp_document_symbol_t LSP DocumentSymbol
---@param buf integer buffer number
---@param win integer window number
---@param siblings lsp_document_symbol_t[]? siblings of the symbol
---@param siblings dropbar_lsp_document_symbol_t[]? siblings of the symbol
---@param idx integer? index of the symbol in siblings
---@return dropbar_symbol_t
local function convert_document_symbol(
Expand Down Expand Up @@ -200,7 +146,7 @@ end
---Convert LSP DocumentSymbol[] into a list of dropbar symbols
---Side effect: change dropbar_symbols
---LSP Specification document: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
---@param lsp_symbols lsp_document_symbol_t[]
---@param lsp_symbols dropbar_lsp_document_symbol_t[]
---@param dropbar_symbols dropbar_symbol_t[] (reference to) dropbar symbols
---@param buf integer buffer number
---@param win integer window number
Expand All @@ -219,7 +165,7 @@ local function convert_document_symbol_list(
-- Parse in reverse order so that the symbol with the largest start position
-- is preferred
for idx, symbol in vim.iter(lsp_symbols):enumerate():rev() do
if cursor_in_range(cursor, symbol.range) then
if utils.range.contains_cursor(cursor, symbol.range) then
if
vim.tbl_contains(
configs.opts.sources.lsp.valid_symbols,
Expand All @@ -246,8 +192,8 @@ local function convert_document_symbol_list(
end

---Convert LSP SymbolInformation[] into DocumentSymbol[]
---@param symbols lsp_symbol_t LSP symbols
---@return lsp_document_symbol_t[]
---@param symbols dropbar_lsp_symbol_t LSP symbols
---@return dropbar_lsp_document_symbol_t[]
local function unify(symbols)
if symbol_type(symbols) == 'DocumentSymbol' or vim.tbl_isempty(symbols) then
return symbols
Expand All @@ -262,9 +208,9 @@ local function unify(symbols)
-- symbol can only be a child or a sibling of the previous symbol in the
-- same list
for list_idx, sym in vim.iter(symbols):enumerate():skip(1) do
local prev = symbols[list_idx - 1] --[[@as lsp_symbol_information_tree_t]]
local prev = symbols[list_idx - 1] --[[@as dropbar_lsp_symbol_information_tree_t]]
-- If the symbol is a child of the previous symbol
if range_contains(prev.location.range, sym.location.range) then
if utils.range.contains(prev.location.range, sym.location.range) then
sym.parent = prev
else -- Else the symbol is a sibling of the previous symbol
sym.parent = prev.parent
Expand Down Expand Up @@ -326,8 +272,8 @@ local function update_symbols(buf, ttl)
-- responses can be disordered i.e. later symbols can appear first
lsp_buf_symbols[buf] = unify(symbols)

---@param s1 lsp_document_symbol_t
---@param s2 lsp_document_symbol_t
---@param s1 dropbar_lsp_document_symbol_t
---@param s2 dropbar_lsp_document_symbol_t
---@return boolean precedes true if `s1` appears before `s2`
table.sort(lsp_buf_symbols[buf], function(s1, s2)
local l1, l2, c1, c2 =
Expand Down
Loading
Loading