1use std::cmp::Ordering;
7
8use rowan::{GreenNode, GreenNodeBuilder};
9
10use super::tkn_tree::{SyntaxElement, SyntaxNode, SyntaxNodeExtTrait, TomlKind};
11
12pub struct Matcher<'a> {
15 pub heading: &'a [&'a str],
17 pub segmented: &'a [&'a str],
19 pub heading_key: &'a [(&'a str, &'a str)],
22}
23
24fn split_seg_last<S: AsRef<str>>(s: S) -> String {
25 let open_close: &[char] = &['[', ']'];
26 let heading = s.as_ref();
27
28 heading
29 .replace(open_close, "")
30 .split('.')
31 .last()
32 .map(ToString::to_string)
33 .unwrap()
34}
35
36pub fn sort_toml_items(root: &SyntaxNode, matcher: &Matcher<'_>) -> SyntaxNode {
37 let mut builder = GreenNodeBuilder::new();
38 builder.start_node(TomlKind::Root.into());
39
40 for ele in sorted_tables_with_tokens(root, matcher.segmented) {
41 match ele.kind() {
42 TomlKind::Table => {
43 let (head, key): (Vec<_>, Vec<_>) = matcher.heading_key.iter().cloned().unzip();
46 let node = ele.as_node().unwrap();
47 if match_table(node, matcher.heading) {
48 add_sorted_table(node, &mut builder)
49 } else if match_table(node, &head) {
50 add_table_sort_items(node, &mut builder, &key)
51 } else {
52 add_element(ele, &mut builder)
53 }
54 }
55 _ => add_element(ele, &mut builder),
56 }
57 }
58 builder.finish_node();
59 let green: GreenNode = builder.finish();
60 SyntaxNode::new_root(green)
61}
62
63fn match_table(node: &SyntaxNode, headings: &[&str]) -> bool {
64 match node.first_child().map(|n| n.kind()) {
65 Some(TomlKind::Heading) => headings.iter().any(|h| node.token_text().contains(h)),
66 _ => false,
67 }
68}
69
70fn sorted_tables_with_tokens(
71 root: &SyntaxNode,
72 segmented: &[&str],
73) -> impl Iterator<Item = SyntaxElement> {
74 let kids = root.children_with_tokens().collect::<Vec<_>>();
75 let pos = root
76 .children_with_tokens()
77 .enumerate()
78 .filter(|(_, n)| n.as_node().map(|n| n.kind()) == Some(TomlKind::Table))
79 .map(|(i, n)| {
80 (
81 i,
82 n.as_node()
83 .unwrap()
84 .children()
85 .find(|n| n.kind() == TomlKind::Heading)
86 .map(|n| n.token_text()),
87 )
88 })
89 .collect::<Vec<_>>();
90
91 let mut tables = Vec::default();
92 let mut start = 0;
93 for (idx, key) in pos {
94 let next_is_whitespace = kids
95 .get(idx + 1)
96 .map(|el| el.as_token().map(|t| t.kind()) == Some(TomlKind::Whitespace))
97 == Some(true);
98
99 let idx = if next_is_whitespace { idx + 1 } else { idx };
100
101 tables.push((key, kids[start..=idx].to_vec()));
102 start = idx + 1;
103 }
104
105 if start != kids.len() {
106 tables.push((None, kids[start..].to_vec()))
107 }
108
109 for seg in segmented {
110 #[rustfmt::skip]
111 tables.sort_by(|chunk, other| {
112 let chunk_matches_heading = chunk.0.as_ref()
113 .map(|head| head.contains(&format!("[{}", seg))) == Some(true);
114 let other_matches_heading = other.0.as_ref()
115 .map(|head| head.contains(&format!("[{}", seg))) == Some(true);
116
117 if chunk_matches_heading && other_matches_heading {
118 chunk
119 .0
120 .as_ref()
121 .map(split_seg_last)
122 .cmp(&other.0.as_ref().map(split_seg_last))
123 } else {
124 Ordering::Equal
125 }
126 });
127 }
128
129 tables.into_iter().map(|p| p.1).flatten()
130}
131
132fn add_sorted_table(node: &SyntaxNode, builder: &mut GreenNodeBuilder) {
133 builder.start_node(node.kind().into());
134
135 if let Some(heading) = node.first_child() {
136 add_node(&heading, builder);
137 } else {
138 unreachable!("table without heading")
139 }
140
141 let kv = node.children_with_tokens().skip(1).collect::<Vec<_>>();
143 for ele in sort_key_value(&kv) {
144 add_element(ele, builder);
145 }
146
147 builder.finish_node();
148}
149
150fn sort_key_value(kv: &[SyntaxElement]) -> Vec<SyntaxElement> {
151 let pos = kv
152 .iter()
153 .enumerate()
154 .filter(|(_, n)| n.as_node().map(|n| n.kind()) == Some(TomlKind::KeyValue))
155 .map(|(i, n)| {
156 (
157 i,
158 n.as_node()
159 .unwrap()
160 .children()
161 .find(|n| n.kind() == TomlKind::Key)
162 .map(|n| n.token_text()),
163 )
164 })
165 .collect::<Vec<_>>();
166
167 let mut keys = Vec::default();
168 let mut start = 0_usize;
169 for (found_idx, key) in pos {
170 let idx = kv
171 .iter()
172 .skip(start)
173 .enumerate()
174 .take_while(|(count, n)| {
175 n.as_node().map(|n| n.kind()) != Some(TomlKind::KeyValue) || *count == 0
178 })
179 .map(|(idx, _)| idx)
180 .sum::<usize>()
181 + found_idx;
182
183 if start > idx || idx > kv.len() {
186 break;
187 }
188
189 keys.push((key, &kv[start..=idx]));
190 start = idx + 1;
191 }
192
193 if start < kv.len() {
195 keys.push((None, &kv[start..]))
196 }
197
198 keys.sort_by(|chunk, other| {
199 if chunk.0.is_none() || other.0.is_none() {
200 return Ordering::Equal;
201 }
202 chunk.0.cmp(&other.0)
203 });
204 keys.into_iter().map(|p| p.1).flatten().cloned().collect()
205}
206
207fn match_key(node: &SyntaxElement, keys: &[&str]) -> bool {
208 match node
209 .as_node()
210 .map(|n| n.first_child().map(|n| n.kind()))
211 .flatten()
212 {
213 Some(TomlKind::Key) => keys.iter().any(|h| {
214 node.as_node()
215 .unwrap()
216 .first_child()
217 .unwrap()
218 .token_text()
219 .contains(h)
220 && node
221 .as_node()
222 .unwrap()
223 .children()
224 .find(|n| n.kind() == TomlKind::Value)
225 .map(|n| n.first_child().map(|n| n.kind() == TomlKind::Array))
226 .flatten()
227 == Some(true)
228 }),
229 _ => false,
230 }
231}
232
233fn add_table_sort_items(node: &SyntaxNode, builder: &mut GreenNodeBuilder, key: &[&str]) {
234 builder.start_node(node.kind().into());
235
236 if let Some(heading) = node.first_child() {
237 add_node(&heading, builder);
238 } else {
239 unreachable!("table without heading")
240 }
241
242 for ele in node.children_with_tokens().skip(1) {
243 if match_key(&ele, key) {
244 builder.start_node(ele.kind().into());
246 for el in ele.as_node().unwrap().children_with_tokens() {
247 match el {
248 SyntaxElement::Node(n) => match n.kind() {
249 TomlKind::Value => {
250 builder.start_node(TomlKind::Value.into());
251 if n.first_child().map(|n| n.kind()) == Some(TomlKind::Array) {
252 builder.start_node(TomlKind::Array.into());
254 builder
255 .token(TomlKind::OpenBrace.into(), rowan::SmolStr::from("["));
256 for (end, sorted) in sort_items(n.first_child().unwrap()) {
257 add_array_items(sorted, builder, end);
258 }
259 builder
260 .token(TomlKind::CloseBrace.into(), rowan::SmolStr::from("]"));
261 builder.finish_node();
262 }
263 builder.finish_node();
264 }
265 _ => add_node(&n, builder),
266 },
267 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
268 }
269 }
270 builder.finish_node();
271 } else {
272 add_element(ele, builder);
273 }
274 }
275
276 builder.finish_node();
277}
278
279fn sort_items(node: SyntaxNode) -> Vec<(bool, SyntaxElement)> {
280 let children = node
282 .children_with_tokens()
283 .filter(|n| {
284 let n = n.as_token().map(|n| n.kind());
285 n != Some(TomlKind::CloseBrace) && n != Some(TomlKind::OpenBrace)
286 })
287 .collect::<Vec<_>>();
288
289 let pos = children
290 .iter()
291 .enumerate()
292 .filter(|(_, n)| n.as_node().map(|n| n.kind()) == Some(TomlKind::ArrayItem))
293 .map(|(i, n)| {
294 (
295 i,
296 n.as_node()
297 .unwrap()
298 .children()
299 .find(|n| n.kind() == TomlKind::Value)
300 .map(|n| n.token_text()),
301 )
302 })
303 .collect::<Vec<_>>();
304
305 let mut sorted = Vec::default();
306 let mut current = 0;
307 for (idx, key) in pos {
308 let next_is_whitespace = children
309 .get(idx + 1)
310 .map(|el| el.as_token().map(|t| t.kind()) == Some(TomlKind::Whitespace))
311 == Some(true);
312
313 let idx = if next_is_whitespace { idx + 1 } else { idx };
314 sorted.push((key, &children[current..=idx]));
315 current = idx + 1;
316 }
317 if current != children.len() {
318 sorted.push((None, &children[current..]))
319 }
320 sorted.sort_by(|chunk, other| {
321 if chunk.0.is_none() {
322 return Ordering::Equal;
323 }
324 if other.0.is_none() {
325 return Ordering::Equal;
326 }
327 chunk.0.cmp(&other.0)
328 });
329 let end = sorted.len() - 1;
330 sorted
331 .into_iter()
332 .flat_map(|p| p.1)
333 .cloned()
334 .enumerate()
335 .map(|(i, el)| (i == end, el))
336 .collect()
337}
338
339fn add_node(node: &SyntaxNode, builder: &mut GreenNodeBuilder) {
340 builder.start_node(node.kind().into());
341
342 for kid in node.children_with_tokens() {
343 match kid {
344 SyntaxElement::Node(n) => add_node(&n, builder),
345 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
346 }
347 }
348
349 builder.finish_node();
350}
351
352fn add_array_items(node: SyntaxElement, builder: &mut GreenNodeBuilder, end: bool) {
355 match node {
356 SyntaxElement::Node(node) => {
357 if node.kind() == TomlKind::ArrayItem && !end && !node.token_text().contains("\n ") {
358 match node
359 .children_with_tokens()
360 .map(|el| el.kind())
361 .collect::<Vec<TomlKind>>()
362 .as_slice()
363 {
364 [.., TomlKind::Comma, TomlKind::Whitespace] => {
365 builder.start_node(node.kind().into());
367 for kid in node.children_with_tokens() {
368 match kid {
369 SyntaxElement::Node(n) => add_node(&n, builder),
370 SyntaxElement::Token(t) => {
371 builder.token(t.kind().into(), t.text().clone())
372 }
373 }
374 }
375 builder.finish_node();
376 }
377 [.., _, _] | [_] | [] => {
378 builder.start_node(node.kind().into());
380 for kid in node.children_with_tokens() {
381 match kid {
382 SyntaxElement::Node(n) => add_node(&n, builder),
383 SyntaxElement::Token(t) => {
384 builder.token(t.kind().into(), t.text().clone())
385 }
386 }
387 }
388 builder.token(TomlKind::Comma.into(), rowan::SmolStr::from(","));
389 builder.token(TomlKind::Whitespace.into(), rowan::SmolStr::from(" "));
390 builder.finish_node();
391 }
392 }
393 } else if end && !node.token_text().contains("\n ") {
394 builder.start_node(node.kind().into());
396 for kid in node.children_with_tokens() {
397 match kid {
398 SyntaxElement::Node(n) => add_node(&n, builder),
399 SyntaxElement::Token(t) => {
400 if t.kind() == TomlKind::Comma {
401 builder.finish_node();
402 return;
403 }
404 builder.token(t.kind().into(), t.text().clone())
405 }
406 }
407 }
408 builder.finish_node();
409 } else {
410 builder.start_node(node.kind().into());
412 for kid in node.children_with_tokens() {
413 match kid {
414 SyntaxElement::Node(n) => add_node(&n, builder),
415 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
416 }
417 }
418 builder.finish_node();
419 }
420 }
421 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
422 }
423}
424
425fn add_element(node: SyntaxElement, builder: &mut GreenNodeBuilder) {
426 match node {
427 SyntaxElement::Node(node) => {
428 builder.start_node(node.kind().into());
429 for kid in node.children_with_tokens() {
430 match kid {
431 SyntaxElement::Node(n) => add_node(&n, builder),
432 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
433 }
434 }
435 builder.finish_node();
436 }
437 SyntaxElement::Token(t) => builder.token(t.kind().into(), t.text().clone()),
438 }
439}