diff --git a/lua/outline/init.lua b/lua/outline/init.lua index 59526e5..ab94d1e 100644 --- a/lua/outline/init.lua +++ b/lua/outline/init.lua @@ -280,11 +280,21 @@ function M._highlight_current_item(winnr) return end - local win = winnr or vim.api.nvim_get_current_win() - local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1 - local leaf_node = nil + -- TODO: Find an efficient way to: + -- 1) Set highlight for all nodes in range (regardless of visibility) + -- 2) Find the line number of the deepest node in range, that is visible (no + -- parents folded) + -- In one go - local cb = function(value) + local win = winnr or vim.api.nvim_get_current_win() + local buf = vim.api.nvim_win_get_buf(win) + + local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1 + local parent_nodes = {} + + -- Must not skip folded nodes so that when user unfolds a parent, they can see the leaf + -- node highlighted. + for value in parser.preorder_iter(M.state.outline_items, function() return true end) do value.hovered = nil if @@ -292,18 +302,35 @@ function M._highlight_current_item(winnr) or (hovered_line > value.range_start and hovered_line < value.range_end) then value.hovered = true - leaf_node = value + table.insert(parent_nodes, value) end end - utils.items_dfs(cb, M.state.outline_items) + if #parent_nodes == 0 then + return + end + -- Probably can't 'just' writer.add_hover_highlights here because we might + -- want to auto_unfold_hover _update_lines() - if leaf_node then - for index, node in ipairs(M.state.flattened_outline_items) do - if node == leaf_node then - vim.api.nvim_win_set_cursor(M.view.winnr, { index, 1 }) + -- Put cursor on deepest visible match + local col = 0 + if cfg.o.outline_items.show_symbol_lineno then + -- Padding area between lineno column and start of guides + col = #tostring(vim.api.nvim_buf_line_count(buf) - 1) + end + local flats = M.state.flattened_outline_items + local found = false + local find_node + + while #parent_nodes > 0 and not found do + find_node = table.remove(parent_nodes, #parent_nodes) + -- TODO: Is it feasible to use binary search here? + for line, node in ipairs(flats) do + if node == find_node then + vim.api.nvim_win_set_cursor(M.view.winnr, { line, col }) + found = true break end end diff --git a/lua/outline/parser.lua b/lua/outline/parser.lua index 2139f16..c3dc490 100644 --- a/lua/outline/parser.lua +++ b/lua/outline/parser.lua @@ -99,11 +99,19 @@ end ---Iterator that traverses the tree parent first before children, returning each node. -- Essentailly 'flatten' items, but returns an iterator. ---@param items outline.SymbolNode[] Tree of symbols parsed by parse_result -function M.preorder_iter(items) - local node = { children = items, traversal_child = 1, depth = 1, folded = false } +---@param children_check function? Takes a node and return whether the children should be explored. +---Note that the root node (param items) is always explored regardless of children_check. +function M.preorder_iter(items, children_check) + local node = { children = items, traversal_child = 1, depth = 1, is_root = true } local prev local visited = {} + if children_check == nil then + children_check = function(n) + return not folding.is_folded(n) + end + end + return function() while node do if node.name and not visited[node] then @@ -113,7 +121,7 @@ function M.preorder_iter(items) if node.children and node.traversal_child <= #node.children - and not folding.is_folded(node) + and (node.is_root or children_check(node)) then prev = node if node.children[node.traversal_child] then diff --git a/lua/outline/writer.lua b/lua/outline/writer.lua index 2ca5f7d..dd0dbf4 100644 --- a/lua/outline/writer.lua +++ b/lua/outline/writer.lua @@ -100,11 +100,11 @@ end -- Handles highlights, virtual text, and of course lines of outline to write ---@param bufnr integer Nothing is done if is_buffer_outline(bufnr) is not true ---@param items outline.SymbolNode[] Tree of symbols after being parsed by parser.parse_result ----@return outline.FlatSymbolNode[]? flattened_items No return value if bufnr is invalid +---@return outline.FlatSymbolNode[] flattened_items Empty table returned if bufnr is invalid ---@param codewin integer code window function M.make_outline(bufnr, items, codewin) if not M.is_buffer_outline(bufnr) then - return + return {} end local codebuf = vim.api.nvim_win_get_buf(codewin)