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).
141 lines
4.5 KiB
Lua
141 lines
4.5 KiB
Lua
local symbols = require 'outline.symbols'
|
|
local ui = require 'outline.ui'
|
|
local cfg = require 'outline.config'
|
|
local t_utils = require 'outline.utils.table'
|
|
local lsp_utils = require 'outline.utils.lsp_utils'
|
|
local folding = require 'outline.folding'
|
|
|
|
local M = {}
|
|
|
|
---@class outline.SymbolNode
|
|
---@field name string
|
|
---@field depth integer
|
|
---@field parent outline.SymbolNode
|
|
---@field deprecated boolean
|
|
---@field kind integer|string
|
|
---@field icon string
|
|
---@field detail string
|
|
---@field line integer
|
|
---@field character integer
|
|
---@field range_start integer
|
|
---@field range_end integer
|
|
---@field isLast boolean
|
|
---@field hierarchy boolean
|
|
---@field children? outline.SymbolNode[]
|
|
---@field traversal_child integer
|
|
|
|
---Parses result from LSP into a reorganized tree of symbols (not flattened,
|
|
-- simply reoganized by merging each property table from the arguments into a
|
|
-- table for each symbol)
|
|
---@param result table The result from a language server.
|
|
---@param depth number? The current depth of the symbol in the hierarchy.
|
|
---@param hierarchy table? A table of booleans which tells if a symbols parent was the last in its group.
|
|
---@param parent table? A reference to the current symbol's parent in the function's recursion
|
|
---@return outline.SymbolNode[]
|
|
local function parse_result(result, depth, hierarchy, parent)
|
|
local ret = {}
|
|
|
|
for index, value in pairs(result) do
|
|
if not cfg.is_symbol_blacklisted(symbols.kinds[value.kind]) then
|
|
-- the hierarchy is basically a table of booleans which
|
|
-- tells whether the parent was the last in its group or
|
|
-- not
|
|
local hir = hierarchy or {}
|
|
-- how many parents this node has, 1 is the lowest value because its
|
|
-- easier to work it
|
|
local level = depth or 1
|
|
-- whether this node is the last (~born~) in its siblings
|
|
local isLast = index == #result
|
|
|
|
local selectionRange = lsp_utils.get_selection_range(value)
|
|
local range = lsp_utils.get_range(value)
|
|
|
|
local node = {
|
|
deprecated = value.deprecated,
|
|
kind = value.kind,
|
|
icon = symbols.icon_from_kind(value.kind),
|
|
name = value.name or value.text,
|
|
detail = value.detail,
|
|
line = selectionRange.start.line,
|
|
character = selectionRange.start.character,
|
|
range_start = range.start.line,
|
|
range_end = range['end'].line,
|
|
depth = level,
|
|
isLast = isLast,
|
|
hierarchy = hir,
|
|
parent = parent,
|
|
traversal_child = 1,
|
|
}
|
|
|
|
table.insert(ret, node)
|
|
|
|
local children = nil
|
|
if value.children ~= nil then
|
|
-- copy by value because we dont want it messing with the hir table
|
|
local child_hir = t_utils.array_copy(hir)
|
|
table.insert(child_hir, isLast)
|
|
children = parse_result(value.children, level + 1, child_hir, node)
|
|
else
|
|
value.children = {}
|
|
end
|
|
|
|
node.children = children
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
---Sorts and reorganizes the response from lsp request
|
|
--'textDocument/documentSymbol', buf_request_all.
|
|
---Used when refreshing and setting up new symbols
|
|
---@param response table The result from buf_request_all
|
|
---@return outline.SymbolNode[]
|
|
function M.parse(response)
|
|
local sorted = lsp_utils.sort_symbols(response)
|
|
|
|
return parse_result(sorted, nil, nil)
|
|
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
|
|
---@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
|
|
visited[node] = true
|
|
return node
|
|
end
|
|
|
|
if
|
|
node.children and node.traversal_child <= #node.children
|
|
and (node.is_root or children_check(node))
|
|
then
|
|
prev = node
|
|
if node.children[node.traversal_child] then
|
|
node.children[node.traversal_child].parent_node = node
|
|
node = node.children[node.traversal_child]
|
|
end
|
|
prev.traversal_child = prev.traversal_child + 1
|
|
else
|
|
node.traversal_child = 1
|
|
node = node.parent_node
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|