local M = { name = 'norg', query = [[ [ (heading1 (heading1_prefix) title: (paragraph_segment) @name) (heading2 (heading2_prefix) title: (paragraph_segment) @name) (heading3 (heading3_prefix) title: (paragraph_segment) @name) (heading4 (heading4_prefix) title: (paragraph_segment) @name) (heading5 (heading5_prefix) title: (paragraph_segment) @name) (heading6 (heading6_prefix) title: (paragraph_segment) @name) ] ]], } local utils = require('outline.utils') ---@param bufnr integer ---@param config table? function M.supports_buffer(bufnr, config) if utils.buf_get_option(bufnr, 'ft') ~= 'norg' then return false end local status, parser = pcall(vim.treesitter.get_parser, bufnr, 'norg') if not status or not parser then return false end M.parser = parser return true end local is_ancestor = vim.treesitter.is_ancestor if not _G._outline_nvim_has[8] then is_ancestor = function(dest, source) if not (dest and source) then return false end local current = source while current ~= nil do if current == dest then return true end current = current:parent() end return false end end ---@param node outline.ProviderSymbol ---@param field string local function rec_remove_field(node, field) node[field] = nil if node.children then for _, child in ipairs(node.children) do rec_remove_field(child, field) end end end ---@param callback fun(symbols?:outline.ProviderSymbol[], opts?:table) ---@param opts table function M.request_symbols(callback, opts) if not M.parser then local status, parser = pcall(vim.treesitter.get_parser, 0, 'norg') if not status or not parser then callback(nil, opts) return end M.parser = parser end local root = M.parser:parse()[1]:root() if not root then callback(nil, opts) return end local r = { children = {}, tsnode = root, name = 'root' } local stack = { r } local query if _G._outline_nvim_has[9] then query = vim.treesitter.query.parse('norg', M.query) else ---@diagnostic disable-next-line: deprecated query = vim.treesitter.query.parse_query('norg', M.query) end ---@diagnostic disable-next-line: missing-parameter for _, captured_node, _ in query:iter_captures(root, 0) do local row1, col1, row2, col2 = captured_node:range() local title = vim.api.nvim_buf_get_text(0, row1, col1, row2, col2, {})[1] local heading_node = captured_node:parent() row1, col1, row2, col2 = heading_node:range() title = title:gsub('^%s+', '') local current = { kind = 15, name = title, -- Treesitter includes the last newline in the end range which spans -- until the next heading, so we -1 -- TODO: This fix can be removed when we let highlight_hovered_item -- account for current column position in addition to the line. -- FIXME: By the way the end character should be the EOL selectionRange = { start = { character = col1, line = row1 }, ['end'] = { character = col2, line = row2 - 1 }, }, range = { start = { character = col1, line = row1 }, ['end'] = { character = col2, line = row2 - 1 }, }, children = {}, tsnode = heading_node, } while #stack > 0 do local top = stack[#stack] if is_ancestor(top.tsnode, heading_node) then current.parent = top table.insert(top.children, current) break end table.remove(stack, #stack) end table.insert(stack, current) end rec_remove_field(r, 'tsnode') callback(r.children, opts) end return M