1use crate::{
2 XmlLanguage,
3 ast::{XmlAttribute, XmlElement, XmlRoot, XmlValue},
4 kind::XmlSyntaxKind,
5};
6use core::range::Range;
7use oak_core::{
8 Builder, BuilderCache, GreenNode, GreenTree, OakDiagnostics, OakError, Parser,
9 builder::BuildOutput,
10 source::{Source, SourceText, TextEdit},
11};
12
13pub struct XmlBuilder;
14
15impl XmlBuilder {
16 pub fn new() -> Self {
17 Self
18 }
19
20 fn build_root<'a>(&self, green_tree: &GreenNode<'a, XmlLanguage>, source: &SourceText) -> Result<XmlRoot, OakError> {
21 let mut children = Vec::new();
22 let mut current_offset = 0;
23
24 for child in green_tree.children {
25 match child {
26 GreenTree::Node(node) => {
27 match node.kind {
28 XmlSyntaxKind::Prolog => {
29 }
31 XmlSyntaxKind::Element => {
32 children.push(self.build_element(node, current_offset, source)?);
33 }
34 _ => {}
35 }
36 current_offset += node.text_len as usize;
37 }
38 GreenTree::Leaf(leaf) => {
39 current_offset += leaf.length as usize;
40 }
41 }
42 }
43
44 let value = if children.len() == 1 { XmlValue::Element(children.remove(0)) } else { children.into_iter().next().map(XmlValue::Element).unwrap_or(XmlValue::Text(String::new())) };
45
46 Ok(XmlRoot { value })
47 }
48
49 fn build_element<'a>(&self, node: &GreenNode<'a, XmlLanguage>, offset: usize, source: &SourceText) -> Result<XmlElement, OakError> {
50 let mut name = String::new();
51 let mut attributes = Vec::new();
52 let mut children = Vec::new();
53 let mut current_offset = offset;
54
55 for child in node.children {
56 match child {
57 GreenTree::Node(n) => {
58 match n.kind {
59 XmlSyntaxKind::StartTag | XmlSyntaxKind::SelfClosingTag => {
60 let mut sub_offset = current_offset;
61 for sub_child in n.children {
62 match sub_child {
63 GreenTree::Leaf(t) if t.kind == XmlSyntaxKind::Identifier => {
64 name = source.get_text_in(Range { start: sub_offset, end: sub_offset + t.length as usize }).to_string();
65 }
66 GreenTree::Node(attr_node) if attr_node.kind == XmlSyntaxKind::Attribute => {
67 attributes.push(self.build_attribute(attr_node, sub_offset, source)?);
68 }
69 _ => {}
70 }
71 sub_offset += sub_child.len() as usize;
72 }
73 }
74 XmlSyntaxKind::Element => {
75 children.push(XmlValue::Element(self.build_element(n, current_offset, source)?));
76 }
77 _ => {}
78 }
79 current_offset += n.text_len as usize;
80 }
81 GreenTree::Leaf(t) => {
82 match t.kind {
83 XmlSyntaxKind::Text => {
84 let text = source.get_text_in(Range { start: current_offset, end: current_offset + t.length as usize });
85 if !text.trim().is_empty() {
86 children.push(XmlValue::Text(text.to_string()));
87 }
88 }
89 _ => {}
90 }
91 current_offset += t.length as usize;
92 }
93 }
94 }
95
96 Ok(XmlElement { name, attributes, children, span: Range { start: offset, end: offset + node.text_len as usize } })
97 }
98
99 fn build_attribute<'a>(&self, node: &GreenNode<'a, XmlLanguage>, offset: usize, source: &SourceText) -> Result<XmlAttribute, OakError> {
100 let mut name = String::new();
101 let mut value = String::new();
102 let mut current_offset = offset;
103
104 for child in node.children {
105 match child {
106 GreenTree::Leaf(t) => {
107 match t.kind {
108 XmlSyntaxKind::Identifier => {
109 name = source.get_text_in(Range { start: current_offset, end: current_offset + t.length as usize }).to_string();
110 }
111 XmlSyntaxKind::StringLiteral => {
112 let raw = source.get_text_in(Range { start: current_offset, end: current_offset + t.length as usize });
113 if raw.len() >= 2 {
115 value = raw[1..raw.len() - 1].to_string();
116 }
117 else {
118 value = raw.to_string();
119 }
120 }
121 _ => {}
122 }
123 current_offset += t.length as usize;
124 }
125 GreenTree::Node(n) => {
126 current_offset += n.text_len as usize;
127 }
128 }
129 }
130
131 Ok(XmlAttribute { name, value, span: Range { start: offset, end: offset + node.text_len as usize } })
132 }
133}
134
135impl Builder<XmlLanguage> for XmlBuilder {
136 fn build<'a, S: Source + ?Sized>(&self, text: &S, edits: &[TextEdit], cache: &'a mut impl BuilderCache<XmlLanguage>) -> BuildOutput<XmlLanguage> {
137 let source = SourceText::new(text.get_text_in(Range { start: 0, end: text.length() }).to_string());
138 let config = XmlLanguage::default();
139 let parser = crate::parser::XmlParser::new(&config);
140 let parse_output = parser.parse(text, edits, cache);
141
142 let mut diagnostics = Vec::new();
143 for error in parse_output.diagnostics {
144 diagnostics.push(error);
145 }
146
147 let result = if let Ok(green_tree) = parse_output.result { self.build_root(green_tree, &source) } else { Err(parse_output.result.err().unwrap()) };
148
149 OakDiagnostics { result, diagnostics }
150 }
151}