-
Notifications
You must be signed in to change notification settings - Fork 19
/
markdown.lua
128 lines (113 loc) · 3.65 KB
/
markdown.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
-- NOTE
-- Our own markdown provider is used because legacy symbols-outline considered
-- the case where markdown does not have an LSP. However, it does, so as of now
-- this module is kept for use when user opens symbols outline before the
-- markdown LSP is ready.
--
-- On buffer open the LSP may not be attached immediately. Before the LSP is
-- ready if the user opens the outline, our own markdown provider will be used.
-- After refreshing/reopening, the provider will then switch to the LSP (if the
-- user has a markdown LSP). That is, if the user has an applicable markdown LSP.
--
-- If they don't this provider will always work as usual.
local M = {
name = 'markdown',
}
---@param bufnr integer
---@param config table?
---@return boolean ft_is_markdown
function M.supports_buffer(bufnr, config)
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
if config and config.filetypes then
for _, ft_check in ipairs(config.filetypes) do
if ft_check == ft then
return true
end
end
end
return ft == "markdown"
end
-- Parses markdown files and returns a table of SymbolInformation[] which is
-- used by the plugin to show the outline.
---@return outline.ProviderSymbol[]
function M.handle_markdown()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local level_symbols = { { children = {} } }
local max_level = 1
local is_inside_code_block = false
for line, value in ipairs(lines) do
if string.find(value, '^```') then
is_inside_code_block = not is_inside_code_block
end
if is_inside_code_block then
goto nextline
end
local next_value = lines[line+1]
local is_emtpy_line = #value:gsub("^%s*(.-)%s*$", "%1") == 0
local header, title = string.match(value, '^(#+)%s+(.+)$')
if not header and next_value and not is_emtpy_line then
if string.match(next_value, '^=+%s*$') then
header = '#'
title = value
elseif string.match(next_value, '^-+%s*$') then
header = '##'
title = value
end
end
if not header or not title then
goto nextline
end
-- TODO: This is not needed and it works?
-- if #header > 6 then
-- goto nextline
-- end
local depth = #header + 1
local parent
for i = depth - 1, 1, -1 do
if level_symbols[i] ~= nil then
parent = level_symbols[i].children
break
end
end
for i = depth, max_level do
if level_symbols[i] ~= nil then
-- -1 for 0-index, -1 to not include current line
-- TODO: This fix can be removed when we let highlight_hovered_item
-- account for current column position in addition to the line
level_symbols[i].selectionRange['end'].line = line - 2
level_symbols[i].range['end'].line = line - 2
level_symbols[i] = nil
end
end
max_level = depth
local entry = {
kind = 15,
name = title,
selectionRange = {
start = { character = 1, line = line - 1 },
['end'] = { character = 1, line = line - 1 },
},
range = {
start = { character = 1, line = line - 1 },
['end'] = { character = 1, line = line - 1 },
},
children = {},
}
parent[#parent + 1] = entry
level_symbols[depth] = entry
::nextline::
end
for i = 2, max_level do
if level_symbols[i] ~= nil then
level_symbols[i].selectionRange['end'].line = #lines
level_symbols[i].range['end'].line = #lines
end
end
return level_symbols[1].children
end
---@param on_symbols fun(symbols?:outline.ProviderSymbol[], opts?:table)
---@param opts table
function M.request_symbols(on_symbols, opts)
on_symbols(M.handle_markdown(), opts)
end
return M