fix(highlight-hover): Set cursor in parent if child is folded
Hopefully this commit fixes #1. - Improved algorithm that finds the items to set hover-highlight - Now we can set cursor on the nearest parent if the leaf node is folded in the outline. - Set cursor column appropriate depending on whether lineno is enabled. API: - parser.preorder_iter now supports an optional argument as function, which determines whether to explore children. By default, folded parents will not explore children. This can now be overridden. Behaviour: - If you fold a nested node and hit <C-g>, you can go back to the nearest parent - The column that cursor goes to is no longer arbitrarily chosen It appears that simrat or whoever wrote this code thought the column was 1-indexed, however it is 0-indexed, so the old code was always putting the cursor on the 2nd column. Now, we put it in the first column. If lineno is enabled, we set the cursor the be at the column of lineno padding, this makes both the lineno and the markers visible. Unfortunately the so-called 'improved' algorithm for _highlight_current_item is still not the best. The most optimal would be O(n). However, to make sure we stop refactoring now that it works OK and can already fix an issue, I will leave this to posterity. Tested to work (for me).
This commit is contained in:
@@ -280,11 +280,21 @@ function M._highlight_current_item(winnr)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local win = winnr or vim.api.nvim_get_current_win()
|
-- TODO: Find an efficient way to:
|
||||||
local hovered_line = vim.api.nvim_win_get_cursor(win)[1] - 1
|
-- 1) Set highlight for all nodes in range (regardless of visibility)
|
||||||
local leaf_node = nil
|
-- 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
|
value.hovered = nil
|
||||||
|
|
||||||
if
|
if
|
||||||
@@ -292,18 +302,35 @@ function M._highlight_current_item(winnr)
|
|||||||
or (hovered_line > value.range_start and hovered_line < value.range_end)
|
or (hovered_line > value.range_start and hovered_line < value.range_end)
|
||||||
then
|
then
|
||||||
value.hovered = true
|
value.hovered = true
|
||||||
leaf_node = value
|
table.insert(parent_nodes, value)
|
||||||
end
|
end
|
||||||
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()
|
_update_lines()
|
||||||
|
|
||||||
if leaf_node then
|
-- Put cursor on deepest visible match
|
||||||
for index, node in ipairs(M.state.flattened_outline_items) do
|
local col = 0
|
||||||
if node == leaf_node then
|
if cfg.o.outline_items.show_symbol_lineno then
|
||||||
vim.api.nvim_win_set_cursor(M.view.winnr, { index, 1 })
|
-- 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
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -99,11 +99,19 @@ end
|
|||||||
---Iterator that traverses the tree parent first before children, returning each node.
|
---Iterator that traverses the tree parent first before children, returning each node.
|
||||||
-- Essentailly 'flatten' items, but returns an iterator.
|
-- Essentailly 'flatten' items, but returns an iterator.
|
||||||
---@param items outline.SymbolNode[] Tree of symbols parsed by parse_result
|
---@param items outline.SymbolNode[] Tree of symbols parsed by parse_result
|
||||||
function M.preorder_iter(items)
|
---@param children_check function? Takes a node and return whether the children should be explored.
|
||||||
local node = { children = items, traversal_child = 1, depth = 1, folded = false }
|
---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 prev
|
||||||
local visited = {}
|
local visited = {}
|
||||||
|
|
||||||
|
if children_check == nil then
|
||||||
|
children_check = function(n)
|
||||||
|
return not folding.is_folded(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
while node do
|
while node do
|
||||||
if node.name and not visited[node] then
|
if node.name and not visited[node] then
|
||||||
@@ -113,7 +121,7 @@ function M.preorder_iter(items)
|
|||||||
|
|
||||||
if
|
if
|
||||||
node.children and node.traversal_child <= #node.children
|
node.children and node.traversal_child <= #node.children
|
||||||
and not folding.is_folded(node)
|
and (node.is_root or children_check(node))
|
||||||
then
|
then
|
||||||
prev = node
|
prev = node
|
||||||
if node.children[node.traversal_child] then
|
if node.children[node.traversal_child] then
|
||||||
|
|||||||
@@ -100,11 +100,11 @@ end
|
|||||||
-- Handles highlights, virtual text, and of course lines of outline to write
|
-- 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 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
|
---@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
|
---@param codewin integer code window
|
||||||
function M.make_outline(bufnr, items, codewin)
|
function M.make_outline(bufnr, items, codewin)
|
||||||
if not M.is_buffer_outline(bufnr) then
|
if not M.is_buffer_outline(bufnr) then
|
||||||
return
|
return {}
|
||||||
end
|
end
|
||||||
local codebuf = vim.api.nvim_win_get_buf(codewin)
|
local codebuf = vim.api.nvim_win_get_buf(codewin)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user