typlite/parser/
list.rs

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
//! HTML list parsing module, handling conversion of ordered and unordered lists

use cmark_writer::ast::{ListItem, Node};
use typst::html::{tag, HtmlElement, HtmlNode};

use crate::attributes::{ListItemAttr, TypliteAttrsParser};
use crate::Result;

use super::core::HtmlToAstParser;

/// List parser
pub struct ListParser;

impl ListParser {
    /// Convert HTML list to ListItem vector
    pub fn convert_list(
        parser: &mut HtmlToAstParser,
        element: &HtmlElement,
    ) -> Result<Vec<ListItem>> {
        let mut all_items = Vec::new();
        let prev_buffer = std::mem::take(&mut parser.inline_buffer);
        let is_ordered = element.tag == tag::ol;

        for child in &element.children {
            if let HtmlNode::Element(li) = child {
                if li.tag == tag::li {
                    let attrs = ListItemAttr::parse(&li.attrs)?;
                    let mut item_content = Vec::new();

                    parser.begin_list();

                    for li_child in &li.children {
                        match li_child {
                            HtmlNode::Text(text, _) => {
                                parser
                                    .inline_buffer
                                    .push(Node::Text(text.as_str().to_string()));
                            }
                            HtmlNode::Element(child_elem) => {
                                if child_elem.tag == tag::ul || child_elem.tag == tag::ol {
                                    // Handle nested lists
                                    if !parser.inline_buffer.is_empty() {
                                        item_content.push(Node::Paragraph(std::mem::take(
                                            &mut parser.inline_buffer,
                                        )));
                                    }

                                    parser.list_level += 1;

                                    let items = Self::convert_list(parser, child_elem)?;

                                    parser.list_level -= 1;

                                    if child_elem.tag == tag::ul {
                                        item_content.push(Node::UnorderedList(items));
                                    } else {
                                        item_content.push(Node::OrderedList { start: 1, items });
                                    }
                                } else {
                                    parser.convert_element(child_elem)?;
                                }
                            }
                            _ => {}
                        }
                    }

                    parser.end_list();

                    if !parser.inline_buffer.is_empty() {
                        item_content
                            .push(Node::Paragraph(std::mem::take(&mut parser.inline_buffer)));
                    }

                    if !item_content.is_empty() {
                        if is_ordered {
                            all_items.push(ListItem::Ordered {
                                number: attrs.value,
                                content: item_content,
                            });
                        } else {
                            all_items.push(ListItem::Unordered {
                                content: item_content,
                            });
                        }
                    }
                }
            }
        }

        parser.inline_buffer = prev_buffer;
        Ok(all_items)
    }
}