Skip to main content

oak_typst/builder/
mod.rs

1use crate::{
2    ast::*,
3    language::TypstLanguage,
4    lexer::token_type::TypstTokenType,
5    parser::{TypstParser, element_type::TypstElementType},
6};
7use oak_core::{
8    Builder, BuilderCache, GreenNode, OakDiagnostics, OakError, Parser, RedNode, RedTree,
9    source::{Source, TextEdit},
10};
11
12/// AST builder for Typst language
13#[derive(Clone)]
14pub struct TypstBuilder<'config> {
15    config: &'config TypstLanguage,
16}
17
18impl<'config> TypstBuilder<'config> {
19    pub fn new(config: &'config TypstLanguage) -> Self {
20        Self { config }
21    }
22}
23
24impl<'config> Builder<TypstLanguage> for TypstBuilder<'config> {
25    fn build<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], cache: &'a mut impl BuilderCache<TypstLanguage>) -> OakDiagnostics<TypstRoot> {
26        let parser = TypstParser::new(self.config);
27
28        let parse_result = parser.parse(source, edits, cache);
29
30        match parse_result.result {
31            Ok(green_tree) => match self.build_root(green_tree, source) {
32                Ok(ast_root) => OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
33                Err(build_error) => {
34                    let mut diagnostics = parse_result.diagnostics;
35                    diagnostics.push(build_error.clone());
36                    OakDiagnostics { result: Err(build_error), diagnostics }
37                }
38            },
39            Err(parse_error) => OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
40        }
41    }
42}
43
44impl<'config> TypstBuilder<'config> {
45    pub(crate) fn build_root<S: Source + ?Sized>(&self, green_tree: &GreenNode<TypstLanguage>, source: &S) -> Result<TypstRoot, OakError> {
46        let red_root = RedNode::new(green_tree, 0);
47        let mut root = TypstRoot::new(red_root.span());
48
49        for child in red_root.children() {
50            if let Some(item) = self.build_tree(child, source)? {
51                root.items.push(item);
52            }
53        }
54
55        Ok(root)
56    }
57
58    fn build_tree<S: Source + ?Sized>(&self, tree: RedTree<TypstLanguage>, source: &S) -> Result<Option<TypstItem>, OakError> {
59        match tree {
60            RedTree::Node(node) => self.build_item(node, source),
61            RedTree::Leaf(leaf) => match leaf.kind {
62                TypstTokenType::Whitespace | TypstTokenType::Newline => Ok(Some(TypstItem::Space)),
63                _ => Ok(Some(TypstItem::Text(source.get_text_in(leaf.span).to_string()))),
64            },
65        }
66    }
67
68    fn build_item<S: Source + ?Sized>(&self, node: RedNode<TypstLanguage>, source: &S) -> Result<Option<TypstItem>, OakError> {
69        match node.kind::<TypstElementType>() {
70            TypstElementType::Heading => {
71                let text = source.get_text_in(node.span());
72                let mut level = 0;
73                for ch in text.chars() {
74                    if ch == '=' {
75                        level += 1;
76                    }
77                    else {
78                        break;
79                    }
80                }
81                let content_text = text.trim_start_matches('=').trim_start().to_string();
82                let mut content = TypstRoot::new(node.span());
83                content.items.push(TypstItem::Text(content_text));
84
85                Ok(Some(TypstItem::Heading(TypstHeading { level, content })))
86            }
87            TypstElementType::Math => {
88                let mut root = TypstRoot::new(node.span());
89                for child in node.children() {
90                    if let Some(item) = self.build_tree(child, source)? {
91                        root.items.push(item);
92                    }
93                }
94                Ok(Some(TypstItem::Math(root)))
95            }
96            TypstElementType::Strong => {
97                let mut root = TypstRoot::new(node.span());
98                for child in node.children() {
99                    if let Some(item) = self.build_tree(child, source)? {
100                        root.items.push(item);
101                    }
102                }
103                Ok(Some(TypstItem::Strong(root)))
104            }
105            TypstElementType::Emphasis => {
106                let mut root = TypstRoot::new(node.span().into());
107                for child in node.children() {
108                    if let Some(item) = self.build_tree(child, source)? {
109                        root.items.push(item);
110                    }
111                }
112                Ok(Some(TypstItem::Emphasis(root)))
113            }
114            TypstElementType::Quote => {
115                let mut root = TypstRoot::new(node.span().into());
116                for child in node.children() {
117                    if let Some(item) = self.build_tree(child, source)? {
118                        root.items.push(item);
119                    }
120                }
121                Ok(Some(TypstItem::Quote(root)))
122            }
123            TypstElementType::ListItem => {
124                let mut root = TypstRoot::new(node.span().into());
125                for child in node.children() {
126                    if let Some(item) = self.build_tree(child, source)? {
127                        root.items.push(item);
128                    }
129                }
130                Ok(Some(TypstItem::ListItem(root)))
131            }
132            TypstElementType::EnumItem => {
133                let mut root = TypstRoot::new(node.span().into());
134                for child in node.children() {
135                    if let Some(item) = self.build_tree(child, source)? {
136                        root.items.push(item);
137                    }
138                }
139                Ok(Some(TypstItem::EnumItem(root)))
140            }
141            TypstElementType::Raw => Ok(Some(TypstItem::Raw(source.get_text_in(node.span()).to_string()))),
142            _ => Ok(None),
143        }
144    }
145}