Files
outline.nvim/lua/outline/providers/norg.lua
2023-11-26 16:49:25 +08:00

145 lines
3.5 KiB
Lua

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)
]
]],
}
function M.supports_buffer(bufnr)
if vim.api.nvim_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