meta_language/
source_generation.rs1use std::collections::BTreeSet;
2
3use crate::link_network::{Link, LinkId, LinkMetadata, LinkNetwork, LinkType};
4
5impl LinkNetwork {
6 #[must_use]
12 pub fn insert_source_token(&mut self, language: &str, text: &str) -> LinkId {
13 self.insert_link(
14 [],
15 LinkMetadata::new()
16 .with_link_type(LinkType::Token)
17 .with_named(!text.trim().is_empty())
18 .with_term(text)
19 .with_language(language),
20 )
21 }
22
23 #[must_use]
29 pub fn insert_syntax_node<const N: usize>(
30 &mut self,
31 language: &str,
32 kind: &str,
33 children: [LinkId; N],
34 ) -> LinkId {
35 self.insert_link(
36 children,
37 LinkMetadata::new()
38 .with_link_type(LinkType::Syntax)
39 .with_named(true)
40 .with_term(kind)
41 .with_language(language),
42 )
43 }
44
45 #[must_use]
47 pub fn insert_dynamic_syntax_node(
48 &mut self,
49 language: &str,
50 kind: &str,
51 children: &[LinkId],
52 ) -> LinkId {
53 self.insert_dynamic_link(
54 children,
55 LinkMetadata::new()
56 .with_link_type(LinkType::Syntax)
57 .with_named(true)
58 .with_term(kind)
59 .with_language(language),
60 )
61 }
62
63 #[must_use]
69 pub fn render_source(&self, language: &str) -> String {
70 if let Some(source) = self.render_source_from_document(language) {
71 return source;
72 }
73
74 self.render_source_roots(language)
75 }
76
77 #[must_use]
79 pub fn render_source_from(&self, root: LinkId, language: &str) -> String {
80 let mut visiting = BTreeSet::new();
81 self.render_link(root, language, &mut visiting)
82 }
83
84 #[must_use]
89 pub fn render_source_from_document(&self, language: &str) -> Option<String> {
90 let mut documents = self
91 .links()
92 .filter(|link| {
93 link.metadata().link_type() == Some(LinkType::Document)
94 && language_matches(link.metadata().language(), language)
95 })
96 .map(Link::id)
97 .peekable();
98
99 documents.peek()?;
100
101 let mut source = String::new();
102 for document in documents {
103 source.push_str(&self.render_source_from(document, language));
104 }
105 Some(source)
106 }
107
108 fn render_source_roots(&self, language: &str) -> String {
109 let child_ids = self.renderable_child_ids(language);
110 let mut roots = self
111 .links()
112 .filter(|link| renderable_root(link, language))
113 .filter(|link| !child_ids.contains(&link.id()))
114 .map(Link::id)
115 .collect::<Vec<_>>();
116 roots.sort_unstable_by_key(|id| id.as_u64());
117
118 let mut source = String::new();
119 for root in roots {
120 source.push_str(&self.render_source_from(root, language));
121 }
122 source
123 }
124
125 fn renderable_child_ids(&self, language: &str) -> BTreeSet<LinkId> {
126 let mut child_ids = BTreeSet::new();
127 for link in self
128 .links()
129 .filter(|link| renderable_container(link, language))
130 {
131 for child in self.render_children(link, language) {
132 child_ids.insert(child);
133 }
134 }
135 child_ids
136 }
137
138 fn render_link(&self, id: LinkId, language: &str, visiting: &mut BTreeSet<LinkId>) -> String {
139 let Some(link) = self.link(id) else {
140 return String::new();
141 };
142 if !renderable_link(link, language) || link.metadata().flags().is_missing() {
143 return String::new();
144 }
145 if link.metadata().link_type() == Some(LinkType::Token) {
146 return link.metadata().term().unwrap_or_default().to_string();
147 }
148 if !visiting.insert(id) {
149 return String::new();
150 }
151
152 let mut source = String::new();
153 for child in self.render_children(link, language) {
154 source.push_str(&self.render_link(child, language, visiting));
155 }
156 visiting.remove(&id);
157 source
158 }
159
160 fn render_children(&self, link: &Link, language: &str) -> Vec<LinkId> {
161 if uses_owned_child_links(link) {
162 let owned_children = self.owned_render_children(link.id(), language);
163 if !owned_children.is_empty() {
164 return owned_children;
165 }
166 }
167
168 if link.metadata().span().is_none() {
169 let direct_children = self.direct_render_children(link, language);
170 if !direct_children.is_empty() {
171 return direct_children;
172 }
173 }
174
175 self.field_render_children(link.id(), language)
176 }
177
178 fn owned_render_children(&self, parent: LinkId, language: &str) -> Vec<LinkId> {
179 let mut children = self
180 .links()
181 .filter(|link| link.id() != parent)
182 .filter(|link| {
183 link.references()
184 .first()
185 .is_some_and(|reference| *reference == parent)
186 })
187 .filter(|link| renderable_child(link, language))
188 .map(Link::id)
189 .collect::<Vec<_>>();
190 self.sort_children_by_source_order(&mut children);
191 children
192 }
193
194 fn direct_render_children(&self, link: &Link, language: &str) -> Vec<LinkId> {
195 let mut children = Vec::new();
196 let mut seen = BTreeSet::new();
197 for child in link.references().iter().copied() {
198 if child == link.id() || !seen.insert(child) {
199 continue;
200 }
201 let Some(child_link) = self.link(child) else {
202 continue;
203 };
204 if renderable_child(child_link, language) {
205 children.push(child);
206 }
207 }
208 children
209 }
210
211 fn field_render_children(&self, parent: LinkId, language: &str) -> Vec<LinkId> {
212 let mut fields = self
213 .links()
214 .filter(|link| link.metadata().link_type() == Some(LinkType::Field))
215 .filter(|link| {
216 link.references()
217 .first()
218 .is_some_and(|reference| *reference == parent)
219 })
220 .filter_map(|field| {
221 field
222 .references()
223 .get(2)
224 .copied()
225 .map(|child| (field.id(), child))
226 })
227 .filter(|(_field, child)| {
228 self.link(*child)
229 .is_some_and(|link| renderable_child(link, language))
230 })
231 .collect::<Vec<_>>();
232 fields.sort_unstable_by_key(|(field, _child)| field.as_u64());
233
234 let mut children = Vec::new();
235 let mut seen = BTreeSet::new();
236 for (_field, child) in fields {
237 if seen.insert(child) {
238 children.push(child);
239 }
240 }
241 children
242 }
243
244 fn sort_children_by_source_order(&self, children: &mut [LinkId]) {
245 children.sort_unstable_by_key(|id| {
246 let span = self.link(*id).and_then(|link| link.metadata().span());
247 (
248 span.is_none(),
249 span.map_or(usize::MAX, |span| span.byte_range().start()),
250 id.as_u64(),
251 )
252 });
253 }
254}
255
256fn renderable_root(link: &Link, language: &str) -> bool {
257 matches!(
258 link.metadata().link_type(),
259 Some(LinkType::Syntax | LinkType::Token)
260 ) && language_matches(link.metadata().language(), language)
261 && !link.metadata().flags().is_missing()
262}
263
264fn renderable_container(link: &Link, language: &str) -> bool {
265 matches!(
266 link.metadata().link_type(),
267 Some(LinkType::Document | LinkType::Region | LinkType::Syntax)
268 ) && language_matches(link.metadata().language(), language)
269 && !link.metadata().flags().is_missing()
270}
271
272fn renderable_child(link: &Link, language: &str) -> bool {
273 matches!(
274 link.metadata().link_type(),
275 Some(LinkType::Syntax | LinkType::Token)
276 ) && language_matches(link.metadata().language(), language)
277 && !link.metadata().flags().is_missing()
278}
279
280fn renderable_link(link: &Link, language: &str) -> bool {
281 matches!(
282 link.metadata().link_type(),
283 Some(LinkType::Document | LinkType::Region | LinkType::Syntax | LinkType::Token)
284 ) && language_matches(link.metadata().language(), language)
285}
286
287const fn uses_owned_child_links(link: &Link) -> bool {
288 link.metadata().span().is_some()
289 || matches!(
290 link.metadata().link_type(),
291 Some(LinkType::Document | LinkType::Region)
292 )
293}
294
295fn language_matches(source_language: Option<&str>, target_language: &str) -> bool {
296 source_language.map_or(true, |source_language| {
297 source_language.eq_ignore_ascii_case(target_language)
298 })
299}