local config = require('outline.config') local M = {} function M.is_buf_attached_to_lsp(bufnr) local clients if _G._outline_nvim_has[10] then clients = vim.lsp.get_clients({ bufnr = bufnr or 0 }) else clients = vim.lsp.get_active_clients({ bufnr = bufnr or 0 }) end return clients ~= nil and #clients > 0 end function M.is_buf_markdown(bufnr) return vim.api.nvim_buf_get_option(bufnr, 'ft') == 'markdown' end --- Merge all client token lists in an LSP response -- Currentlhy unused because we are only supporting receiving symbols -- from 1 LSP client. function M.merge_responses(responses) local all_results = {} -- flatten results to one giant table of symbols for client_id, client_response in pairs(responses) do if config.is_client_blacklisted(client_id) then goto continue end local result = client_response['result'] if result == nil or type(result) ~= 'table' then goto continue end for _, value in pairs(result) do table.insert(all_results, value) end ::continue:: end return all_results end function M.get_selection_range(token) -- support symbolinformation[] -- https://microsoft.github.io/language-server-protocol/specification#textdocument_documentsymbol if token.selectionRange == nil then -- fallback: selectionRange => location.range => range return token.location and token.location.range or token.range end return token.selectionRange end function M.get_range(token) if token == nil then return { start = { line = math.huge, character = math.huge }, ['end'] = { line = -math.huge, character = -math.huge }, } end -- support symbolinformation[] -- https://microsoft.github.io/language-server-protocol/specification#textdocument_documentsymbol if token.range == nil then return token.location.range end return token.range end --- lexicographically strict compare Ranges, line first --- https://microsoft.github.io/language-server-protocol/specification/#range local function range_compare(a, b) if a == nil and b == nil then return true end if a == nil then return true end if b == nil then return false end return (a.line < b.line) or (a.line == b.line and a.character < b.character) end ---Sort the result from LSP by where the symbols start. ---Recursively sorts all children of each symbol function M.sort_symbols(symbols) table.sort(symbols, function(a, b) return range_compare(M.get_range(a).start, M.get_range(b).start) end) for _, child in ipairs(symbols) do if child.children ~= nil then M.sort_symbols(child.children) end end return symbols end --- Preorder DFS iterator on the symbol tree function M.symbol_preorder_iter(symbols) local stk = {} local function push_stk(symbols_list) for i = #symbols_list, 1, -1 do table.insert(stk, symbols_list[i]) end end push_stk(symbols) local function is_empty() return #stk == 0 end local function next() if not is_empty() then local top = table.remove(stk) push_stk(top and top.children or {}) return top end end local function peek() return stk[#stk] end return { next = next, is_empty = is_empty, peek = peek } end local function merge_symbols_rec(iter1, iter2, ub) local res = {} while not (iter1.is_empty() and iter2.is_empty()) do local bv1 = ((not iter1.is_empty()) and M.get_range(iter1.peek()).start) or { line = math.huge, character = math.huge } local bv2 = ((not iter2.is_empty()) and M.get_range(iter2.peek()).start) or { line = math.huge, character = math.huge } local iter = (range_compare(bv1, bv2) and iter1) or iter2 if ub ~= nil and range_compare(ub, M.get_range(iter.peek()).start) then break end local node = iter.next() node.new_children = merge_symbols_rec(iter1, iter2, M.get_range(node)['end']) table.insert(res, node) end return res end --- Merge symbols from two symbol trees --- NOTE: Symbols are mutated! function M.merge_symbols(symbols1, symbols2) M.sort_symbols(symbols1) M.sort_symbols(symbols2) local iter1 = M.symbol_preorder_iter(symbols1) local iter2 = M.symbol_preorder_iter(symbols2) local symbols = merge_symbols_rec(iter1, iter2) local function dfs(nodes) for _, node in ipairs(nodes) do dfs(node.new_children or {}) node.children = node.new_children node.new_children = nil end end dfs(symbols) return symbols end return M