Somehow marksman also does this? As for treesitter (norg) it may be because treesitter includes the newline and the next line indent until the next heading, so the line of the next heading is included in the range of the previous heading. We manually -1 on the range end line to fix it.
141 lines
3.4 KiB
Lua
141 lines
3.4 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
|
|
|
|
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
|
|
|
|
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
|