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]
51 pub fn render_source(&self, language: &str) -> String {
52 if let Some(source) = self.render_source_from_document(language) {
53 return source;
54 }
55
56 self.render_source_roots(language)
57 }
58
59 #[must_use]
61 pub fn render_source_from(&self, root: LinkId, language: &str) -> String {
62 let mut visiting = BTreeSet::new();
63 self.render_link(root, language, &mut visiting)
64 }
65
66 #[must_use]
71 pub fn render_source_from_document(&self, language: &str) -> Option<String> {
72 let mut documents = self
73 .links()
74 .filter(|link| {
75 link.metadata().link_type() == Some(LinkType::Document)
76 && language_matches(link.metadata().language(), language)
77 })
78 .map(Link::id)
79 .peekable();
80
81 documents.peek()?;
82
83 let mut source = String::new();
84 for document in documents {
85 source.push_str(&self.render_source_from(document, language));
86 }
87 Some(source)
88 }
89
90 fn render_source_roots(&self, language: &str) -> String {
91 let child_ids = self.renderable_child_ids(language);
92 let mut roots = self
93 .links()
94 .filter(|link| renderable_root(link, language))
95 .filter(|link| !child_ids.contains(&link.id()))
96 .map(Link::id)
97 .collect::<Vec<_>>();
98 roots.sort_unstable_by_key(|id| id.as_u64());
99
100 let mut source = String::new();
101 for root in roots {
102 source.push_str(&self.render_source_from(root, language));
103 }
104 source
105 }
106
107 fn renderable_child_ids(&self, language: &str) -> BTreeSet<LinkId> {
108 let mut child_ids = BTreeSet::new();
109 for link in self
110 .links()
111 .filter(|link| renderable_container(link, language))
112 {
113 for child in self.render_children(link, language) {
114 child_ids.insert(child);
115 }
116 }
117 child_ids
118 }
119
120 fn render_link(&self, id: LinkId, language: &str, visiting: &mut BTreeSet<LinkId>) -> String {
121 let Some(link) = self.link(id) else {
122 return String::new();
123 };
124 if !renderable_link(link, language) || link.metadata().flags().is_missing() {
125 return String::new();
126 }
127 if link.metadata().link_type() == Some(LinkType::Token) {
128 return link.metadata().term().unwrap_or_default().to_string();
129 }
130 if !visiting.insert(id) {
131 return String::new();
132 }
133
134 let mut source = String::new();
135 for child in self.render_children(link, language) {
136 source.push_str(&self.render_link(child, language, visiting));
137 }
138 visiting.remove(&id);
139 source
140 }
141
142 fn render_children(&self, link: &Link, language: &str) -> Vec<LinkId> {
143 if uses_owned_child_links(link) {
144 let owned_children = self.owned_render_children(link.id(), language);
145 if !owned_children.is_empty() {
146 return owned_children;
147 }
148 }
149
150 if link.metadata().span().is_none() {
151 let direct_children = self.direct_render_children(link, language);
152 if !direct_children.is_empty() {
153 return direct_children;
154 }
155 }
156
157 self.field_render_children(link.id(), language)
158 }
159
160 fn owned_render_children(&self, parent: LinkId, language: &str) -> Vec<LinkId> {
161 let mut children = self
162 .links()
163 .filter(|link| link.id() != parent)
164 .filter(|link| {
165 link.references()
166 .first()
167 .is_some_and(|reference| *reference == parent)
168 })
169 .filter(|link| renderable_child(link, language))
170 .map(Link::id)
171 .collect::<Vec<_>>();
172 self.sort_children_by_source_order(&mut children);
173 children
174 }
175
176 fn direct_render_children(&self, link: &Link, language: &str) -> Vec<LinkId> {
177 let mut children = Vec::new();
178 let mut seen = BTreeSet::new();
179 for child in link.references().iter().copied() {
180 if child == link.id() || !seen.insert(child) {
181 continue;
182 }
183 let Some(child_link) = self.link(child) else {
184 continue;
185 };
186 if renderable_child(child_link, language) {
187 children.push(child);
188 }
189 }
190 children
191 }
192
193 fn field_render_children(&self, parent: LinkId, language: &str) -> Vec<LinkId> {
194 let mut fields = self
195 .links()
196 .filter(|link| link.metadata().link_type() == Some(LinkType::Field))
197 .filter(|link| {
198 link.references()
199 .first()
200 .is_some_and(|reference| *reference == parent)
201 })
202 .filter_map(|field| {
203 field
204 .references()
205 .get(2)
206 .copied()
207 .map(|child| (field.id(), child))
208 })
209 .filter(|(_field, child)| {
210 self.link(*child)
211 .is_some_and(|link| renderable_child(link, language))
212 })
213 .collect::<Vec<_>>();
214 fields.sort_unstable_by_key(|(field, _child)| field.as_u64());
215
216 let mut children = Vec::new();
217 let mut seen = BTreeSet::new();
218 for (_field, child) in fields {
219 if seen.insert(child) {
220 children.push(child);
221 }
222 }
223 children
224 }
225
226 fn sort_children_by_source_order(&self, children: &mut [LinkId]) {
227 children.sort_unstable_by_key(|id| {
228 let span = self.link(*id).and_then(|link| link.metadata().span());
229 (
230 span.is_none(),
231 span.map_or(usize::MAX, |span| span.byte_range().start()),
232 id.as_u64(),
233 )
234 });
235 }
236}
237
238fn renderable_root(link: &Link, language: &str) -> bool {
239 matches!(
240 link.metadata().link_type(),
241 Some(LinkType::Syntax | LinkType::Token)
242 ) && language_matches(link.metadata().language(), language)
243 && !link.metadata().flags().is_missing()
244}
245
246fn renderable_container(link: &Link, language: &str) -> bool {
247 matches!(
248 link.metadata().link_type(),
249 Some(LinkType::Document | LinkType::Region | LinkType::Syntax)
250 ) && language_matches(link.metadata().language(), language)
251 && !link.metadata().flags().is_missing()
252}
253
254fn renderable_child(link: &Link, language: &str) -> bool {
255 matches!(
256 link.metadata().link_type(),
257 Some(LinkType::Syntax | LinkType::Token)
258 ) && language_matches(link.metadata().language(), language)
259 && !link.metadata().flags().is_missing()
260}
261
262fn renderable_link(link: &Link, language: &str) -> bool {
263 matches!(
264 link.metadata().link_type(),
265 Some(LinkType::Document | LinkType::Region | LinkType::Syntax | LinkType::Token)
266 ) && language_matches(link.metadata().language(), language)
267}
268
269const fn uses_owned_child_links(link: &Link) -> bool {
270 link.metadata().span().is_some()
271 || matches!(
272 link.metadata().link_type(),
273 Some(LinkType::Document | LinkType::Region)
274 )
275}
276
277fn language_matches(source_language: Option<&str>, target_language: &str) -> bool {
278 source_language.map_or(true, |source_language| {
279 source_language.eq_ignore_ascii_case(target_language)
280 })
281}