267 lines
7.7 KiB
Lua
267 lines
7.7 KiB
Lua
local cfg = require('outline.config')
|
|
local folding = require('outline.folding')
|
|
local lsp_utils = require('outline.utils.lsp')
|
|
local symbols = require('outline.symbols')
|
|
local utils = require('outline.utils.init')
|
|
|
|
local M = {}
|
|
|
|
local function norm_kind(kind)
|
|
if type(kind) == 'number' then
|
|
return kind
|
|
else
|
|
-- string
|
|
return symbols.str_to_kind[kind] or 21 -- fallback to Null
|
|
end
|
|
end
|
|
|
|
---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 outline.ProviderSymbol 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
|
|
---@param bufnr integer The buffer number which the result was from
|
|
---@return outline.Symbol[]
|
|
local function parse_result(result, depth, hierarchy, parent, bufnr)
|
|
local ret = {}
|
|
|
|
-- LEIGH MOORE
|
|
-- Begin filter and Sort Symbols
|
|
local lspKind = {
|
|
[5] = 0, -- 'Class'
|
|
[7] = 1, -- 'Property'
|
|
[9] = 2, -- 'Constructor'
|
|
[6] = 3, -- 'Method'
|
|
[1] = 999, -- 'File'
|
|
[2] = 999, -- 'Module'
|
|
[3] = 999, -- 'Namespace'
|
|
[4] = 999, -- 'Package'
|
|
[8] = 999, -- 'Field'
|
|
[10] = 999, -- 'Enum'
|
|
[11] = 999, -- 'Interface'
|
|
[12] = 999, -- 'Function'
|
|
[13] = 999, -- 'Variable'
|
|
[14] = 999, -- 'Constant'
|
|
[15] = 999, -- 'String'
|
|
[16] = 999, -- 'Number'
|
|
[17] = 999, -- 'Boolean'
|
|
[18] = 999, -- 'Array'
|
|
[19] = 999, -- 'Object'
|
|
[20] = 999, -- 'Key'
|
|
[21] = 999, -- 'Null'
|
|
[22] = 999, -- 'EnumMember'
|
|
[23] = 999, -- 'Struct'
|
|
[24] = 999, -- 'Event'
|
|
[25] = 999, -- 'Operator'
|
|
[26] = 999, -- 'TypeParameter'
|
|
[27] = 999, -- 'Component'
|
|
[28] = 999, -- 'Fragment'
|
|
|
|
-- ccls
|
|
[252] = 999, -- 'TypeAlias'
|
|
[253] = 999, -- 'Parameter'
|
|
[254] = 999, -- 'StaticMethod'
|
|
[255] = 999, -- 'Macro'
|
|
}
|
|
|
|
local function filter_array(arr, predicate)
|
|
local result = {}
|
|
for _, value in ipairs(arr) do
|
|
-- vim.print(vim.inspect(value))
|
|
if predicate(value) then
|
|
table.insert(result, value)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
result = filter_array(result, function(value)
|
|
return value.kind ~= 12 and value.name:match(' callback') == nil
|
|
end)
|
|
|
|
table.sort(result, function(a, b)
|
|
a.kind = norm_kind(a.kind)
|
|
b.kind = norm_kind(b.kind)
|
|
if lspKind[a.kind] < lspKind[b.kind] then
|
|
return true
|
|
elseif lspKind[a.kind] > lspKind[b.kind] then
|
|
return false
|
|
else
|
|
return a.name < b.name
|
|
end
|
|
end)
|
|
-- End filter and Sort Symbols
|
|
|
|
local kinds = {}
|
|
|
|
for index, value in pairs(result) do
|
|
-- FIXME: If a parent was excluded, all children will not be considered
|
|
value.kind = norm_kind(value.kind)
|
|
kinds[value.kind] = true;
|
|
if cfg.should_include_symbol(symbols.kinds[value.kind], bufnr) 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, bufnr, value),
|
|
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,
|
|
_i = 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 = utils.array_copy(hir)
|
|
table.insert(child_hir, isLast)
|
|
children = parse_result(value.children, level + 1, child_hir, node, bufnr)
|
|
else
|
|
value.children = {}
|
|
end
|
|
|
|
node.children = children
|
|
end
|
|
end
|
|
vim.print("KINDS")
|
|
vim.print(vim.inspect(kinds))
|
|
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
|
|
---@param bufnr integer
|
|
---@return outline.Symbol[]
|
|
function M.parse(response, bufnr)
|
|
local sorted = lsp_utils.sort_symbols(response)
|
|
|
|
return parse_result(sorted, nil, nil, { is_root = true, child_count = #sorted }, bufnr)
|
|
end
|
|
|
|
---Iterator that traverses the tree parent first before children, returning each node.
|
|
-- Essentailly 'flatten' items, but returns an iterator.
|
|
---@param items outline.Symbol[] 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, _i = 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._i <= #node.children and (node.is_root or children_check(node)) then
|
|
prev = node
|
|
if node.children[node._i] then
|
|
node.children[node._i].parent_node = node
|
|
node = node.children[node._i]
|
|
end
|
|
prev._i = prev._i + 1
|
|
else
|
|
node._i = 1
|
|
node = node.parent_node
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---Merges a symbol tree recursively, only replacing nodes
|
|
---which have changed. This will maintain the folding
|
|
---status of any unchanged nodes.
|
|
---@param new_node table New node
|
|
---@param old_node table Old node
|
|
---@param index? number Index of old_item in parent
|
|
---@param parent? table Parent of old_item
|
|
function M.merge_items_rec(new_node, old_node, index, parent)
|
|
local failed = false
|
|
|
|
if not new_node or not old_node then
|
|
failed = true
|
|
else
|
|
for key, _ in pairs(new_node) do
|
|
if
|
|
vim.tbl_contains({
|
|
'parent',
|
|
'children',
|
|
'folded',
|
|
'hovered',
|
|
'line_in_outline',
|
|
'hierarchy',
|
|
}, key)
|
|
then
|
|
goto continue
|
|
end
|
|
|
|
if key == 'name' then
|
|
-- in the case of a rename, just rename the existing node
|
|
old_node['name'] = new_node['name']
|
|
else
|
|
if not vim.deep_equal(new_node[key], old_node[key]) then
|
|
failed = true
|
|
break
|
|
end
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
if failed then
|
|
if parent and index then
|
|
parent[index] = new_node
|
|
end
|
|
else
|
|
local next_new_item = new_node.children or {}
|
|
|
|
-- in case new children are created on a node which
|
|
-- previously had no children
|
|
if #next_new_item > 0 and not old_node.children then
|
|
old_node.children = {}
|
|
end
|
|
|
|
local next_old_item = old_node.children or {}
|
|
|
|
for i = 1, math.max(#next_new_item, #next_old_item) do
|
|
M.merge_items_rec(next_new_item[i], next_old_item[i], i, next_old_item)
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|