Skip to content

Commit 6011a3c

Browse files
committed
chore(asciidoc): Added foundation for TOC support
1 parent 99f6641 commit 6011a3c

6 files changed

Lines changed: 506 additions & 48 deletions

File tree

lua/markview/config/asciidoc.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,14 @@ return {
4545
document_attributes = {
4646
enable = true,
4747
},
48+
49+
tocs = {
50+
shift_width = 1,
51+
52+
depth_1 = { icon = "1. " },
53+
depth_2 = { icon = "2. " },
54+
depth_3 = { icon = "3. " },
55+
depth_4 = { icon = "4. " },
56+
depth_5 = { icon = "5. " },
57+
},
4858
};

lua/markview/parsers/asciidoc.lua

Lines changed: 161 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
--- HTML parser for `markview.nvim`.
22
local asciidoc = {};
33

4-
asciidoc.data = {
5-
document_title = nil,
6-
};
4+
---@type markview.parser.asciidoc.data
5+
asciidoc.data = {};
76

87
--- Queried contents
98
---@type table[]
@@ -25,9 +24,32 @@ asciidoc.insert = function (data)
2524
table.insert(asciidoc.sorted[data.class], data);
2625
end
2726

27+
---@param buffer integer
28+
---@param TSNode TSNode
2829
---@param text string[]
2930
---@param range markview.parsed.range
30-
asciidoc.doc_attr = function (_, _, text, range)
31+
asciidoc.doc_attr = function (buffer, TSNode, text, range)
32+
local _name = TSNode:named_child(1) --[[@as TSNode]];
33+
local name = vim.treesitter.get_node_text(_name, buffer, {});
34+
35+
local _value = TSNode:named_child(3);
36+
37+
if name == "toc" then
38+
return;
39+
elseif name == "toc-title" and _value then
40+
asciidoc.data.toc_title = vim.treesitter.get_node_text(_value, buffer, {});
41+
elseif name == "toclevels" and _value then
42+
asciidoc.data.toc_max_depth = math.max(
43+
math.min(
44+
tonumber(
45+
vim.treesitter.get_node_text(_value, buffer, {})
46+
) or 0,
47+
5
48+
),
49+
0
50+
);
51+
end
52+
3153
asciidoc.insert({
3254
class = "asciidoc_document_attribute",
3355

@@ -54,15 +76,69 @@ end
5476
---@param text string[]
5577
---@param range markview.parsed.range
5678
asciidoc.section_title = function (buffer, TSNode, text, range)
57-
local marker = TSNode:child(0);
79+
local _marker = TSNode:child(0);
5880

59-
if not marker then
81+
if not _marker then
6082
return;
6183
end
6284

85+
local marker = vim.treesitter.get_node_text(_marker, buffer, {});
86+
local prev = TSNode:prev_named_sibling();
87+
88+
if prev then
89+
local prev_text = vim.treesitter.get_node_text(prev, buffer, {});
90+
91+
if prev:type() == "element_attr" and prev_text == "[discrete]" then
92+
goto dont_add_to_toc;
93+
end
94+
end
95+
96+
if not asciidoc.data.toc_entries then
97+
asciidoc.data.toc_entries = {};
98+
end
99+
100+
table.insert(asciidoc.data.toc_entries, {
101+
depth = (#marker or 1) - 1,
102+
text = string.gsub(text[1] or "", "^[=%s]+", ""),
103+
104+
range = vim.deepcopy(range, true),
105+
} --[[@as markview.parser.asciidoc.data.toc_entry]]);
106+
107+
::dont_add_to_toc::
108+
63109
asciidoc.insert({
64110
class = "asciidoc_section_title",
65-
marker = vim.treesitter.get_node_text(marker, buffer, {}),
111+
marker = marker,
112+
113+
text = text,
114+
range = range
115+
});
116+
end
117+
118+
---@param range markview.parsed.range
119+
asciidoc.toc_pos = function (_, _, _, range)
120+
asciidoc.data.toc_pos = range;
121+
end
122+
123+
---@param text string[]
124+
---@param range markview.parsed.range
125+
asciidoc.toc = function (_, _, text, range)
126+
local validated = {};
127+
128+
for _, entry in ipairs(asciidoc.data.toc_entries or {}) do
129+
if entry.depth < (asciidoc.data.toc_max_depth or 5) then
130+
table.insert(validated, entry);
131+
end
132+
end
133+
134+
range.position = asciidoc.data.toc_pos;
135+
136+
asciidoc.insert({
137+
class = "asciidoc_toc",
138+
139+
title = asciidoc.data.toc_title,
140+
max_depth = asciidoc.data.toc_max_depth,
141+
entries = validated,
66142

67143
text = text,
68144
range = range
@@ -92,6 +168,12 @@ asciidoc.parse = function (buffer, TSTree, from, to)
92168
(title4)
93169
(title5)
94170
] @asciidoc.section_title
171+
172+
(block_macro
173+
(
174+
(block_macro_name) @toc_pos_name
175+
(#eq? @toc_pos_name "toc")
176+
)) @asciidoc.toc_pos
95177
]]);
96178

97179
if not can_scan then
@@ -109,61 +191,92 @@ asciidoc.parse = function (buffer, TSTree, from, to)
109191
return asciidoc.content, asciidoc.sorted;
110192
end
111193

112-
for capture_id, capture_node, _, _ in scanned_queries:iter_captures(TSTree:root(), buffer, from, to) do
113-
local capture_name = scanned_queries.captures[capture_id];
194+
local function iter (queries)
195+
---|fS
114196

115-
if not capture_name:match("^asciidoc%.") then
116-
goto continue;
117-
end
197+
for capture_id, capture_node, _, _ in queries:iter_captures(TSTree:root(), buffer, from, to) do
198+
local capture_name = queries.captures[capture_id];
118199

119-
---@type string?
120-
local capture_text = vim.treesitter.get_node_text(capture_node, buffer);
121-
local r_start, c_start, r_end, c_end = capture_node:range();
200+
if not capture_name:match("^asciidoc%.") then
201+
goto continue;
202+
end
122203

123-
if capture_text == nil then
124-
goto continue;
125-
end
204+
---@type string?
205+
local capture_text = vim.treesitter.get_node_text(capture_node, buffer);
206+
local r_start, c_start, r_end, c_end = capture_node:range();
126207

127-
if not capture_text:match("\n$") then
128-
capture_text = capture_text .. "\n";
129-
end
208+
if capture_text == nil then
209+
goto continue;
210+
end
130211

131-
local lines = {};
212+
if not capture_text:match("\n$") then
213+
capture_text = capture_text .. "\n";
214+
end
132215

133-
for line in capture_text:gmatch("(.-)\n") do
134-
table.insert(lines, line);
135-
end
216+
local lines = {};
136217

137-
---@type boolean, string
138-
local success, error = pcall(
139-
asciidoc[capture_name:gsub("^asciidoc%.", "")],
218+
for line in capture_text:gmatch("(.-)\n") do
219+
table.insert(lines, line);
220+
end
140221

141-
buffer,
142-
capture_node,
143-
lines,
144-
{
145-
row_start = r_start,
146-
col_start = c_start,
222+
---@type boolean, string
223+
local success, error = pcall(
224+
asciidoc[capture_name:gsub("^asciidoc%.", "")],
147225

148-
row_end = r_end,
149-
col_end = c_end
150-
}
151-
);
226+
buffer,
227+
capture_node,
228+
lines,
229+
{
230+
row_start = r_start,
231+
col_start = c_start,
152232

153-
if success == false then
154-
require("markview.health").print({
155-
kind = "ERR",
233+
row_end = r_end,
234+
col_end = c_end
235+
}
236+
);
156237

157-
from = "parsers/asciidoc.lua",
158-
fn = "parse()",
238+
if success == false then
239+
require("markview.health").print({
240+
kind = "ERR",
159241

160-
message = {
161-
{ tostring(error), "DiagnosticError" }
162-
}
163-
});
242+
from = "parsers/asciidoc.lua",
243+
fn = "parse()",
244+
245+
message = {
246+
{ tostring(error), "DiagnosticError" }
247+
}
248+
});
249+
end
250+
251+
::continue::
164252
end
165253

166-
::continue::
254+
---|fE
255+
end
256+
257+
iter(scanned_queries);
258+
259+
local can_scan_tquery, scanned_tqueries = pcall(vim.treesitter.query.parse, "asciidoc", [[
260+
(document_attr
261+
(
262+
(attr_name) @toc_attr
263+
(#eq? @toc_attr "toc")
264+
)) @asciidoc.toc
265+
]]);
266+
267+
if not can_scan_tquery then
268+
require("markview.health").print({
269+
kind = "ERR",
270+
271+
from = "parsers/asciidoc.lua",
272+
fn = "parse() -> toc_query",
273+
274+
message = {
275+
{ tostring(error), "DiagnosticError" }
276+
}
277+
});
278+
else
279+
iter(scanned_tqueries);
167280
end
168281

169282
return asciidoc.content, asciidoc.sorted;

lua/markview/renderers/asciidoc.lua

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,71 @@ asciidoc.section_title = function (buffer, item)
8787
});
8888
end
8989

90+
---@param buffer integer
91+
---@param item markview.parsed.asciidoc.tocs
92+
asciidoc.toc = function (buffer, item)
93+
---@type markview.config.asciidoc.section_titles?
94+
local main_config = spec.get({ "asciidoc", "tocs" }, { fallback = nil, eval_args = { buffer, item } });
95+
96+
if not main_config then
97+
return;
98+
end
99+
100+
local range = item.range;
101+
local lines = {};
102+
103+
if main_config.title then
104+
table.insert(lines, main_config.title);
105+
elseif item.title then
106+
table.insert(lines, { { item.title, main_config.hl } });
107+
else
108+
table.insert(lines, { { "Table of contents", main_config.hl } });
109+
end
110+
111+
if item.entries and #item.entries > 0 then
112+
table.insert(lines, { { "" } });
113+
end
114+
115+
for _, entry in ipairs(item.entries or {}) do
116+
---@type markview.config.asciidoc.section_titles.opts?
117+
local config = spec.get({ "depth_" .. (entry.depth or 1) }, { source = main_config, eval_args = { buffer, item } });
118+
119+
if config then
120+
local text = require("markview.renderers.asciidoc.tostring").tostring(buffer, entry.text, config.hl);
121+
122+
local line = {
123+
{ string.rep(config.shift_char or " ", (entry.depth or 1) - 1), config.hl },
124+
{ config.icon or "", config.icon_hl or config.hl },
125+
};
126+
127+
vim.list_extend(line, text);
128+
table.insert(lines, line);
129+
end
130+
end
131+
132+
if range.position then
133+
utils.set_extmark(buffer, asciidoc.ns, range.row_start, range.col_start, {
134+
end_row = range.row_end - 1,
135+
conceal_lines = "",
136+
});
137+
138+
local r_pos = range.position;
139+
local title = table.remove(lines, 1);
140+
141+
utils.set_extmark(buffer, asciidoc.ns, r_pos.row_start, r_pos.col_start, {
142+
end_col = -1,
143+
conceal = "",
144+
145+
virt_text = title,
146+
virt_text_pos = "inline",
147+
148+
virt_lines = lines,
149+
hl_mode = "combine",
150+
});
151+
else
152+
end
153+
end
154+
90155
---@param buffer integer
91156
---@param content markview.parsed.asciidoc[]
92157
asciidoc.render = function (buffer, content)

0 commit comments

Comments
 (0)