yerba 0.1.1 → 0.2.0
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/README.md +492 -15
- data/ext/yerba/extconf.rb +87 -30
- data/ext/yerba/include/yerba.h +168 -0
- data/ext/yerba/yerba.c +752 -0
- data/lib/yerba/collection.rb +31 -0
- data/lib/yerba/document.rb +59 -0
- data/lib/yerba/formatting.rb +18 -0
- data/lib/yerba/location.rb +5 -0
- data/lib/yerba/map.rb +166 -0
- data/lib/yerba/scalar.rb +85 -0
- data/lib/yerba/sequence.rb +182 -0
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba.rb +30 -4
- data/rust/Cargo.lock +378 -2
- data/rust/Cargo.toml +5 -1
- data/rust/build.rs +11 -0
- data/rust/cbindgen.toml +27 -0
- data/rust/src/commands/apply.rs +5 -0
- data/rust/src/commands/blank_lines.rs +58 -0
- data/rust/src/commands/check.rs +5 -0
- data/rust/src/commands/delete.rs +35 -0
- data/rust/src/commands/get.rs +194 -0
- data/rust/src/commands/init.rs +89 -0
- data/rust/src/commands/insert.rs +106 -0
- data/rust/src/commands/mate.rs +55 -0
- data/rust/src/commands/mod.rs +349 -0
- data/rust/src/commands/move_item.rs +54 -0
- data/rust/src/commands/move_key.rs +87 -0
- data/rust/src/commands/quote_style.rs +62 -0
- data/rust/src/commands/remove.rs +35 -0
- data/rust/src/commands/rename.rs +37 -0
- data/rust/src/commands/set.rs +59 -0
- data/rust/src/commands/sort.rs +52 -0
- data/rust/src/commands/sort_keys.rs +62 -0
- data/rust/src/commands/version.rs +8 -0
- data/rust/src/document.rs +798 -340
- data/rust/src/error.rs +0 -5
- data/rust/src/ffi.rs +991 -0
- data/rust/src/json.rs +49 -90
- data/rust/src/lib.rs +9 -2
- data/rust/src/main.rs +55 -839
- data/rust/src/selector.rs +241 -0
- data/rust/src/syntax.rs +97 -21
- data/rust/src/yaml_writer.rs +89 -0
- data/rust/src/yerbafile.rs +34 -122
- data/yerba.gemspec +4 -0
- metadata +33 -1
data/rust/src/document.rs
CHANGED
|
@@ -11,18 +11,11 @@ use crate::error::YerbaError;
|
|
|
11
11
|
use crate::QuoteStyle;
|
|
12
12
|
|
|
13
13
|
use crate::syntax::{
|
|
14
|
-
extract_scalar_text, find_entry_by_key, find_scalar_token, format_scalar_value, is_map_key,
|
|
15
|
-
preceding_whitespace_indent, preceding_whitespace_token, removal_range, unescape_double_quoted,
|
|
16
|
-
unescape_single_quoted,
|
|
14
|
+
extract_scalar, extract_scalar_text, find_entry_by_key, find_scalar_token, format_scalar_value, is_map_key,
|
|
15
|
+
is_yaml_non_string, preceding_whitespace_indent, preceding_whitespace_token, removal_range, unescape_double_quoted,
|
|
16
|
+
unescape_single_quoted, ScalarValue,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
#[derive(Debug)]
|
|
20
|
-
pub struct FindResult {
|
|
21
|
-
pub text: String,
|
|
22
|
-
pub line: usize,
|
|
23
|
-
pub end_line: usize,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
19
|
#[derive(Debug, Clone)]
|
|
27
20
|
pub struct SortField {
|
|
28
21
|
pub path: String,
|
|
@@ -66,6 +59,8 @@ pub enum InsertPosition {
|
|
|
66
59
|
Last,
|
|
67
60
|
Before(String),
|
|
68
61
|
After(String),
|
|
62
|
+
BeforeCondition(String),
|
|
63
|
+
AfterCondition(String),
|
|
69
64
|
FromSortOrder(Vec<String>),
|
|
70
65
|
}
|
|
71
66
|
|
|
@@ -98,56 +93,79 @@ impl Document {
|
|
|
98
93
|
return self.get_all(dot_path).into_iter().next();
|
|
99
94
|
}
|
|
100
95
|
|
|
101
|
-
let
|
|
102
|
-
let current_node = self.navigate_to_path(&keys).ok()?;
|
|
96
|
+
let current_node = self.navigate(dot_path).ok()?;
|
|
103
97
|
|
|
104
98
|
extract_scalar_text(¤t_node)
|
|
105
99
|
}
|
|
106
100
|
|
|
107
101
|
pub fn get_all(&self, dot_path: &str) -> Vec<String> {
|
|
108
102
|
self
|
|
109
|
-
.
|
|
103
|
+
.navigate_all(dot_path)
|
|
110
104
|
.iter()
|
|
111
105
|
.filter_map(extract_scalar_text)
|
|
112
106
|
.collect()
|
|
113
107
|
}
|
|
114
108
|
|
|
115
|
-
pub fn
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
pub fn get_typed(&self, dot_path: &str) -> Option<ScalarValue> {
|
|
110
|
+
if crate::selector::Selector::parse(dot_path).has_wildcard() {
|
|
111
|
+
return self.get_all_typed(dot_path).into_iter().next();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let current_node = self.navigate(dot_path).ok()?;
|
|
115
|
+
|
|
116
|
+
if current_node
|
|
117
|
+
.descendants()
|
|
118
|
+
.any(|child| child.kind() == SyntaxKind::BLOCK_MAP || child.kind() == SyntaxKind::BLOCK_SEQ)
|
|
119
|
+
{
|
|
120
|
+
return None;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
extract_scalar(¤t_node)
|
|
124
|
+
}
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
pub fn get_all_typed(&self, dot_path: &str) -> Vec<ScalarValue> {
|
|
127
|
+
self
|
|
128
|
+
.navigate_all(dot_path)
|
|
120
129
|
.iter()
|
|
121
|
-
.filter(|node|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
FindResult {
|
|
127
|
-
text: node.text().to_string(),
|
|
128
|
-
line: byte_offset_to_line(&source, start_offset),
|
|
129
|
-
end_line: byte_offset_to_line(&source, end_offset),
|
|
130
|
-
}
|
|
130
|
+
.filter(|node| {
|
|
131
|
+
!node
|
|
132
|
+
.descendants()
|
|
133
|
+
.any(|child| child.kind() == SyntaxKind::BLOCK_MAP || child.kind() == SyntaxKind::BLOCK_SEQ)
|
|
131
134
|
})
|
|
135
|
+
.filter_map(extract_scalar)
|
|
132
136
|
.collect()
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
pub fn
|
|
136
|
-
|
|
139
|
+
pub fn get_value(&self, dot_path: &str) -> Option<serde_yaml::Value> {
|
|
140
|
+
if dot_path.is_empty() {
|
|
141
|
+
return Some(node_to_yaml_value(&self.root));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let nodes = self.navigate_all(dot_path);
|
|
137
145
|
|
|
146
|
+
if nodes.is_empty() {
|
|
147
|
+
return None;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if nodes.len() == 1 {
|
|
151
|
+
return Some(node_to_yaml_value(&nodes[0]));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let values: Vec<serde_yaml::Value> = nodes.iter().map(node_to_yaml_value).collect();
|
|
155
|
+
|
|
156
|
+
Some(serde_yaml::Value::Sequence(values))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pub fn get_values(&self, dot_path: &str) -> Vec<serde_yaml::Value> {
|
|
160
|
+
self.navigate_all(dot_path).iter().map(node_to_yaml_value).collect()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
pub fn filter(&self, dot_path: &str, condition: &str) -> Vec<serde_yaml::Value> {
|
|
138
164
|
self
|
|
139
|
-
.
|
|
165
|
+
.navigate_all(dot_path)
|
|
140
166
|
.iter()
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
let end_offset: usize = node.text_range().end().into();
|
|
144
|
-
|
|
145
|
-
FindResult {
|
|
146
|
-
text: node.text().to_string(),
|
|
147
|
-
line: byte_offset_to_line(&source, start_offset),
|
|
148
|
-
end_line: byte_offset_to_line(&source, end_offset),
|
|
149
|
-
}
|
|
150
|
-
})
|
|
167
|
+
.filter(|node| self.evaluate_condition_on_node(node, condition))
|
|
168
|
+
.map(node_to_yaml_value)
|
|
151
169
|
.collect()
|
|
152
170
|
}
|
|
153
171
|
|
|
@@ -159,9 +177,13 @@ impl Document {
|
|
|
159
177
|
None => return false,
|
|
160
178
|
};
|
|
161
179
|
|
|
162
|
-
let
|
|
163
|
-
let target_nodes = navigate_from_node(node, left_path);
|
|
180
|
+
let path = crate::selector::Selector::parse(&left);
|
|
164
181
|
|
|
182
|
+
if !path.is_relative() {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let target_nodes = navigate_from_node(node, &path.to_selector_string());
|
|
165
187
|
let values: Vec<String> = target_nodes.iter().filter_map(extract_scalar_text).collect();
|
|
166
188
|
|
|
167
189
|
match operator {
|
|
@@ -207,7 +229,7 @@ impl Document {
|
|
|
207
229
|
|
|
208
230
|
pub fn exists(&self, dot_path: &str) -> bool {
|
|
209
231
|
if dot_path.contains('[') {
|
|
210
|
-
return !self.
|
|
232
|
+
return !self.navigate_all(dot_path).is_empty();
|
|
211
233
|
}
|
|
212
234
|
|
|
213
235
|
self.get(dot_path).is_some()
|
|
@@ -221,17 +243,21 @@ impl Document {
|
|
|
221
243
|
None => return false,
|
|
222
244
|
};
|
|
223
245
|
|
|
224
|
-
let
|
|
246
|
+
let path = crate::selector::Selector::parse(&left);
|
|
247
|
+
|
|
248
|
+
let full_path = if path.is_relative() {
|
|
249
|
+
let path_string = path.to_selector_string();
|
|
250
|
+
|
|
225
251
|
if parent_path.is_empty() {
|
|
226
|
-
|
|
252
|
+
path_string
|
|
227
253
|
} else {
|
|
228
|
-
format!("{}.{}", parent_path,
|
|
254
|
+
format!("{}.{}", parent_path, path_string)
|
|
229
255
|
}
|
|
230
256
|
} else {
|
|
231
|
-
|
|
257
|
+
path.to_selector_string()
|
|
232
258
|
};
|
|
233
259
|
|
|
234
|
-
let has_brackets = full_path.
|
|
260
|
+
let has_brackets = crate::selector::Selector::parse(&full_path).has_brackets();
|
|
235
261
|
|
|
236
262
|
match operator {
|
|
237
263
|
"==" => {
|
|
@@ -291,8 +317,7 @@ impl Document {
|
|
|
291
317
|
}
|
|
292
318
|
|
|
293
319
|
pub fn get_sequence_values(&self, dot_path: &str) -> Vec<String> {
|
|
294
|
-
let
|
|
295
|
-
let current_node = match self.navigate_to_path(&keys) {
|
|
320
|
+
let current_node = match self.navigate(dot_path) {
|
|
296
321
|
Ok(node) => node,
|
|
297
322
|
Err(_) => return Vec::new(),
|
|
298
323
|
};
|
|
@@ -309,8 +334,24 @@ impl Document {
|
|
|
309
334
|
}
|
|
310
335
|
|
|
311
336
|
pub fn set(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
|
312
|
-
let
|
|
313
|
-
|
|
337
|
+
let current_node = self.navigate(dot_path)?;
|
|
338
|
+
|
|
339
|
+
if let Some(block_scalar) = current_node
|
|
340
|
+
.descendants()
|
|
341
|
+
.find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR)
|
|
342
|
+
{
|
|
343
|
+
let new_text = if value.is_empty() {
|
|
344
|
+
"\"\"".to_string()
|
|
345
|
+
} else if value.contains('\n') {
|
|
346
|
+
format!("|-\n {}", value.replace('\n', "\n "))
|
|
347
|
+
} else {
|
|
348
|
+
format!("\"{}\"", value.replace('"', "\\\""))
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
let range = block_scalar.text_range();
|
|
352
|
+
|
|
353
|
+
return self.apply_edit(range, &new_text);
|
|
354
|
+
}
|
|
314
355
|
|
|
315
356
|
let scalar_token =
|
|
316
357
|
find_scalar_token(¤t_node).ok_or_else(|| YerbaError::PathNotFound(dot_path.to_string()))?;
|
|
@@ -320,14 +361,64 @@ impl Document {
|
|
|
320
361
|
self.replace_token(&scalar_token, &new_text)
|
|
321
362
|
}
|
|
322
363
|
|
|
364
|
+
pub fn set_scalar_style(&mut self, dot_path: &str, style: &QuoteStyle) -> Result<(), YerbaError> {
|
|
365
|
+
let current_node = self.navigate(dot_path)?;
|
|
366
|
+
let scalar_token =
|
|
367
|
+
find_scalar_token(¤t_node).ok_or_else(|| YerbaError::PathNotFound(dot_path.to_string()))?;
|
|
368
|
+
|
|
369
|
+
let current_kind = scalar_token.kind();
|
|
370
|
+
let target_kind = style.to_syntax_kind();
|
|
371
|
+
|
|
372
|
+
if current_kind == target_kind {
|
|
373
|
+
return Ok(());
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let raw_value = match current_kind {
|
|
377
|
+
SyntaxKind::DOUBLE_QUOTED_SCALAR => {
|
|
378
|
+
let text = scalar_token.text();
|
|
379
|
+
unescape_double_quoted(&text[1..text.len() - 1])
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
383
|
+
let text = scalar_token.text();
|
|
384
|
+
unescape_single_quoted(&text[1..text.len() - 1])
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
SyntaxKind::PLAIN_SCALAR => scalar_token.text().to_string(),
|
|
388
|
+
|
|
389
|
+
_ => return Ok(()),
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
let new_text = format_scalar_value(&raw_value, target_kind);
|
|
393
|
+
|
|
394
|
+
self.replace_token(&scalar_token, &new_text)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
pub fn set_plain(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
|
398
|
+
let current_node = self.navigate(dot_path)?;
|
|
399
|
+
|
|
400
|
+
if let Some(block_scalar) = current_node
|
|
401
|
+
.descendants()
|
|
402
|
+
.find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR)
|
|
403
|
+
{
|
|
404
|
+
let range = block_scalar.text_range();
|
|
405
|
+
return self.apply_edit(range, value);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let scalar_token =
|
|
409
|
+
find_scalar_token(¤t_node).ok_or_else(|| YerbaError::PathNotFound(dot_path.to_string()))?;
|
|
410
|
+
|
|
411
|
+
self.replace_token(&scalar_token, value)
|
|
412
|
+
}
|
|
413
|
+
|
|
323
414
|
pub fn append(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
|
324
415
|
self.insert_into(dot_path, value, InsertPosition::Last)
|
|
325
416
|
}
|
|
326
417
|
|
|
327
418
|
pub fn insert_into(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
|
|
328
|
-
|
|
419
|
+
Self::validate_path(dot_path)?;
|
|
329
420
|
|
|
330
|
-
if let Ok(current_node) = self.
|
|
421
|
+
if let Ok(current_node) = self.navigate(dot_path) {
|
|
331
422
|
if current_node.descendants().find_map(BlockSeq::cast).is_some() {
|
|
332
423
|
return self.insert_sequence_item(dot_path, value, position);
|
|
333
424
|
}
|
|
@@ -339,8 +430,7 @@ impl Document {
|
|
|
339
430
|
}
|
|
340
431
|
|
|
341
432
|
fn insert_sequence_item(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
|
|
342
|
-
let
|
|
343
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
433
|
+
let current_node = self.navigate(dot_path)?;
|
|
344
434
|
|
|
345
435
|
let sequence = current_node
|
|
346
436
|
.descendants()
|
|
@@ -359,7 +449,37 @@ impl Document {
|
|
|
359
449
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
360
450
|
.unwrap_or_default();
|
|
361
451
|
|
|
362
|
-
let new_item =
|
|
452
|
+
let new_item = if value.contains('\n') {
|
|
453
|
+
let item_indent = format!("{} ", indent);
|
|
454
|
+
let lines: Vec<&str> = value.split('\n').collect();
|
|
455
|
+
|
|
456
|
+
let min_indent = lines
|
|
457
|
+
.iter()
|
|
458
|
+
.skip(1)
|
|
459
|
+
.filter(|line| !line.trim().is_empty())
|
|
460
|
+
.map(|line| line.len() - line.trim_start().len())
|
|
461
|
+
.min()
|
|
462
|
+
.unwrap_or(0);
|
|
463
|
+
|
|
464
|
+
let indented: Vec<String> = lines
|
|
465
|
+
.iter()
|
|
466
|
+
.enumerate()
|
|
467
|
+
.map(|(index, line)| {
|
|
468
|
+
if index == 0 {
|
|
469
|
+
line.to_string()
|
|
470
|
+
} else if line.trim().is_empty() {
|
|
471
|
+
String::new()
|
|
472
|
+
} else {
|
|
473
|
+
let relative = &line[min_indent..];
|
|
474
|
+
format!("{}{}", item_indent, relative)
|
|
475
|
+
}
|
|
476
|
+
})
|
|
477
|
+
.collect();
|
|
478
|
+
|
|
479
|
+
format!("- {}", indented.join("\n"))
|
|
480
|
+
} else {
|
|
481
|
+
format!("- {}", value)
|
|
482
|
+
};
|
|
363
483
|
|
|
364
484
|
match position {
|
|
365
485
|
InsertPosition::Last => {
|
|
@@ -421,6 +541,30 @@ impl Document {
|
|
|
421
541
|
self.insert_after_node(target_entry.syntax(), &new_text)
|
|
422
542
|
}
|
|
423
543
|
|
|
544
|
+
InsertPosition::BeforeCondition(condition) => {
|
|
545
|
+
let target_entry = entries
|
|
546
|
+
.iter()
|
|
547
|
+
.find(|entry| self.evaluate_condition_on_node(entry.syntax(), &condition))
|
|
548
|
+
.ok_or_else(|| YerbaError::PathNotFound(format!("{} condition '{}'", dot_path, condition)))?;
|
|
549
|
+
|
|
550
|
+
let target_range = target_entry.syntax().text_range();
|
|
551
|
+
let replacement = format!("{}\n{}", new_item, indent);
|
|
552
|
+
let insert_range = TextRange::new(target_range.start(), target_range.start());
|
|
553
|
+
|
|
554
|
+
self.apply_edit(insert_range, &replacement)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
InsertPosition::AfterCondition(condition) => {
|
|
558
|
+
let target_entry = entries
|
|
559
|
+
.iter()
|
|
560
|
+
.find(|entry| self.evaluate_condition_on_node(entry.syntax(), &condition))
|
|
561
|
+
.ok_or_else(|| YerbaError::PathNotFound(format!("{} condition '{}'", dot_path, condition)))?;
|
|
562
|
+
|
|
563
|
+
let new_text = format!("\n{}{}", indent, new_item);
|
|
564
|
+
|
|
565
|
+
self.insert_after_node(target_entry.syntax(), &new_text)
|
|
566
|
+
}
|
|
567
|
+
|
|
424
568
|
InsertPosition::FromSortOrder(_) => {
|
|
425
569
|
let last_entry = entries.last().unwrap();
|
|
426
570
|
let new_text = format!("\n{}{}", indent, new_item);
|
|
@@ -437,8 +581,7 @@ impl Document {
|
|
|
437
581
|
value: &str,
|
|
438
582
|
position: InsertPosition,
|
|
439
583
|
) -> Result<(), YerbaError> {
|
|
440
|
-
let
|
|
441
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
584
|
+
let current_node = self.navigate(dot_path)?;
|
|
442
585
|
|
|
443
586
|
let map = current_node
|
|
444
587
|
.descendants()
|
|
@@ -513,6 +656,10 @@ impl Document {
|
|
|
513
656
|
self.insert_after_node(target_entry.syntax(), &new_text)
|
|
514
657
|
}
|
|
515
658
|
|
|
659
|
+
InsertPosition::BeforeCondition(_) | InsertPosition::AfterCondition(_) => {
|
|
660
|
+
self.insert_map_key(dot_path, key, value, InsertPosition::Last)
|
|
661
|
+
}
|
|
662
|
+
|
|
516
663
|
InsertPosition::FromSortOrder(order) => {
|
|
517
664
|
let new_key_position = order.iter().position(|ordered_key| ordered_key == key);
|
|
518
665
|
|
|
@@ -542,6 +689,9 @@ impl Document {
|
|
|
542
689
|
}
|
|
543
690
|
|
|
544
691
|
pub fn rename(&mut self, source_path: &str, destination_path: &str) -> Result<(), YerbaError> {
|
|
692
|
+
Self::validate_path(source_path)?;
|
|
693
|
+
Self::validate_path(destination_path)?;
|
|
694
|
+
|
|
545
695
|
let source_parent = source_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
546
696
|
|
|
547
697
|
let destination_parent = destination_path
|
|
@@ -555,9 +705,8 @@ impl Document {
|
|
|
555
705
|
.unwrap_or(destination_path);
|
|
556
706
|
|
|
557
707
|
if source_parent == destination_parent {
|
|
558
|
-
let
|
|
559
|
-
let parent_node = self.
|
|
560
|
-
let source_key = keys.last().unwrap();
|
|
708
|
+
let (parent_path, source_key) = source_path.rsplit_once('.').unwrap_or(("", source_path));
|
|
709
|
+
let parent_node = self.navigate(parent_path)?;
|
|
561
710
|
|
|
562
711
|
let map = parent_node
|
|
563
712
|
.descendants()
|
|
@@ -588,9 +737,10 @@ impl Document {
|
|
|
588
737
|
}
|
|
589
738
|
|
|
590
739
|
pub fn delete(&mut self, dot_path: &str) -> Result<(), YerbaError> {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
let last_key =
|
|
740
|
+
Self::validate_path(dot_path)?;
|
|
741
|
+
|
|
742
|
+
let (parent_path, last_key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
|
|
743
|
+
let parent_node = self.navigate(parent_path)?;
|
|
594
744
|
|
|
595
745
|
let map = parent_node
|
|
596
746
|
.descendants()
|
|
@@ -603,8 +753,7 @@ impl Document {
|
|
|
603
753
|
}
|
|
604
754
|
|
|
605
755
|
pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
|
606
|
-
let
|
|
607
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
756
|
+
let current_node = self.navigate(dot_path)?;
|
|
608
757
|
|
|
609
758
|
let sequence = current_node
|
|
610
759
|
.descendants()
|
|
@@ -625,13 +774,31 @@ impl Document {
|
|
|
625
774
|
self.remove_node(target_entry.syntax())
|
|
626
775
|
}
|
|
627
776
|
|
|
777
|
+
pub fn remove_at(&mut self, dot_path: &str, index: usize) -> Result<(), YerbaError> {
|
|
778
|
+
Self::validate_path(dot_path)?;
|
|
779
|
+
|
|
780
|
+
let current_node = self.navigate(dot_path)?;
|
|
781
|
+
|
|
782
|
+
let sequence = current_node
|
|
783
|
+
.descendants()
|
|
784
|
+
.find_map(BlockSeq::cast)
|
|
785
|
+
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
786
|
+
|
|
787
|
+
let entries: Vec<_> = sequence.entries().collect();
|
|
788
|
+
|
|
789
|
+
if index >= entries.len() {
|
|
790
|
+
return Err(YerbaError::IndexOutOfBounds(index, entries.len()));
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
self.remove_node(entries[index].syntax())
|
|
794
|
+
}
|
|
795
|
+
|
|
628
796
|
pub fn move_item(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
|
|
629
797
|
if from == to {
|
|
630
798
|
return Ok(());
|
|
631
799
|
}
|
|
632
800
|
|
|
633
|
-
let
|
|
634
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
801
|
+
let current_node = self.navigate(dot_path)?;
|
|
635
802
|
|
|
636
803
|
let sequence = current_node
|
|
637
804
|
.descendants()
|
|
@@ -640,14 +807,7 @@ impl Document {
|
|
|
640
807
|
|
|
641
808
|
let entries: Vec<_> = sequence.entries().collect();
|
|
642
809
|
|
|
643
|
-
self.reorder_entries(
|
|
644
|
-
&entries,
|
|
645
|
-
from,
|
|
646
|
-
to,
|
|
647
|
-
|entry| entry.syntax().text().to_string(),
|
|
648
|
-
|entry| preceding_whitespace_indent(entry.syntax()),
|
|
649
|
-
sequence.syntax().text_range(),
|
|
650
|
-
)
|
|
810
|
+
self.reorder_entries(sequence.syntax(), &entries, from, to)
|
|
651
811
|
}
|
|
652
812
|
|
|
653
813
|
pub fn move_key(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
|
|
@@ -655,8 +815,7 @@ impl Document {
|
|
|
655
815
|
return Ok(());
|
|
656
816
|
}
|
|
657
817
|
|
|
658
|
-
let
|
|
659
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
818
|
+
let current_node = self.navigate(dot_path)?;
|
|
660
819
|
|
|
661
820
|
let map = current_node
|
|
662
821
|
.descendants()
|
|
@@ -665,19 +824,11 @@ impl Document {
|
|
|
665
824
|
|
|
666
825
|
let entries: Vec<_> = map.entries().collect();
|
|
667
826
|
|
|
668
|
-
self.reorder_entries(
|
|
669
|
-
&entries,
|
|
670
|
-
from,
|
|
671
|
-
to,
|
|
672
|
-
|entry| entry.syntax().text().to_string(),
|
|
673
|
-
|entry| preceding_whitespace_indent(entry.syntax()),
|
|
674
|
-
map.syntax().text_range(),
|
|
675
|
-
)
|
|
827
|
+
self.reorder_entries(map.syntax(), &entries, from, to)
|
|
676
828
|
}
|
|
677
829
|
|
|
678
830
|
pub fn resolve_key_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
679
|
-
let
|
|
680
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
831
|
+
let current_node = self.navigate(dot_path)?;
|
|
681
832
|
|
|
682
833
|
let map = current_node
|
|
683
834
|
.descendants()
|
|
@@ -709,8 +860,7 @@ impl Document {
|
|
|
709
860
|
}
|
|
710
861
|
|
|
711
862
|
pub fn resolve_sequence_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
712
|
-
let
|
|
713
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
863
|
+
let current_node = self.navigate(dot_path)?;
|
|
714
864
|
|
|
715
865
|
let sequence = current_node
|
|
716
866
|
.descendants()
|
|
@@ -727,6 +877,15 @@ impl Document {
|
|
|
727
877
|
return Ok(index);
|
|
728
878
|
}
|
|
729
879
|
|
|
880
|
+
if crate::selector::Selector::parse(reference).is_relative() {
|
|
881
|
+
return sequence
|
|
882
|
+
.entries()
|
|
883
|
+
.enumerate()
|
|
884
|
+
.find(|(_index, entry)| self.evaluate_condition_on_node(entry.syntax(), reference))
|
|
885
|
+
.map(|(index, _entry)| index)
|
|
886
|
+
.ok_or_else(|| YerbaError::PathNotFound(format!("{} condition '{}'", dot_path, reference)));
|
|
887
|
+
}
|
|
888
|
+
|
|
730
889
|
sequence
|
|
731
890
|
.entries()
|
|
732
891
|
.enumerate()
|
|
@@ -752,8 +911,7 @@ impl Document {
|
|
|
752
911
|
return self.validate_each_sort_keys(seq_path, key_order);
|
|
753
912
|
}
|
|
754
913
|
|
|
755
|
-
let
|
|
756
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
914
|
+
let current_node = self.navigate(dot_path)?;
|
|
757
915
|
|
|
758
916
|
let map = current_node
|
|
759
917
|
.descendants()
|
|
@@ -784,8 +942,7 @@ impl Document {
|
|
|
784
942
|
return self.sort_each_keys(seq_path, key_order);
|
|
785
943
|
}
|
|
786
944
|
|
|
787
|
-
let
|
|
788
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
945
|
+
let current_node = self.navigate(dot_path)?;
|
|
789
946
|
|
|
790
947
|
let map = current_node
|
|
791
948
|
.descendants()
|
|
@@ -798,21 +955,23 @@ impl Document {
|
|
|
798
955
|
return Ok(());
|
|
799
956
|
}
|
|
800
957
|
|
|
801
|
-
let
|
|
958
|
+
let (groups, range) = collect_groups_with_range(map.syntax());
|
|
959
|
+
|
|
960
|
+
let mut keyed: Vec<(String, EntryGroup)> = entries
|
|
802
961
|
.iter()
|
|
803
|
-
.
|
|
962
|
+
.zip(groups)
|
|
963
|
+
.map(|(entry, group)| {
|
|
804
964
|
let key_name = entry
|
|
805
965
|
.key()
|
|
806
966
|
.and_then(|key_node| extract_scalar_text(key_node.syntax()))
|
|
807
967
|
.unwrap_or_default();
|
|
808
|
-
|
|
809
|
-
(key_name, text)
|
|
968
|
+
(key_name, group)
|
|
810
969
|
})
|
|
811
970
|
.collect();
|
|
812
971
|
|
|
813
|
-
let
|
|
972
|
+
let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
|
|
814
973
|
|
|
815
|
-
|
|
974
|
+
keyed.sort_by(|(key_a, _), (key_b, _)| {
|
|
816
975
|
let position_a = key_order.iter().position(|&key| key == key_a);
|
|
817
976
|
let position_b = key_order.iter().position(|&key| key == key_b);
|
|
818
977
|
|
|
@@ -821,16 +980,17 @@ impl Document {
|
|
|
821
980
|
(Some(_), None) => std::cmp::Ordering::Less,
|
|
822
981
|
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
823
982
|
(None, None) => {
|
|
824
|
-
let original_a =
|
|
825
|
-
let original_b =
|
|
983
|
+
let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
|
|
984
|
+
let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
|
|
826
985
|
original_a.cmp(&original_b)
|
|
827
986
|
}
|
|
828
987
|
}
|
|
829
988
|
});
|
|
830
989
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
990
|
+
let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
|
|
991
|
+
let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
|
|
992
|
+
|
|
993
|
+
if sorted_keys == orig_refs {
|
|
834
994
|
return Ok(());
|
|
835
995
|
}
|
|
836
996
|
|
|
@@ -839,15 +999,14 @@ impl Document {
|
|
|
839
999
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
840
1000
|
.unwrap_or_default();
|
|
841
1001
|
|
|
842
|
-
let
|
|
843
|
-
let
|
|
1002
|
+
let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
|
|
1003
|
+
let map_text = rebuild_from_groups(&sorted_groups, &indent);
|
|
844
1004
|
|
|
845
|
-
self.apply_edit(
|
|
1005
|
+
self.apply_edit(range, &map_text)
|
|
846
1006
|
}
|
|
847
1007
|
|
|
848
1008
|
pub fn sort_each_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
849
|
-
let
|
|
850
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
1009
|
+
let current_node = self.navigate(dot_path)?;
|
|
851
1010
|
|
|
852
1011
|
let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
|
|
853
1012
|
Some(sequence) => sequence,
|
|
@@ -870,21 +1029,23 @@ impl Document {
|
|
|
870
1029
|
continue;
|
|
871
1030
|
}
|
|
872
1031
|
|
|
873
|
-
let
|
|
1032
|
+
let (groups, group_range) = collect_groups_with_range(map.syntax());
|
|
1033
|
+
|
|
1034
|
+
let mut keyed: Vec<(String, EntryGroup)> = entries
|
|
874
1035
|
.iter()
|
|
875
|
-
.
|
|
1036
|
+
.zip(groups)
|
|
1037
|
+
.map(|(entry, group)| {
|
|
876
1038
|
let key_name = entry
|
|
877
1039
|
.key()
|
|
878
1040
|
.and_then(|key_node| extract_scalar_text(key_node.syntax()))
|
|
879
1041
|
.unwrap_or_default();
|
|
880
|
-
|
|
881
|
-
(key_name, text)
|
|
1042
|
+
(key_name, group)
|
|
882
1043
|
})
|
|
883
1044
|
.collect();
|
|
884
1045
|
|
|
885
|
-
let
|
|
1046
|
+
let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
|
|
886
1047
|
|
|
887
|
-
|
|
1048
|
+
keyed.sort_by(|(key_a, _), (key_b, _)| {
|
|
888
1049
|
let position_a = key_order.iter().position(|&key| key == key_a);
|
|
889
1050
|
let position_b = key_order.iter().position(|&key| key == key_b);
|
|
890
1051
|
|
|
@@ -894,17 +1055,18 @@ impl Document {
|
|
|
894
1055
|
(None, Some(_)) => std::cmp::Ordering::Greater,
|
|
895
1056
|
|
|
896
1057
|
(None, None) => {
|
|
897
|
-
let original_a =
|
|
898
|
-
let original_b =
|
|
1058
|
+
let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
|
|
1059
|
+
let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
|
|
899
1060
|
|
|
900
1061
|
original_a.cmp(&original_b)
|
|
901
1062
|
}
|
|
902
1063
|
}
|
|
903
1064
|
});
|
|
904
1065
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1066
|
+
let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
|
|
1067
|
+
let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
|
|
1068
|
+
|
|
1069
|
+
if sorted_keys == orig_refs {
|
|
908
1070
|
continue;
|
|
909
1071
|
}
|
|
910
1072
|
|
|
@@ -913,9 +1075,9 @@ impl Document {
|
|
|
913
1075
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
914
1076
|
.unwrap_or_default();
|
|
915
1077
|
|
|
916
|
-
let
|
|
917
|
-
|
|
918
|
-
edits.push((
|
|
1078
|
+
let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
|
|
1079
|
+
let map_text = rebuild_from_groups(&sorted_groups, &indent);
|
|
1080
|
+
edits.push((group_range, map_text));
|
|
919
1081
|
}
|
|
920
1082
|
|
|
921
1083
|
if edits.is_empty() {
|
|
@@ -942,8 +1104,7 @@ impl Document {
|
|
|
942
1104
|
}
|
|
943
1105
|
|
|
944
1106
|
pub fn validate_each_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
945
|
-
let
|
|
946
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
1107
|
+
let current_node = self.navigate(dot_path)?;
|
|
947
1108
|
|
|
948
1109
|
let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
|
|
949
1110
|
Some(sequence) => sequence,
|
|
@@ -974,13 +1135,17 @@ impl Document {
|
|
|
974
1135
|
}
|
|
975
1136
|
}
|
|
976
1137
|
|
|
977
|
-
pub fn sort_items(
|
|
1138
|
+
pub fn sort_items(
|
|
1139
|
+
&mut self,
|
|
1140
|
+
dot_path: &str,
|
|
1141
|
+
sort_fields: &[SortField],
|
|
1142
|
+
case_sensitive: bool,
|
|
1143
|
+
) -> Result<(), YerbaError> {
|
|
978
1144
|
if dot_path.contains("[].") {
|
|
979
|
-
return self.sort_each_items(dot_path, sort_fields);
|
|
1145
|
+
return self.sort_each_items(dot_path, sort_fields, case_sensitive);
|
|
980
1146
|
}
|
|
981
1147
|
|
|
982
|
-
let
|
|
983
|
-
let current_node = self.navigate_to_path(&keys)?;
|
|
1148
|
+
let current_node = self.navigate(dot_path)?;
|
|
984
1149
|
|
|
985
1150
|
let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
|
|
986
1151
|
Some(sequence) => sequence,
|
|
@@ -993,9 +1158,12 @@ impl Document {
|
|
|
993
1158
|
return Ok(());
|
|
994
1159
|
}
|
|
995
1160
|
|
|
996
|
-
let
|
|
1161
|
+
let (groups, range) = collect_groups_with_range(sequence.syntax());
|
|
1162
|
+
|
|
1163
|
+
let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
|
|
997
1164
|
.iter()
|
|
998
|
-
.
|
|
1165
|
+
.zip(groups)
|
|
1166
|
+
.map(|(entry, group)| {
|
|
999
1167
|
let sort_values = if sort_fields.is_empty() {
|
|
1000
1168
|
vec![entry
|
|
1001
1169
|
.flow()
|
|
@@ -1011,18 +1179,22 @@ impl Document {
|
|
|
1011
1179
|
.collect()
|
|
1012
1180
|
};
|
|
1013
1181
|
|
|
1014
|
-
(sort_values,
|
|
1182
|
+
(sort_values, group)
|
|
1015
1183
|
})
|
|
1016
1184
|
.collect();
|
|
1017
1185
|
|
|
1018
|
-
let
|
|
1186
|
+
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
1019
1187
|
|
|
1020
1188
|
sortable.sort_by(|(values_a, _), (values_b, _)| {
|
|
1021
1189
|
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
1022
1190
|
let value_a = &values_a[index];
|
|
1023
1191
|
let value_b = &values_b[index];
|
|
1024
1192
|
|
|
1025
|
-
let ordering =
|
|
1193
|
+
let ordering = if case_sensitive {
|
|
1194
|
+
value_a.cmp(value_b)
|
|
1195
|
+
} else {
|
|
1196
|
+
value_a.to_lowercase().cmp(&value_b.to_lowercase())
|
|
1197
|
+
};
|
|
1026
1198
|
|
|
1027
1199
|
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
1028
1200
|
|
|
@@ -1032,15 +1204,19 @@ impl Document {
|
|
|
1032
1204
|
}
|
|
1033
1205
|
|
|
1034
1206
|
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
1035
|
-
return
|
|
1207
|
+
return if case_sensitive {
|
|
1208
|
+
values_a[0].cmp(&values_b[0])
|
|
1209
|
+
} else {
|
|
1210
|
+
values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
|
|
1211
|
+
};
|
|
1036
1212
|
}
|
|
1037
1213
|
|
|
1038
1214
|
std::cmp::Ordering::Equal
|
|
1039
1215
|
});
|
|
1040
1216
|
|
|
1041
|
-
let
|
|
1217
|
+
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
1042
1218
|
|
|
1043
|
-
if
|
|
1219
|
+
if sorted_bodies == original_bodies {
|
|
1044
1220
|
return Ok(());
|
|
1045
1221
|
}
|
|
1046
1222
|
|
|
@@ -1049,20 +1225,25 @@ impl Document {
|
|
|
1049
1225
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
1050
1226
|
.unwrap_or_default();
|
|
1051
1227
|
|
|
1052
|
-
let
|
|
1053
|
-
let
|
|
1228
|
+
let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
|
|
1229
|
+
let sequence_text = rebuild_from_groups(&sorted_groups, &indent);
|
|
1054
1230
|
|
|
1055
|
-
self.apply_edit(
|
|
1231
|
+
self.apply_edit(range, &sequence_text)
|
|
1056
1232
|
}
|
|
1057
1233
|
|
|
1058
|
-
fn sort_each_items(
|
|
1234
|
+
fn sort_each_items(
|
|
1235
|
+
&mut self,
|
|
1236
|
+
dot_path: &str,
|
|
1237
|
+
sort_fields: &[SortField],
|
|
1238
|
+
case_sensitive: bool,
|
|
1239
|
+
) -> Result<(), YerbaError> {
|
|
1059
1240
|
let (parent_path, child_path) = if let Some(last_bracket) = dot_path.rfind("[].") {
|
|
1060
1241
|
(&dot_path[..last_bracket + 2], &dot_path[last_bracket + 3..])
|
|
1061
1242
|
} else {
|
|
1062
1243
|
(dot_path, "")
|
|
1063
1244
|
};
|
|
1064
1245
|
|
|
1065
|
-
let parent_nodes = self.
|
|
1246
|
+
let parent_nodes = self.navigate_all(parent_path);
|
|
1066
1247
|
let source = self.root.text().to_string();
|
|
1067
1248
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
1068
1249
|
|
|
@@ -1085,9 +1266,12 @@ impl Document {
|
|
|
1085
1266
|
continue;
|
|
1086
1267
|
}
|
|
1087
1268
|
|
|
1088
|
-
let
|
|
1269
|
+
let (groups, group_range) = collect_groups_with_range(sequence.syntax());
|
|
1270
|
+
|
|
1271
|
+
let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
|
|
1089
1272
|
.iter()
|
|
1090
|
-
.
|
|
1273
|
+
.zip(groups)
|
|
1274
|
+
.map(|(entry, group)| {
|
|
1091
1275
|
let sort_values = if sort_fields.is_empty() {
|
|
1092
1276
|
vec![entry
|
|
1093
1277
|
.flow()
|
|
@@ -1103,18 +1287,23 @@ impl Document {
|
|
|
1103
1287
|
.collect()
|
|
1104
1288
|
};
|
|
1105
1289
|
|
|
1106
|
-
(sort_values,
|
|
1290
|
+
(sort_values, group)
|
|
1107
1291
|
})
|
|
1108
1292
|
.collect();
|
|
1109
1293
|
|
|
1110
|
-
let
|
|
1294
|
+
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
1111
1295
|
|
|
1112
1296
|
sortable.sort_by(|(values_a, _), (values_b, _)| {
|
|
1113
1297
|
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
1114
1298
|
let value_a = &values_a[index];
|
|
1115
1299
|
let value_b = &values_b[index];
|
|
1116
1300
|
|
|
1117
|
-
let ordering =
|
|
1301
|
+
let ordering = if case_sensitive {
|
|
1302
|
+
value_a.cmp(value_b)
|
|
1303
|
+
} else {
|
|
1304
|
+
value_a.to_lowercase().cmp(&value_b.to_lowercase())
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1118
1307
|
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
1119
1308
|
|
|
1120
1309
|
if ordering != std::cmp::Ordering::Equal {
|
|
@@ -1123,15 +1312,19 @@ impl Document {
|
|
|
1123
1312
|
}
|
|
1124
1313
|
|
|
1125
1314
|
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
1126
|
-
return
|
|
1315
|
+
return if case_sensitive {
|
|
1316
|
+
values_a[0].cmp(&values_b[0])
|
|
1317
|
+
} else {
|
|
1318
|
+
values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
|
|
1319
|
+
};
|
|
1127
1320
|
}
|
|
1128
1321
|
|
|
1129
1322
|
std::cmp::Ordering::Equal
|
|
1130
1323
|
});
|
|
1131
1324
|
|
|
1132
|
-
let
|
|
1325
|
+
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
1133
1326
|
|
|
1134
|
-
if
|
|
1327
|
+
if sorted_bodies == original_bodies {
|
|
1135
1328
|
continue;
|
|
1136
1329
|
}
|
|
1137
1330
|
|
|
@@ -1140,9 +1333,9 @@ impl Document {
|
|
|
1140
1333
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
1141
1334
|
.unwrap_or_default();
|
|
1142
1335
|
|
|
1143
|
-
let
|
|
1144
|
-
|
|
1145
|
-
edits.push((
|
|
1336
|
+
let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
|
|
1337
|
+
let sequence_text = rebuild_from_groups(&sorted_groups, &indent);
|
|
1338
|
+
edits.push((group_range, sequence_text));
|
|
1146
1339
|
}
|
|
1147
1340
|
}
|
|
1148
1341
|
|
|
@@ -1169,40 +1362,43 @@ impl Document {
|
|
|
1169
1362
|
}
|
|
1170
1363
|
|
|
1171
1364
|
pub fn enforce_blank_lines(&mut self, dot_path: &str, blank_lines: usize) -> Result<(), YerbaError> {
|
|
1172
|
-
let
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
Some(sequence) => sequence,
|
|
1177
|
-
None => return Ok(()),
|
|
1365
|
+
let nodes = if dot_path.contains('[') {
|
|
1366
|
+
self.navigate_all(dot_path)
|
|
1367
|
+
} else {
|
|
1368
|
+
vec![self.navigate(dot_path)?]
|
|
1178
1369
|
};
|
|
1179
1370
|
|
|
1180
|
-
let
|
|
1371
|
+
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
1181
1372
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1373
|
+
for current_node in &nodes {
|
|
1374
|
+
let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
|
|
1375
|
+
Some(sequence) => sequence,
|
|
1376
|
+
None => continue,
|
|
1377
|
+
};
|
|
1185
1378
|
|
|
1186
|
-
|
|
1187
|
-
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
1379
|
+
let entries: Vec<_> = sequence.entries().collect();
|
|
1188
1380
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1381
|
+
if entries.len() <= 1 {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1192
1384
|
|
|
1193
|
-
|
|
1385
|
+
for entry in entries.iter().skip(1) {
|
|
1386
|
+
if let Some(whitespace_token) = preceding_whitespace_token(entry.syntax()) {
|
|
1387
|
+
let whitespace_text = whitespace_token.text();
|
|
1388
|
+
let newline_count = whitespace_text.chars().filter(|character| *character == '\n').count();
|
|
1194
1389
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1390
|
+
let indent = whitespace_text
|
|
1391
|
+
.rfind('\n')
|
|
1392
|
+
.map(|position| &whitespace_text[position + 1..])
|
|
1393
|
+
.unwrap_or("");
|
|
1199
1394
|
|
|
1200
|
-
|
|
1395
|
+
let desired_newlines = blank_lines + 1;
|
|
1201
1396
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1397
|
+
if newline_count != desired_newlines {
|
|
1398
|
+
let new_whitespace = format!("{}{}", "\n".repeat(desired_newlines), indent);
|
|
1204
1399
|
|
|
1205
|
-
|
|
1400
|
+
edits.push((whitespace_token.text_range(), new_whitespace));
|
|
1401
|
+
}
|
|
1206
1402
|
}
|
|
1207
1403
|
}
|
|
1208
1404
|
}
|
|
@@ -1211,8 +1407,9 @@ impl Document {
|
|
|
1211
1407
|
return Ok(());
|
|
1212
1408
|
}
|
|
1213
1409
|
|
|
1214
|
-
edits.
|
|
1410
|
+
edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
|
|
1215
1411
|
|
|
1412
|
+
let source = self.root.text().to_string();
|
|
1216
1413
|
let mut new_source = source;
|
|
1217
1414
|
|
|
1218
1415
|
for (range, replacement) in edits {
|
|
@@ -1232,20 +1429,19 @@ impl Document {
|
|
|
1232
1429
|
pub fn enforce_key_style(&mut self, style: &QuoteStyle, dot_path: Option<&str>) -> Result<(), YerbaError> {
|
|
1233
1430
|
let source = self.root.text().to_string();
|
|
1234
1431
|
|
|
1235
|
-
let
|
|
1236
|
-
Some(path) if !path.is_empty() =>
|
|
1237
|
-
|
|
1238
|
-
self.navigate_to_path(&keys)?
|
|
1239
|
-
}
|
|
1240
|
-
_ => self.root.clone(),
|
|
1432
|
+
let scope_ranges: Vec<TextRange> = match dot_path {
|
|
1433
|
+
Some(path) if !path.is_empty() => self.navigate_all(path).iter().map(|node| node.text_range()).collect(),
|
|
1434
|
+
_ => vec![self.root.text_range()],
|
|
1241
1435
|
};
|
|
1242
1436
|
|
|
1243
|
-
let scope_range = scope_node.text_range();
|
|
1244
1437
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
1245
1438
|
|
|
1246
1439
|
for element in self.root.descendants_with_tokens() {
|
|
1247
1440
|
if let Some(token) = element.into_token() {
|
|
1248
|
-
if !
|
|
1441
|
+
if !scope_ranges
|
|
1442
|
+
.iter()
|
|
1443
|
+
.any(|range| range.contains_range(token.text_range()))
|
|
1444
|
+
{
|
|
1249
1445
|
continue;
|
|
1250
1446
|
}
|
|
1251
1447
|
|
|
@@ -1335,21 +1531,19 @@ impl Document {
|
|
|
1335
1531
|
pub fn enforce_quotes_at(&mut self, style: &QuoteStyle, dot_path: Option<&str>) -> Result<(), YerbaError> {
|
|
1336
1532
|
let source = self.root.text().to_string();
|
|
1337
1533
|
|
|
1338
|
-
let
|
|
1339
|
-
Some(path) if !path.is_empty() =>
|
|
1340
|
-
|
|
1341
|
-
self.navigate_to_path(&keys)?
|
|
1342
|
-
}
|
|
1343
|
-
_ => self.root.clone(),
|
|
1534
|
+
let scope_ranges: Vec<TextRange> = match dot_path {
|
|
1535
|
+
Some(path) if !path.is_empty() => self.navigate_all(path).iter().map(|node| node.text_range()).collect(),
|
|
1536
|
+
_ => vec![self.root.text_range()],
|
|
1344
1537
|
};
|
|
1345
1538
|
|
|
1346
|
-
let scope_range = scope_node.text_range();
|
|
1347
|
-
|
|
1348
1539
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
1349
1540
|
|
|
1350
1541
|
for element in self.root.descendants_with_tokens() {
|
|
1351
1542
|
if let Some(token) = element.into_token() {
|
|
1352
|
-
if !
|
|
1543
|
+
if !scope_ranges
|
|
1544
|
+
.iter()
|
|
1545
|
+
.any(|range| range.contains_range(token.text_range()))
|
|
1546
|
+
{
|
|
1353
1547
|
continue;
|
|
1354
1548
|
}
|
|
1355
1549
|
|
|
@@ -1466,8 +1660,71 @@ impl Document {
|
|
|
1466
1660
|
Ok(())
|
|
1467
1661
|
}
|
|
1468
1662
|
|
|
1469
|
-
pub fn
|
|
1470
|
-
|
|
1663
|
+
pub fn navigate(&self, dot_path: &str) -> Result<SyntaxNode, YerbaError> {
|
|
1664
|
+
Self::validate_path(dot_path)?;
|
|
1665
|
+
|
|
1666
|
+
if dot_path.is_empty() {
|
|
1667
|
+
let root = Root::cast(self.root.clone()).ok_or_else(|| YerbaError::PathNotFound(dot_path.to_string()))?;
|
|
1668
|
+
|
|
1669
|
+
let document = root
|
|
1670
|
+
.documents()
|
|
1671
|
+
.next()
|
|
1672
|
+
.ok_or_else(|| YerbaError::PathNotFound(dot_path.to_string()))?;
|
|
1673
|
+
|
|
1674
|
+
return Ok(document.syntax().clone());
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
let nodes = self.navigate_all(dot_path);
|
|
1678
|
+
|
|
1679
|
+
match nodes.len() {
|
|
1680
|
+
0 => Err(YerbaError::PathNotFound(dot_path.to_string())),
|
|
1681
|
+
1 => Ok(nodes.into_iter().next().unwrap()),
|
|
1682
|
+
_ => Err(YerbaError::PathNotFound(format!(
|
|
1683
|
+
"{} (matched {} nodes, expected 1)",
|
|
1684
|
+
dot_path,
|
|
1685
|
+
nodes.len()
|
|
1686
|
+
))),
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
pub fn validate_path(dot_path: &str) -> Result<(), YerbaError> {
|
|
1691
|
+
if dot_path.ends_with('.') {
|
|
1692
|
+
return Err(YerbaError::ParseError(format!(
|
|
1693
|
+
"invalid path: trailing dot in '{}'",
|
|
1694
|
+
dot_path
|
|
1695
|
+
)));
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
if dot_path.contains("..") {
|
|
1699
|
+
return Err(YerbaError::ParseError(format!(
|
|
1700
|
+
"invalid path: double dot in '{}'",
|
|
1701
|
+
dot_path
|
|
1702
|
+
)));
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if dot_path.starts_with('.') {
|
|
1706
|
+
return Err(YerbaError::ParseError(format!(
|
|
1707
|
+
"invalid path: leading dot in '{}'",
|
|
1708
|
+
dot_path
|
|
1709
|
+
)));
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
if dot_path.contains('[') && !dot_path.contains(']') {
|
|
1713
|
+
return Err(YerbaError::ParseError(format!(
|
|
1714
|
+
"invalid path: unclosed bracket in '{}'",
|
|
1715
|
+
dot_path
|
|
1716
|
+
)));
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
Ok(())
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
pub fn navigate_all(&self, dot_path: &str) -> Vec<SyntaxNode> {
|
|
1723
|
+
if Document::validate_path(dot_path).is_err() {
|
|
1724
|
+
return Vec::new();
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
let parsed = crate::selector::Selector::parse(dot_path);
|
|
1471
1728
|
|
|
1472
1729
|
let root = match Root::cast(self.root.clone()) {
|
|
1473
1730
|
Some(root) => root,
|
|
@@ -1481,7 +1738,7 @@ impl Document {
|
|
|
1481
1738
|
|
|
1482
1739
|
let mut current_nodes = vec![document.syntax().clone()];
|
|
1483
1740
|
|
|
1484
|
-
if
|
|
1741
|
+
if parsed.is_empty() {
|
|
1485
1742
|
if let Some(sequence) = document.syntax().descendants().find_map(BlockSeq::cast) {
|
|
1486
1743
|
current_nodes = sequence.entries().map(|entry| entry.syntax().clone()).collect();
|
|
1487
1744
|
}
|
|
@@ -1489,7 +1746,7 @@ impl Document {
|
|
|
1489
1746
|
return current_nodes;
|
|
1490
1747
|
}
|
|
1491
1748
|
|
|
1492
|
-
for segment in
|
|
1749
|
+
for segment in parsed.segments() {
|
|
1493
1750
|
let mut next_nodes = Vec::new();
|
|
1494
1751
|
|
|
1495
1752
|
for node in ¤t_nodes {
|
|
@@ -1506,37 +1763,6 @@ impl Document {
|
|
|
1506
1763
|
current_nodes
|
|
1507
1764
|
}
|
|
1508
1765
|
|
|
1509
|
-
fn navigate_to_path(&self, keys: &[&str]) -> Result<SyntaxNode, YerbaError> {
|
|
1510
|
-
let keys: Vec<&&str> = keys.iter().filter(|key| !key.is_empty()).collect();
|
|
1511
|
-
let path_string = keys.iter().map(|key| **key).collect::<Vec<_>>().join(".");
|
|
1512
|
-
|
|
1513
|
-
let root = Root::cast(self.root.clone()).ok_or_else(|| YerbaError::PathNotFound(path_string.clone()))?;
|
|
1514
|
-
|
|
1515
|
-
let document = root
|
|
1516
|
-
.documents()
|
|
1517
|
-
.next()
|
|
1518
|
-
.ok_or_else(|| YerbaError::PathNotFound(path_string.clone()))?;
|
|
1519
|
-
|
|
1520
|
-
let mut current_node = document.syntax().clone();
|
|
1521
|
-
|
|
1522
|
-
for key in &keys {
|
|
1523
|
-
let map = current_node
|
|
1524
|
-
.descendants()
|
|
1525
|
-
.find_map(BlockMap::cast)
|
|
1526
|
-
.ok_or_else(|| YerbaError::PathNotFound(path_string.clone()))?;
|
|
1527
|
-
|
|
1528
|
-
let entry = find_entry_by_key(&map, key).ok_or_else(|| YerbaError::PathNotFound(path_string.clone()))?;
|
|
1529
|
-
|
|
1530
|
-
let map_value = entry
|
|
1531
|
-
.value()
|
|
1532
|
-
.ok_or_else(|| YerbaError::PathNotFound(path_string.clone()))?;
|
|
1533
|
-
|
|
1534
|
-
current_node = map_value.syntax().clone();
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
Ok(current_node)
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
1766
|
fn replace_token(&mut self, token: &SyntaxToken, new_text: &str) -> Result<(), YerbaError> {
|
|
1541
1767
|
let range = token.text_range();
|
|
1542
1768
|
|
|
@@ -1551,22 +1777,51 @@ impl Document {
|
|
|
1551
1777
|
}
|
|
1552
1778
|
|
|
1553
1779
|
fn remove_node(&mut self, node: &SyntaxNode) -> Result<(), YerbaError> {
|
|
1780
|
+
let inline_comment = self.find_inline_comment(node);
|
|
1554
1781
|
let range = removal_range(node);
|
|
1555
1782
|
|
|
1556
|
-
|
|
1783
|
+
if let Some((comment_text, comment_end)) = inline_comment {
|
|
1784
|
+
let indent = preceding_whitespace_indent(node);
|
|
1785
|
+
let replacement = format!("\n{}{}", indent, comment_text);
|
|
1786
|
+
let expanded_range = TextRange::new(range.start(), comment_end);
|
|
1787
|
+
|
|
1788
|
+
self.apply_edit(expanded_range, &replacement)
|
|
1789
|
+
} else {
|
|
1790
|
+
self.apply_edit(range, "")
|
|
1791
|
+
}
|
|
1557
1792
|
}
|
|
1558
1793
|
|
|
1559
|
-
fn
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1794
|
+
fn find_inline_comment(&self, node: &SyntaxNode) -> Option<(String, rowan::TextSize)> {
|
|
1795
|
+
let mut sibling = node.next_sibling_or_token();
|
|
1796
|
+
|
|
1797
|
+
while let Some(ref element) = sibling {
|
|
1798
|
+
match element {
|
|
1799
|
+
rowan::NodeOrToken::Token(token) => {
|
|
1800
|
+
if token.kind() == SyntaxKind::COMMENT {
|
|
1801
|
+
return Some((token.text().to_string(), token.text_range().end()));
|
|
1802
|
+
} else if token.kind() == SyntaxKind::WHITESPACE {
|
|
1803
|
+
if token.text().contains('\n') {
|
|
1804
|
+
return None;
|
|
1805
|
+
}
|
|
1806
|
+
} else {
|
|
1807
|
+
return None;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
_ => return None,
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
sibling = match element {
|
|
1814
|
+
rowan::NodeOrToken::Token(token) => token.next_sibling_or_token(),
|
|
1815
|
+
rowan::NodeOrToken::Node(node) => node.next_sibling_or_token(),
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
None
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
fn reorder_entries<T>(&mut self, parent: &SyntaxNode, entries: &[T], from: usize, to: usize) -> Result<(), YerbaError>
|
|
1568
1823
|
where
|
|
1569
|
-
T: rowan::ast::AstNode
|
|
1824
|
+
T: rowan::ast::AstNode<Language = yaml_parser::YamlLanguage>,
|
|
1570
1825
|
{
|
|
1571
1826
|
let length = entries.len();
|
|
1572
1827
|
|
|
@@ -1578,15 +1833,18 @@ impl Document {
|
|
|
1578
1833
|
return Err(YerbaError::IndexOutOfBounds(to, length));
|
|
1579
1834
|
}
|
|
1580
1835
|
|
|
1581
|
-
let
|
|
1836
|
+
let (groups, range) = collect_groups_with_range(parent);
|
|
1582
1837
|
|
|
1583
|
-
let mut reordered =
|
|
1838
|
+
let mut reordered = groups.clone();
|
|
1584
1839
|
let item = reordered.remove(from);
|
|
1585
|
-
|
|
1586
1840
|
reordered.insert(to, item);
|
|
1587
1841
|
|
|
1588
|
-
let indent = entries
|
|
1589
|
-
|
|
1842
|
+
let indent = entries
|
|
1843
|
+
.get(1)
|
|
1844
|
+
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
1845
|
+
.unwrap_or_default();
|
|
1846
|
+
|
|
1847
|
+
let text = rebuild_from_groups(&reordered, &indent);
|
|
1590
1848
|
|
|
1591
1849
|
self.apply_edit(range, &text)
|
|
1592
1850
|
}
|
|
@@ -1613,6 +1871,96 @@ impl std::fmt::Display for Document {
|
|
|
1613
1871
|
}
|
|
1614
1872
|
}
|
|
1615
1873
|
|
|
1874
|
+
pub fn node_to_yaml_value(node: &SyntaxNode) -> serde_yaml::Value {
|
|
1875
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
1876
|
+
let map_position = node
|
|
1877
|
+
.descendants()
|
|
1878
|
+
.find_map(BlockMap::cast)
|
|
1879
|
+
.map(|map| map.syntax().text_range().start());
|
|
1880
|
+
|
|
1881
|
+
let sequence_position = sequence.syntax().text_range().start();
|
|
1882
|
+
|
|
1883
|
+
if map_position.is_none() || sequence_position <= map_position.unwrap() {
|
|
1884
|
+
let values: Vec<serde_yaml::Value> = sequence
|
|
1885
|
+
.entries()
|
|
1886
|
+
.map(|entry| node_to_yaml_value(entry.syntax()))
|
|
1887
|
+
.collect();
|
|
1888
|
+
|
|
1889
|
+
return serde_yaml::Value::Sequence(values);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
if let Some(map) = node.descendants().find_map(BlockMap::cast) {
|
|
1894
|
+
let mut mapping = serde_yaml::Mapping::new();
|
|
1895
|
+
|
|
1896
|
+
for entry in map.entries() {
|
|
1897
|
+
let key = entry
|
|
1898
|
+
.key()
|
|
1899
|
+
.and_then(|key_node| extract_scalar_text(key_node.syntax()))
|
|
1900
|
+
.unwrap_or_default();
|
|
1901
|
+
|
|
1902
|
+
let value = entry
|
|
1903
|
+
.value()
|
|
1904
|
+
.map(|value_node| node_to_yaml_value(value_node.syntax()))
|
|
1905
|
+
.unwrap_or(serde_yaml::Value::Null);
|
|
1906
|
+
|
|
1907
|
+
mapping.insert(serde_yaml::Value::String(key), value);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
return serde_yaml::Value::Mapping(mapping);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
1914
|
+
let values: Vec<serde_yaml::Value> = sequence
|
|
1915
|
+
.entries()
|
|
1916
|
+
.map(|entry| node_to_yaml_value(entry.syntax()))
|
|
1917
|
+
.collect();
|
|
1918
|
+
|
|
1919
|
+
return serde_yaml::Value::Sequence(values);
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
if let Some(block_scalar) = node
|
|
1923
|
+
.descendants()
|
|
1924
|
+
.find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR)
|
|
1925
|
+
{
|
|
1926
|
+
let text = block_scalar
|
|
1927
|
+
.descendants_with_tokens()
|
|
1928
|
+
.filter_map(|element| element.into_token())
|
|
1929
|
+
.find(|token| token.kind() == SyntaxKind::BLOCK_SCALAR_TEXT)
|
|
1930
|
+
.map(|token| token.text().to_string())
|
|
1931
|
+
.unwrap_or_default();
|
|
1932
|
+
|
|
1933
|
+
return serde_yaml::Value::String(text);
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
if let Some(scalar) = extract_scalar(node) {
|
|
1937
|
+
use crate::syntax::{detect_yaml_type, is_yaml_truthy, YerbaValueType};
|
|
1938
|
+
|
|
1939
|
+
return match detect_yaml_type(&scalar) {
|
|
1940
|
+
YerbaValueType::Null => serde_yaml::Value::Null,
|
|
1941
|
+
YerbaValueType::Boolean => serde_yaml::Value::Bool(is_yaml_truthy(&scalar.text)),
|
|
1942
|
+
|
|
1943
|
+
YerbaValueType::Integer => scalar
|
|
1944
|
+
.text
|
|
1945
|
+
.parse::<i64>()
|
|
1946
|
+
.map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
|
|
1947
|
+
.unwrap_or(serde_yaml::Value::String(scalar.text)),
|
|
1948
|
+
|
|
1949
|
+
YerbaValueType::Float => scalar
|
|
1950
|
+
.text
|
|
1951
|
+
.parse::<f64>()
|
|
1952
|
+
.map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
|
|
1953
|
+
.unwrap_or(serde_yaml::Value::String(scalar.text)),
|
|
1954
|
+
|
|
1955
|
+
YerbaValueType::String => serde_yaml::Value::String(scalar.text),
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
let text = node.text().to_string();
|
|
1960
|
+
|
|
1961
|
+
serde_yaml::from_str(&text).unwrap_or(serde_yaml::Value::String(text))
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1616
1964
|
fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
|
|
1617
1965
|
let (left, operator, right) = if let Some(index) = condition.find(" not_contains ") {
|
|
1618
1966
|
(
|
|
@@ -1639,140 +1987,250 @@ fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
|
|
|
1639
1987
|
Some((left.to_string(), operator, right.to_string()))
|
|
1640
1988
|
}
|
|
1641
1989
|
|
|
1642
|
-
fn
|
|
1643
|
-
|
|
1644
|
-
return Vec::new();
|
|
1645
|
-
}
|
|
1990
|
+
fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment) -> Vec<SyntaxNode> {
|
|
1991
|
+
use crate::selector::SelectorSegment;
|
|
1646
1992
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1993
|
+
match segment {
|
|
1994
|
+
SelectorSegment::AllItems => {
|
|
1995
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
1996
|
+
sequence.entries().map(|entry| entry.syntax().clone()).collect()
|
|
1997
|
+
} else {
|
|
1998
|
+
Vec::new()
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1649
2001
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
2002
|
+
SelectorSegment::Index(index) => {
|
|
2003
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
2004
|
+
sequence
|
|
2005
|
+
.entries()
|
|
2006
|
+
.nth(*index)
|
|
2007
|
+
.map(|entry| vec![entry.syntax().clone()])
|
|
2008
|
+
.unwrap_or_default()
|
|
2009
|
+
} else {
|
|
2010
|
+
Vec::new()
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
1655
2013
|
|
|
1656
|
-
|
|
1657
|
-
|
|
2014
|
+
SelectorSegment::Key(key) => {
|
|
2015
|
+
if let Some(map) = node.descendants().find_map(BlockMap::cast) {
|
|
2016
|
+
if let Some(entry) = find_entry_by_key(&map, key) {
|
|
2017
|
+
if let Some(value) = entry.value() {
|
|
2018
|
+
return vec![value.syntax().clone()];
|
|
2019
|
+
}
|
|
1658
2020
|
}
|
|
1659
|
-
} else {
|
|
1660
|
-
segments.push(rest);
|
|
1661
|
-
break;
|
|
1662
2021
|
}
|
|
1663
|
-
} else {
|
|
1664
|
-
let dot_index = rest.find('.');
|
|
1665
|
-
let bracket_index = rest.find('[');
|
|
1666
|
-
|
|
1667
|
-
let split_at = match (dot_index, bracket_index) {
|
|
1668
|
-
(Some(dot), Some(bracket)) => Some(dot.min(bracket)),
|
|
1669
|
-
(Some(dot), None) => Some(dot),
|
|
1670
|
-
(None, Some(bracket)) => Some(bracket),
|
|
1671
|
-
(None, None) => None,
|
|
1672
|
-
};
|
|
1673
2022
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
2023
|
+
Vec::new()
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
1677
2027
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
2028
|
+
fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNode> {
|
|
2029
|
+
let parsed = crate::selector::Selector::parse(path);
|
|
2030
|
+
let mut current_nodes = vec![node.clone()];
|
|
1681
2031
|
|
|
1682
|
-
|
|
2032
|
+
for segment in parsed.segments() {
|
|
2033
|
+
let mut next_nodes = Vec::new();
|
|
1683
2034
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
}
|
|
2035
|
+
for current in ¤t_nodes {
|
|
2036
|
+
next_nodes.extend(resolve_segment(current, segment));
|
|
2037
|
+
}
|
|
1688
2038
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
}
|
|
2039
|
+
current_nodes = next_nodes;
|
|
2040
|
+
|
|
2041
|
+
if current_nodes.is_empty() {
|
|
2042
|
+
break;
|
|
1694
2043
|
}
|
|
1695
2044
|
}
|
|
1696
2045
|
|
|
1697
|
-
|
|
2046
|
+
current_nodes
|
|
1698
2047
|
}
|
|
1699
2048
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2049
|
+
#[derive(Debug, Clone)]
|
|
2050
|
+
struct EntryGroup {
|
|
2051
|
+
separator: String,
|
|
2052
|
+
preceding: String,
|
|
2053
|
+
body: String,
|
|
2054
|
+
}
|
|
1704
2055
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
2056
|
+
impl EntryGroup {
|
|
2057
|
+
fn full_text(&self) -> String {
|
|
2058
|
+
if self.preceding.is_empty() {
|
|
2059
|
+
self.body.clone()
|
|
2060
|
+
} else {
|
|
2061
|
+
format!("{}\n{}", self.preceding, self.body)
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1709
2064
|
}
|
|
1710
2065
|
|
|
1711
|
-
fn
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
match parse_bracket_index(segment) {
|
|
1715
|
-
None => sequence.entries().map(|entry| entry.syntax().clone()).collect(),
|
|
2066
|
+
fn collect_entry_groups(parent: &SyntaxNode) -> Vec<EntryGroup> {
|
|
2067
|
+
let mut groups: Vec<EntryGroup> = Vec::new();
|
|
2068
|
+
let mut buffer = String::new();
|
|
1716
2069
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
2070
|
+
for child in parent.children_with_tokens() {
|
|
2071
|
+
let is_entry = child.as_node().is_some()
|
|
2072
|
+
&& matches!(
|
|
2073
|
+
child.as_node().unwrap().kind(),
|
|
2074
|
+
SyntaxKind::BLOCK_MAP_ENTRY | SyntaxKind::BLOCK_SEQ_ENTRY
|
|
2075
|
+
);
|
|
2076
|
+
|
|
2077
|
+
if is_entry {
|
|
2078
|
+
let entry_text = child.as_node().unwrap().text().to_string();
|
|
2079
|
+
|
|
2080
|
+
if groups.is_empty() {
|
|
2081
|
+
let preceding = buffer.trim_start_matches('\n').to_string();
|
|
2082
|
+
|
|
2083
|
+
groups.push(EntryGroup {
|
|
2084
|
+
separator: String::new(),
|
|
2085
|
+
preceding,
|
|
2086
|
+
body: entry_text,
|
|
2087
|
+
});
|
|
2088
|
+
} else {
|
|
2089
|
+
let (trailing, separator, preceding) = split_at_blank_line(&buffer);
|
|
2090
|
+
|
|
2091
|
+
if let Some(last) = groups.last_mut() {
|
|
2092
|
+
last.body.push_str(&trailing);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
groups.push(EntryGroup {
|
|
2096
|
+
separator,
|
|
2097
|
+
preceding,
|
|
2098
|
+
body: entry_text,
|
|
2099
|
+
});
|
|
1722
2100
|
}
|
|
2101
|
+
|
|
2102
|
+
buffer.clear();
|
|
1723
2103
|
} else {
|
|
1724
|
-
|
|
2104
|
+
let text = match &child {
|
|
2105
|
+
rowan::NodeOrToken::Node(node) => node.text().to_string(),
|
|
2106
|
+
rowan::NodeOrToken::Token(token) => token.text().to_string(),
|
|
2107
|
+
};
|
|
2108
|
+
|
|
2109
|
+
buffer.push_str(&text);
|
|
1725
2110
|
}
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
if let Some(last) = groups.last_mut() {
|
|
2114
|
+
let trimmed = buffer.trim_end_matches(['\n', ' ', '\t']);
|
|
2115
|
+
|
|
2116
|
+
if !trimmed.is_empty() {
|
|
2117
|
+
last.body.push_str(trimmed);
|
|
1733
2118
|
}
|
|
2119
|
+
}
|
|
1734
2120
|
|
|
1735
|
-
|
|
2121
|
+
for group in &mut groups {
|
|
2122
|
+
let trimmed = group.body.trim_end_matches(['\n', ' ', '\t']);
|
|
2123
|
+
|
|
2124
|
+
group.body = trimmed.to_string();
|
|
1736
2125
|
}
|
|
2126
|
+
|
|
2127
|
+
groups
|
|
1737
2128
|
}
|
|
1738
2129
|
|
|
1739
|
-
fn
|
|
1740
|
-
let
|
|
1741
|
-
|
|
2130
|
+
fn split_at_blank_line(text: &str) -> (String, String, String) {
|
|
2131
|
+
if let Some(position) = text.find("\n\n") {
|
|
2132
|
+
let trailing = text[..position].to_string();
|
|
2133
|
+
let rest = &text[position..];
|
|
2134
|
+
let content_start = rest.len() - rest.trim_start_matches('\n').len();
|
|
2135
|
+
let separator = rest[..content_start].to_string();
|
|
2136
|
+
let preceding = rest[content_start..].trim_end_matches(['\n', ' ', '\t']).to_string();
|
|
1742
2137
|
|
|
1743
|
-
|
|
1744
|
-
|
|
2138
|
+
(trailing, separator, preceding)
|
|
2139
|
+
} else {
|
|
2140
|
+
(text.to_string(), String::new(), String::new())
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
1745
2143
|
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2144
|
+
fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String, Option<rowan::TextSize>) {
|
|
2145
|
+
let mut comments: Vec<String> = Vec::new();
|
|
2146
|
+
let mut earliest_start = None;
|
|
2147
|
+
let mut node = parent.clone();
|
|
2148
|
+
|
|
2149
|
+
loop {
|
|
2150
|
+
let mut sibling = node.prev_sibling_or_token();
|
|
2151
|
+
|
|
2152
|
+
while let Some(ref element) = sibling {
|
|
2153
|
+
match element {
|
|
2154
|
+
rowan::NodeOrToken::Token(token) => {
|
|
2155
|
+
if token.kind() == SyntaxKind::COMMENT {
|
|
2156
|
+
comments.push(token.text().to_string());
|
|
2157
|
+
earliest_start = Some(token.text_range().start());
|
|
2158
|
+
} else if token.kind() == SyntaxKind::WHITESPACE {
|
|
2159
|
+
// Keep looking past whitespace
|
|
2160
|
+
} else {
|
|
2161
|
+
break;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
_ => break,
|
|
2165
|
+
}
|
|
1749
2166
|
|
|
1750
|
-
|
|
2167
|
+
sibling = match element {
|
|
2168
|
+
rowan::NodeOrToken::Token(token) => token.prev_sibling_or_token(),
|
|
2169
|
+
rowan::NodeOrToken::Node(node) => node.prev_sibling_or_token(),
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
1751
2172
|
|
|
1752
|
-
if
|
|
2173
|
+
if !comments.is_empty() {
|
|
1753
2174
|
break;
|
|
1754
2175
|
}
|
|
2176
|
+
|
|
2177
|
+
match node.parent() {
|
|
2178
|
+
Some(parent)
|
|
2179
|
+
if parent.kind() == SyntaxKind::BLOCK
|
|
2180
|
+
|| parent.kind() == SyntaxKind::DOCUMENT
|
|
2181
|
+
|| parent.kind() == SyntaxKind::BLOCK_MAP_VALUE
|
|
2182
|
+
|| parent.kind() == SyntaxKind::BLOCK_SEQ_ENTRY =>
|
|
2183
|
+
{
|
|
2184
|
+
node = parent
|
|
2185
|
+
}
|
|
2186
|
+
_ => break,
|
|
2187
|
+
}
|
|
1755
2188
|
}
|
|
1756
2189
|
|
|
1757
|
-
|
|
2190
|
+
comments.reverse();
|
|
2191
|
+
(comments.join("\n"), earliest_start)
|
|
1758
2192
|
}
|
|
1759
2193
|
|
|
1760
|
-
fn
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
2194
|
+
fn collect_groups_with_range(parent: &SyntaxNode) -> (Vec<EntryGroup>, TextRange) {
|
|
2195
|
+
let mut groups = collect_entry_groups(parent);
|
|
2196
|
+
|
|
2197
|
+
let (sibling_comments, earliest_start) = collect_preceding_sibling_comments(parent);
|
|
2198
|
+
|
|
2199
|
+
if !sibling_comments.is_empty() {
|
|
2200
|
+
if let Some(first) = groups.first_mut() {
|
|
2201
|
+
if first.preceding.is_empty() {
|
|
2202
|
+
first.preceding = sibling_comments;
|
|
2203
|
+
} else {
|
|
2204
|
+
first.preceding = format!("{}\n{}", sibling_comments, first.preceding);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
let range = match earliest_start {
|
|
2210
|
+
Some(start) => TextRange::new(start, parent.text_range().end()),
|
|
2211
|
+
None => parent.text_range(),
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
(groups, range)
|
|
1766
2215
|
}
|
|
1767
2216
|
|
|
1768
|
-
fn
|
|
1769
|
-
|
|
2217
|
+
fn rebuild_from_groups(groups: &[EntryGroup], indent: &str) -> String {
|
|
2218
|
+
let default_separator = groups
|
|
2219
|
+
.iter()
|
|
2220
|
+
.find(|group| !group.separator.is_empty())
|
|
2221
|
+
.map(|group| group.separator.clone())
|
|
2222
|
+
.unwrap_or_else(|| "\n".to_string());
|
|
2223
|
+
|
|
2224
|
+
groups
|
|
2225
|
+
.iter()
|
|
1770
2226
|
.enumerate()
|
|
1771
|
-
.map(|(index,
|
|
2227
|
+
.map(|(index, group)| {
|
|
1772
2228
|
if index == 0 {
|
|
1773
|
-
|
|
2229
|
+
group.full_text()
|
|
2230
|
+
} else if group.preceding.is_empty() {
|
|
2231
|
+
format!("{}{}{}", default_separator, indent, group.body)
|
|
1774
2232
|
} else {
|
|
1775
|
-
format!("\n{}{}", indent,
|
|
2233
|
+
format!("{}{}\n{}{}", default_separator, group.preceding, indent, group.body)
|
|
1776
2234
|
}
|
|
1777
2235
|
})
|
|
1778
2236
|
.collect()
|