|
1 | 1 | mod code_block;
|
2 | 2 |
|
3 | 3 | use comrak::{
|
4 |
| - nodes::{AstNode, NodeValue}, |
| 4 | + nodes::{AstNode, LineColumn, NodeValue}, |
5 | 5 | parse_document, Arena,
|
6 | 6 | };
|
7 | 7 | use proc_macro2::{Ident, Span, TokenStream};
|
8 | 8 | use quote::quote;
|
9 | 9 | use syn::ItemMacro;
|
10 | 10 |
|
11 |
| -pub fn parse_markdown(md_text: &str) -> Result<(TokenStream, Vec<String>), String> { |
| 11 | +pub fn parse_markdown(md_text: &str) -> Result<(TokenStream, Vec<String>, Vec<(String, String)>), String> { |
12 | 12 | let mut demos: Vec<String> = vec![];
|
| 13 | + let mut toc: Vec<(String, String)> = vec![]; |
13 | 14 |
|
14 | 15 | let arena = Arena::new();
|
15 | 16 | let mut options = comrak::Options::default();
|
16 | 17 | options.extension.table = true;
|
17 | 18 |
|
18 | 19 | let root = parse_document(&arena, &md_text, &options);
|
19 |
| - let body = iter_nodes(root, &mut demos); |
20 |
| - Ok((body, demos)) |
| 20 | + let body = iter_nodes(md_text, root, &mut demos, &mut toc); |
| 21 | + Ok((body, demos, toc)) |
21 | 22 | }
|
22 | 23 |
|
23 |
| -fn iter_nodes<'a>(node: &'a AstNode<'a>, demos: &mut Vec<String>) -> TokenStream { |
| 24 | +fn iter_nodes<'a>( |
| 25 | + md_text: &str, |
| 26 | + node: &'a AstNode<'a>, |
| 27 | + demos: &mut Vec<String>, |
| 28 | + toc: &mut Vec<(String, String)>, |
| 29 | +) -> TokenStream { |
24 | 30 | let mut children = vec![];
|
25 | 31 | for c in node.children() {
|
26 |
| - children.push(iter_nodes(c, demos)); |
| 32 | + children.push(iter_nodes(md_text, c, demos, toc)); |
27 | 33 | }
|
28 | 34 | match &node.data.borrow().value {
|
29 | 35 | NodeValue::Document => quote!(#(#children)*),
|
@@ -55,9 +61,15 @@ fn iter_nodes<'a>(node: &'a AstNode<'a>, demos: &mut Vec<String>) -> TokenStream
|
55 | 61 | </p >
|
56 | 62 | ),
|
57 | 63 | NodeValue::Heading(node_h) => {
|
| 64 | + let sourcepos = node.data.borrow().sourcepos; |
| 65 | + let text = range_text(md_text, sourcepos.start.clone(), sourcepos.end.clone()); |
| 66 | + let level = node_h.level as usize + 1; |
| 67 | + let text = text[level..].to_string(); |
| 68 | + let h_id = format!("{}", text.replace(' ', "-").to_ascii_lowercase()); |
| 69 | + toc.push((h_id.clone(), text)); |
58 | 70 | let h = Ident::new(&format!("h{}", node_h.level), Span::call_site());
|
59 | 71 | quote!(
|
60 |
| - <#h> |
| 72 | + <#h id=#h_id> |
61 | 73 | #(#children)*
|
62 | 74 | </#h>
|
63 | 75 | )
|
@@ -148,3 +160,40 @@ fn iter_nodes<'a>(node: &'a AstNode<'a>, demos: &mut Vec<String>) -> TokenStream
|
148 | 160 | NodeValue::MultilineBlockQuote(_) => quote!("FootnoteReference todo!!!"),
|
149 | 161 | }
|
150 | 162 | }
|
| 163 | + |
| 164 | +fn range_text(text: &str, start: LineColumn, end: LineColumn) -> &str { |
| 165 | + let LineColumn { |
| 166 | + line: start_line, |
| 167 | + column: start_col, |
| 168 | + } = start; |
| 169 | + let LineColumn { |
| 170 | + line: end_line, |
| 171 | + column: end_col, |
| 172 | + } = end; |
| 173 | + |
| 174 | + let mut lines = text.lines(); |
| 175 | + |
| 176 | + let mut current_line_num = 1; |
| 177 | + let mut start_line_text = lines.next().unwrap_or(""); |
| 178 | + while current_line_num < start_line { |
| 179 | + start_line_text = lines.next().unwrap_or(""); |
| 180 | + current_line_num += 1; |
| 181 | + } |
| 182 | + |
| 183 | + let start_index = start_col - 1; |
| 184 | + let mut start_line_text = &start_line_text[start_index..]; |
| 185 | + |
| 186 | + let mut current_line_num = start_line + 1; |
| 187 | + while current_line_num < end_line { |
| 188 | + let next_line = lines.next().unwrap_or(""); |
| 189 | + start_line_text = &next_line; |
| 190 | + current_line_num += 1; |
| 191 | + } |
| 192 | + |
| 193 | + let end_index = end_col; |
| 194 | + if current_line_num == end_line { |
| 195 | + start_line_text = &start_line_text[..end_index]; |
| 196 | + } |
| 197 | + |
| 198 | + start_line_text |
| 199 | +} |
0 commit comments