forked from hedyhli/outline.nvim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
norg.lua
146 lines (124 loc) · 3.57 KB
/
norg.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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)
]
]],
}
---@param bufnr integer
---@param config table?
function M.supports_buffer(bufnr, config)
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