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