yerba 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/document/condition.rs +4 -4
- data/rust/src/document/delete.rs +8 -31
- data/rust/src/document/get.rs +10 -16
- data/rust/src/document/insert.rs +50 -144
- data/rust/src/document/mod.rs +80 -79
- data/rust/src/document/schema.rs +1 -1
- data/rust/src/document/set.rs +42 -38
- data/rust/src/document/sort.rs +150 -310
- data/rust/src/document/style.rs +78 -308
- data/rust/src/document/unique.rs +4 -5
- data/rust/src/selector.rs +3 -3
- data/rust/src/syntax.rs +63 -47
- metadata +1 -1
data/rust/src/document/sort.rs
CHANGED
|
@@ -8,10 +8,7 @@ impl Document {
|
|
|
8
8
|
|
|
9
9
|
let current_node = self.navigate(dot_path)?;
|
|
10
10
|
|
|
11
|
-
let sequence = current_node
|
|
12
|
-
.descendants()
|
|
13
|
-
.find_map(BlockSeq::cast)
|
|
14
|
-
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
11
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
15
12
|
|
|
16
13
|
let entries: Vec<_> = sequence.entries().collect();
|
|
17
14
|
|
|
@@ -25,10 +22,7 @@ impl Document {
|
|
|
25
22
|
|
|
26
23
|
let current_node = self.navigate(dot_path)?;
|
|
27
24
|
|
|
28
|
-
let map = current_node
|
|
29
|
-
.descendants()
|
|
30
|
-
.find_map(BlockMap::cast)
|
|
31
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
25
|
+
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
32
26
|
|
|
33
27
|
let entries: Vec<_> = map.entries().collect();
|
|
34
28
|
|
|
@@ -38,10 +32,7 @@ impl Document {
|
|
|
38
32
|
pub fn resolve_key_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
39
33
|
let current_node = self.navigate(dot_path)?;
|
|
40
34
|
|
|
41
|
-
let map = current_node
|
|
42
|
-
.descendants()
|
|
43
|
-
.find_map(BlockMap::cast)
|
|
44
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
35
|
+
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
45
36
|
|
|
46
37
|
if let Ok(index) = reference.parse::<usize>() {
|
|
47
38
|
let length = map.entries().count();
|
|
@@ -70,10 +61,7 @@ impl Document {
|
|
|
70
61
|
pub fn resolve_sequence_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
71
62
|
let current_node = self.navigate(dot_path)?;
|
|
72
63
|
|
|
73
|
-
let sequence = current_node
|
|
74
|
-
.descendants()
|
|
75
|
-
.find_map(BlockSeq::cast)
|
|
76
|
-
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
64
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
77
65
|
|
|
78
66
|
if let Ok(index) = reference.parse::<usize>() {
|
|
79
67
|
let length = sequence.entries().count();
|
|
@@ -118,7 +106,7 @@ impl Document {
|
|
|
118
106
|
Err(_) => return Ok(()),
|
|
119
107
|
};
|
|
120
108
|
|
|
121
|
-
let map = match current_node
|
|
109
|
+
let map = match find_block_map(¤t_node) {
|
|
122
110
|
Some(map) => map,
|
|
123
111
|
None => return Ok(()),
|
|
124
112
|
};
|
|
@@ -146,59 +134,15 @@ impl Document {
|
|
|
146
134
|
Err(_) => return Ok(()),
|
|
147
135
|
};
|
|
148
136
|
|
|
149
|
-
let map = match current_node
|
|
137
|
+
let map = match find_block_map(¤t_node) {
|
|
150
138
|
Some(map) => map,
|
|
151
139
|
None => return Ok(()),
|
|
152
140
|
};
|
|
153
141
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return Ok(());
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let (groups, range) = collect_groups_with_range(map.syntax());
|
|
161
|
-
|
|
162
|
-
let mut keyed: Vec<(String, EntryGroup)> = entries
|
|
163
|
-
.iter()
|
|
164
|
-
.zip(groups)
|
|
165
|
-
.map(|(entry, group)| {
|
|
166
|
-
let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
|
|
167
|
-
(key_name, group)
|
|
168
|
-
})
|
|
169
|
-
.collect();
|
|
170
|
-
|
|
171
|
-
let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
|
|
172
|
-
|
|
173
|
-
keyed.sort_by(|(key_a, _), (key_b, _)| {
|
|
174
|
-
let position_a = key_order.iter().position(|&key| key == key_a);
|
|
175
|
-
let position_b = key_order.iter().position(|&key| key == key_b);
|
|
176
|
-
|
|
177
|
-
match (position_a, position_b) {
|
|
178
|
-
(Some(a), Some(b)) => a.cmp(&b),
|
|
179
|
-
(Some(_), None) => std::cmp::Ordering::Less,
|
|
180
|
-
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
181
|
-
(None, None) => {
|
|
182
|
-
let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
|
|
183
|
-
let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
|
|
184
|
-
original_a.cmp(&original_b)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
|
|
190
|
-
let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
|
|
191
|
-
|
|
192
|
-
if sorted_keys == orig_refs {
|
|
193
|
-
return Ok(());
|
|
142
|
+
match sort_map_edit(&map, key_order) {
|
|
143
|
+
Some(edit) => self.apply_edits(vec![edit]),
|
|
144
|
+
None => Ok(()),
|
|
194
145
|
}
|
|
195
|
-
|
|
196
|
-
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
197
|
-
|
|
198
|
-
let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
|
|
199
|
-
let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
|
|
200
|
-
|
|
201
|
-
self.apply_edit(range, &map_text)
|
|
202
146
|
}
|
|
203
147
|
|
|
204
148
|
pub fn sort_each_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
@@ -218,92 +162,24 @@ impl Document {
|
|
|
218
162
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
219
163
|
|
|
220
164
|
for current_node in &nodes {
|
|
221
|
-
let sequence = match current_node
|
|
165
|
+
let sequence = match find_block_sequence(current_node) {
|
|
222
166
|
Some(sequence) => sequence,
|
|
223
167
|
None => continue,
|
|
224
168
|
};
|
|
225
169
|
|
|
226
170
|
for entry in sequence.entries() {
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
let map = match entry_node.descendants().find_map(BlockMap::cast) {
|
|
171
|
+
let map = match find_block_map(entry.syntax()) {
|
|
230
172
|
Some(map) => map,
|
|
231
173
|
None => continue,
|
|
232
174
|
};
|
|
233
175
|
|
|
234
|
-
let
|
|
235
|
-
|
|
236
|
-
if entries.len() <= 1 {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let (groups, group_range) = collect_groups_with_range(map.syntax());
|
|
241
|
-
|
|
242
|
-
let mut keyed: Vec<(String, EntryGroup)> = entries
|
|
243
|
-
.iter()
|
|
244
|
-
.zip(groups)
|
|
245
|
-
.map(|(entry, group)| {
|
|
246
|
-
let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
|
|
247
|
-
(key_name, group)
|
|
248
|
-
})
|
|
249
|
-
.collect();
|
|
250
|
-
|
|
251
|
-
let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
|
|
252
|
-
|
|
253
|
-
keyed.sort_by(|(key_a, _), (key_b, _)| {
|
|
254
|
-
let position_a = key_order.iter().position(|&key| key == key_a);
|
|
255
|
-
let position_b = key_order.iter().position(|&key| key == key_b);
|
|
256
|
-
|
|
257
|
-
match (position_a, position_b) {
|
|
258
|
-
(Some(a), Some(b)) => a.cmp(&b),
|
|
259
|
-
(Some(_), None) => std::cmp::Ordering::Less,
|
|
260
|
-
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
261
|
-
|
|
262
|
-
(None, None) => {
|
|
263
|
-
let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
|
|
264
|
-
let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
|
|
265
|
-
|
|
266
|
-
original_a.cmp(&original_b)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
|
|
272
|
-
let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
|
|
273
|
-
|
|
274
|
-
if sorted_keys == orig_refs {
|
|
275
|
-
continue;
|
|
176
|
+
if let Some(edit) = sort_map_edit(&map, key_order) {
|
|
177
|
+
edits.push(edit);
|
|
276
178
|
}
|
|
277
|
-
|
|
278
|
-
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
279
|
-
|
|
280
|
-
let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
|
|
281
|
-
let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
|
|
282
|
-
edits.push((group_range, map_text));
|
|
283
179
|
}
|
|
284
180
|
}
|
|
285
181
|
|
|
286
|
-
|
|
287
|
-
return Ok(());
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
edits.reverse();
|
|
291
|
-
|
|
292
|
-
let source = self.root.text().to_string();
|
|
293
|
-
let mut new_source = source;
|
|
294
|
-
|
|
295
|
-
for (range, replacement) in edits {
|
|
296
|
-
let start: usize = range.start().into();
|
|
297
|
-
let end: usize = range.end().into();
|
|
298
|
-
|
|
299
|
-
new_source.replace_range(start..end, &replacement);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
let path = self.path.take();
|
|
303
|
-
*self = Self::parse(&new_source)?;
|
|
304
|
-
self.path = path;
|
|
305
|
-
|
|
306
|
-
Ok(())
|
|
182
|
+
self.apply_edits(edits)
|
|
307
183
|
}
|
|
308
184
|
|
|
309
185
|
pub fn validate_each_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
@@ -323,13 +199,13 @@ impl Document {
|
|
|
323
199
|
let mut all_unknown: Vec<String> = Vec::new();
|
|
324
200
|
|
|
325
201
|
for current_node in &nodes {
|
|
326
|
-
let sequence = match current_node
|
|
202
|
+
let sequence = match find_block_sequence(current_node) {
|
|
327
203
|
Some(sequence) => sequence,
|
|
328
204
|
None => continue,
|
|
329
205
|
};
|
|
330
206
|
|
|
331
207
|
for entry in sequence.entries() {
|
|
332
|
-
if let Some(map) = entry.syntax()
|
|
208
|
+
if let Some(map) = find_block_map(entry.syntax()) {
|
|
333
209
|
for map_entry in map.entries() {
|
|
334
210
|
if let Some(key_name) = map_entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())) {
|
|
335
211
|
if !key_order.contains(&key_name.as_str()) && !all_unknown.contains(&key_name) {
|
|
@@ -355,86 +231,15 @@ impl Document {
|
|
|
355
231
|
|
|
356
232
|
let current_node = self.navigate(dot_path)?;
|
|
357
233
|
|
|
358
|
-
let sequence = match current_node
|
|
234
|
+
let sequence = match find_block_sequence(¤t_node) {
|
|
359
235
|
Some(sequence) => sequence,
|
|
360
236
|
None => return Ok(()),
|
|
361
237
|
};
|
|
362
238
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return Ok(());
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
let (groups, range) = collect_groups_with_range(sequence.syntax());
|
|
370
|
-
|
|
371
|
-
let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
|
|
372
|
-
.iter()
|
|
373
|
-
.zip(groups)
|
|
374
|
-
.map(|(entry, group)| {
|
|
375
|
-
let sort_values = if sort_fields.is_empty() {
|
|
376
|
-
vec![entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()]
|
|
377
|
-
} else {
|
|
378
|
-
sort_fields
|
|
379
|
-
.iter()
|
|
380
|
-
.map(|field| {
|
|
381
|
-
if field.path.is_empty() {
|
|
382
|
-
entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()
|
|
383
|
-
} else {
|
|
384
|
-
let nodes = navigate_from_node(entry.syntax(), &field.path);
|
|
385
|
-
nodes.first().and_then(extract_scalar_text).unwrap_or_default()
|
|
386
|
-
}
|
|
387
|
-
})
|
|
388
|
-
.collect()
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
(sort_values, group)
|
|
392
|
-
})
|
|
393
|
-
.collect();
|
|
394
|
-
|
|
395
|
-
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
396
|
-
|
|
397
|
-
sortable.sort_by(|(values_a, _), (values_b, _)| {
|
|
398
|
-
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
399
|
-
let value_a = &values_a[index];
|
|
400
|
-
let value_b = &values_b[index];
|
|
401
|
-
|
|
402
|
-
let ordering = if case_sensitive {
|
|
403
|
-
value_a.cmp(value_b)
|
|
404
|
-
} else {
|
|
405
|
-
value_a.to_lowercase().cmp(&value_b.to_lowercase())
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
409
|
-
|
|
410
|
-
if ordering != std::cmp::Ordering::Equal {
|
|
411
|
-
return ordering;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
416
|
-
return if case_sensitive {
|
|
417
|
-
values_a[0].cmp(&values_b[0])
|
|
418
|
-
} else {
|
|
419
|
-
values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
std::cmp::Ordering::Equal
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
427
|
-
|
|
428
|
-
if sorted_bodies == original_bodies {
|
|
429
|
-
return Ok(());
|
|
239
|
+
match sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
|
|
240
|
+
Some(edit) => self.apply_edits(vec![edit]),
|
|
241
|
+
None => Ok(()),
|
|
430
242
|
}
|
|
431
|
-
|
|
432
|
-
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
433
|
-
|
|
434
|
-
let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
|
|
435
|
-
let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
|
|
436
|
-
|
|
437
|
-
self.apply_edit(range, &sequence_text)
|
|
438
243
|
}
|
|
439
244
|
|
|
440
245
|
fn sort_each_items(&mut self, dot_path: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
|
|
@@ -445,7 +250,6 @@ impl Document {
|
|
|
445
250
|
};
|
|
446
251
|
|
|
447
252
|
let parent_nodes = self.navigate_all_compact(parent_path);
|
|
448
|
-
let source = self.root.text().to_string();
|
|
449
253
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
450
254
|
|
|
451
255
|
for parent_node in &parent_nodes {
|
|
@@ -456,108 +260,18 @@ impl Document {
|
|
|
456
260
|
};
|
|
457
261
|
|
|
458
262
|
for child_node in &child_nodes {
|
|
459
|
-
let sequence = match child_node
|
|
263
|
+
let sequence = match find_block_sequence(child_node) {
|
|
460
264
|
Some(sequence) => sequence,
|
|
461
265
|
None => continue,
|
|
462
266
|
};
|
|
463
267
|
|
|
464
|
-
let
|
|
465
|
-
|
|
466
|
-
if entries.len() <= 1 {
|
|
467
|
-
continue;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
let (groups, group_range) = collect_groups_with_range(sequence.syntax());
|
|
471
|
-
|
|
472
|
-
let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
|
|
473
|
-
.iter()
|
|
474
|
-
.zip(groups)
|
|
475
|
-
.map(|(entry, group)| {
|
|
476
|
-
let sort_values = if sort_fields.is_empty() {
|
|
477
|
-
vec![entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()]
|
|
478
|
-
} else {
|
|
479
|
-
sort_fields
|
|
480
|
-
.iter()
|
|
481
|
-
.map(|field| {
|
|
482
|
-
if field.path.is_empty() {
|
|
483
|
-
entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default()
|
|
484
|
-
} else {
|
|
485
|
-
let nodes = navigate_from_node(entry.syntax(), &field.path);
|
|
486
|
-
nodes.first().and_then(extract_scalar_text).unwrap_or_default()
|
|
487
|
-
}
|
|
488
|
-
})
|
|
489
|
-
.collect()
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
(sort_values, group)
|
|
493
|
-
})
|
|
494
|
-
.collect();
|
|
495
|
-
|
|
496
|
-
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
497
|
-
|
|
498
|
-
sortable.sort_by(|(values_a, _), (values_b, _)| {
|
|
499
|
-
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
500
|
-
let value_a = &values_a[index];
|
|
501
|
-
let value_b = &values_b[index];
|
|
502
|
-
|
|
503
|
-
let ordering = if case_sensitive {
|
|
504
|
-
value_a.cmp(value_b)
|
|
505
|
-
} else {
|
|
506
|
-
value_a.to_lowercase().cmp(&value_b.to_lowercase())
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
510
|
-
|
|
511
|
-
if ordering != std::cmp::Ordering::Equal {
|
|
512
|
-
return ordering;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
517
|
-
return if case_sensitive {
|
|
518
|
-
values_a[0].cmp(&values_b[0])
|
|
519
|
-
} else {
|
|
520
|
-
values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
std::cmp::Ordering::Equal
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
528
|
-
|
|
529
|
-
if sorted_bodies == original_bodies {
|
|
530
|
-
continue;
|
|
268
|
+
if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
|
|
269
|
+
edits.push(edit);
|
|
531
270
|
}
|
|
532
|
-
|
|
533
|
-
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
534
|
-
let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
|
|
535
|
-
let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
|
|
536
|
-
|
|
537
|
-
edits.push((group_range, sequence_text));
|
|
538
271
|
}
|
|
539
272
|
}
|
|
540
273
|
|
|
541
|
-
|
|
542
|
-
return Ok(());
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
edits.reverse();
|
|
546
|
-
|
|
547
|
-
let mut new_source = source;
|
|
548
|
-
|
|
549
|
-
for (range, replacement) in edits {
|
|
550
|
-
let start: usize = range.start().into();
|
|
551
|
-
let end: usize = range.end().into();
|
|
552
|
-
|
|
553
|
-
new_source.replace_range(start..end, &replacement);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
let path = self.path.take();
|
|
557
|
-
*self = Self::parse(&new_source)?;
|
|
558
|
-
self.path = path;
|
|
559
|
-
|
|
560
|
-
Ok(())
|
|
274
|
+
self.apply_edits(edits)
|
|
561
275
|
}
|
|
562
276
|
|
|
563
277
|
pub fn reorder_items(&mut self, dot_path: &str, by: &str, desired_order: &[&str]) -> Result<(), YerbaError> {
|
|
@@ -634,6 +348,132 @@ impl Document {
|
|
|
634
348
|
}
|
|
635
349
|
}
|
|
636
350
|
|
|
351
|
+
fn sort_map_edit(map: &BlockMap, key_order: &[&str]) -> Option<(TextRange, String)> {
|
|
352
|
+
let entries: Vec<_> = map.entries().collect();
|
|
353
|
+
|
|
354
|
+
if entries.len() <= 1 {
|
|
355
|
+
return None;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
let (groups, range) = collect_groups_with_range(map.syntax());
|
|
359
|
+
|
|
360
|
+
let mut keyed: Vec<(String, EntryGroup)> = entries
|
|
361
|
+
.iter()
|
|
362
|
+
.zip(groups)
|
|
363
|
+
.map(|(entry, group)| {
|
|
364
|
+
let key_name = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
|
|
365
|
+
(key_name, group)
|
|
366
|
+
})
|
|
367
|
+
.collect();
|
|
368
|
+
|
|
369
|
+
let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
|
|
370
|
+
|
|
371
|
+
keyed.sort_by(|(key_a, _), (key_b, _)| {
|
|
372
|
+
let position_a = key_order.iter().position(|&key| key == key_a);
|
|
373
|
+
let position_b = key_order.iter().position(|&key| key == key_b);
|
|
374
|
+
|
|
375
|
+
match (position_a, position_b) {
|
|
376
|
+
(Some(a), Some(b)) => a.cmp(&b),
|
|
377
|
+
(Some(_), None) => std::cmp::Ordering::Less,
|
|
378
|
+
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
379
|
+
(None, None) => {
|
|
380
|
+
let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
|
|
381
|
+
let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
|
|
382
|
+
|
|
383
|
+
original_a.cmp(&original_b)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
|
|
389
|
+
let original_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
|
|
390
|
+
|
|
391
|
+
if sorted_keys == original_refs {
|
|
392
|
+
return None;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
396
|
+
let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
|
|
397
|
+
|
|
398
|
+
Some((range, rebuild_from_groups(&sorted_groups, &indent, false)))
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool) -> Option<(TextRange, String)> {
|
|
402
|
+
let entries: Vec<_> = sequence.entries().collect();
|
|
403
|
+
|
|
404
|
+
if entries.len() <= 1 {
|
|
405
|
+
return None;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let (groups, range) = collect_groups_with_range(sequence.syntax());
|
|
409
|
+
|
|
410
|
+
let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
|
|
411
|
+
.iter()
|
|
412
|
+
.zip(groups)
|
|
413
|
+
.map(|(entry, group)| (entry_sort_values(entry, sort_fields), group))
|
|
414
|
+
.collect();
|
|
415
|
+
|
|
416
|
+
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
417
|
+
|
|
418
|
+
sortable.sort_by(|(values_a, _), (values_b, _)| compare_sort_values(values_a, values_b, sort_fields, case_sensitive));
|
|
419
|
+
|
|
420
|
+
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
421
|
+
|
|
422
|
+
if sorted_bodies == original_bodies {
|
|
423
|
+
return None;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
let indent = entries.get(1).map(|entry| preceding_whitespace_indent(entry.syntax())).unwrap_or_default();
|
|
427
|
+
let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
|
|
428
|
+
|
|
429
|
+
Some((range, rebuild_from_groups(&sorted_groups, &indent, true)))
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
fn entry_sort_values(entry: &yaml_parser::ast::BlockSeqEntry, sort_fields: &[SortField]) -> Vec<String> {
|
|
433
|
+
let scalar_value = || entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default();
|
|
434
|
+
|
|
435
|
+
if sort_fields.is_empty() {
|
|
436
|
+
return vec![scalar_value()];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
sort_fields
|
|
440
|
+
.iter()
|
|
441
|
+
.map(|field| {
|
|
442
|
+
if field.path.is_empty() {
|
|
443
|
+
scalar_value()
|
|
444
|
+
} else {
|
|
445
|
+
let nodes = navigate_from_node(entry.syntax(), &field.path);
|
|
446
|
+
nodes.first().and_then(extract_scalar_text).unwrap_or_default()
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
.collect()
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[SortField], case_sensitive: bool) -> std::cmp::Ordering {
|
|
453
|
+
let compare = |value_a: &String, value_b: &String| {
|
|
454
|
+
if case_sensitive {
|
|
455
|
+
value_a.cmp(value_b)
|
|
456
|
+
} else {
|
|
457
|
+
value_a.to_lowercase().cmp(&value_b.to_lowercase())
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
462
|
+
let ordering = compare(&values_a[index], &values_b[index]);
|
|
463
|
+
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
464
|
+
|
|
465
|
+
if ordering != std::cmp::Ordering::Equal {
|
|
466
|
+
return ordering;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
471
|
+
return compare(&values_a[0], &values_b[0]);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
std::cmp::Ordering::Equal
|
|
475
|
+
}
|
|
476
|
+
|
|
637
477
|
fn strip_bracket_suffix(path: &str) -> Option<&str> {
|
|
638
478
|
if path == "[]" {
|
|
639
479
|
Some("")
|