yerba 0.7.2 → 0.7.4
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/ext/yerba/yerba.c +2 -0
- data/lib/yerba/map.rb +22 -0
- data/lib/yerba/node.rb +8 -2
- data/lib/yerba/sequence.rb +15 -0
- 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 +82 -36
- data/rust/src/document/get.rs +14 -17
- data/rust/src/document/insert.rs +50 -144
- data/rust/src/document/mod.rs +81 -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/ffi.rs +8 -0
- data/rust/src/selector.rs +3 -3
- data/rust/src/syntax.rs +63 -47
- metadata +1 -1
data/rust/src/document/insert.rs
CHANGED
|
@@ -60,10 +60,7 @@ impl Document {
|
|
|
60
60
|
let quote_style = self.detect_sequence_quote_style(dot_path);
|
|
61
61
|
let current_node = self.navigate(dot_path)?;
|
|
62
62
|
|
|
63
|
-
let sequence = current_node
|
|
64
|
-
.descendants()
|
|
65
|
-
.find_map(BlockSeq::cast)
|
|
66
|
-
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
63
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
67
64
|
|
|
68
65
|
let entries: Vec<_> = sequence.entries().collect();
|
|
69
66
|
|
|
@@ -81,38 +78,7 @@ impl Document {
|
|
|
81
78
|
|
|
82
79
|
for json_value in json_values {
|
|
83
80
|
let yaml_text = crate::yaml_writer::json_to_yaml_text(json_value, "e_style, 0);
|
|
84
|
-
|
|
85
|
-
let new_item = if yaml_text.contains('\n') {
|
|
86
|
-
let item_indent = format!("{} ", indent);
|
|
87
|
-
let lines: Vec<&str> = yaml_text.split('\n').collect();
|
|
88
|
-
|
|
89
|
-
let min_indent = lines
|
|
90
|
-
.iter()
|
|
91
|
-
.skip(1)
|
|
92
|
-
.filter(|line| !line.trim().is_empty())
|
|
93
|
-
.map(|line| line.len() - line.trim_start().len())
|
|
94
|
-
.min()
|
|
95
|
-
.unwrap_or(0);
|
|
96
|
-
|
|
97
|
-
let indented: Vec<String> = lines
|
|
98
|
-
.iter()
|
|
99
|
-
.enumerate()
|
|
100
|
-
.map(|(index, line)| {
|
|
101
|
-
if index == 0 {
|
|
102
|
-
line.to_string()
|
|
103
|
-
} else if line.trim().is_empty() {
|
|
104
|
-
String::new()
|
|
105
|
-
} else {
|
|
106
|
-
let relative = &line[min_indent..];
|
|
107
|
-
format!("{}{}", item_indent, relative)
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
.collect();
|
|
111
|
-
|
|
112
|
-
format!("- {}", indented.join("\n"))
|
|
113
|
-
} else {
|
|
114
|
-
format!("- {}", yaml_text)
|
|
115
|
-
};
|
|
81
|
+
let new_item = Self::format_sequence_item(&yaml_text, &indent);
|
|
116
82
|
|
|
117
83
|
new_text.push_str(&format!("\n{}{}", indent, new_item));
|
|
118
84
|
}
|
|
@@ -126,7 +92,7 @@ impl Document {
|
|
|
126
92
|
Self::validate_path(dot_path)?;
|
|
127
93
|
|
|
128
94
|
if let Ok(current_node) = self.navigate(dot_path) {
|
|
129
|
-
if current_node
|
|
95
|
+
if find_block_sequence(¤t_node).is_some()
|
|
130
96
|
|| matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty())
|
|
131
97
|
{
|
|
132
98
|
return self.insert_sequence_item(dot_path, value, position);
|
|
@@ -152,7 +118,7 @@ impl Document {
|
|
|
152
118
|
None => continue,
|
|
153
119
|
};
|
|
154
120
|
|
|
155
|
-
let map = match node
|
|
121
|
+
let map = match find_block_map(&node) {
|
|
156
122
|
Some(map) => map,
|
|
157
123
|
None => {
|
|
158
124
|
let has_empty_flow_map = node
|
|
@@ -186,42 +152,13 @@ impl Document {
|
|
|
186
152
|
|
|
187
153
|
let start_col = {
|
|
188
154
|
let offset: usize = first_entry.syntax().text_range().start().into();
|
|
189
|
-
let source = self.
|
|
190
|
-
let before = &source[..offset];
|
|
155
|
+
let source = self.source_text();
|
|
191
156
|
|
|
192
|
-
|
|
157
|
+
column_at(&source, offset)
|
|
193
158
|
};
|
|
194
159
|
|
|
195
160
|
let indent = " ".repeat(start_col);
|
|
196
|
-
|
|
197
|
-
let is_block_value = value.contains('\n') || value.starts_with("- ");
|
|
198
|
-
let new_entry_text = if is_block_value {
|
|
199
|
-
let value_indent = format!("{} ", indent);
|
|
200
|
-
let lines: Vec<&str> = value.lines().collect();
|
|
201
|
-
|
|
202
|
-
let min_indent = lines
|
|
203
|
-
.iter()
|
|
204
|
-
.filter(|line| !line.trim().is_empty())
|
|
205
|
-
.map(|line| line.len() - line.trim_start().len())
|
|
206
|
-
.min()
|
|
207
|
-
.unwrap_or(0);
|
|
208
|
-
|
|
209
|
-
let indented_lines: Vec<String> = lines
|
|
210
|
-
.iter()
|
|
211
|
-
.map(|line| {
|
|
212
|
-
if line.trim().is_empty() {
|
|
213
|
-
String::new()
|
|
214
|
-
} else {
|
|
215
|
-
let relative = &line[min_indent..];
|
|
216
|
-
format!("{}{}", value_indent, relative)
|
|
217
|
-
}
|
|
218
|
-
})
|
|
219
|
-
.collect();
|
|
220
|
-
|
|
221
|
-
format!("{}:\n{}", key, indented_lines.join("\n"))
|
|
222
|
-
} else {
|
|
223
|
-
format!("{}: {}", key, value)
|
|
224
|
-
};
|
|
161
|
+
let new_entry_text = Self::format_map_entry(key, value, &indent);
|
|
225
162
|
|
|
226
163
|
match &position {
|
|
227
164
|
InsertPosition::After(target_key) => {
|
|
@@ -265,7 +202,7 @@ impl Document {
|
|
|
265
202
|
fn insert_sequence_item(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
|
|
266
203
|
let current_node = self.navigate(dot_path)?;
|
|
267
204
|
|
|
268
|
-
let Some(sequence) = current_node
|
|
205
|
+
let Some(sequence) = find_block_sequence(¤t_node) else {
|
|
269
206
|
if matches!(self.get_value(dot_path).as_ref(), Some(yaml_serde::Value::Sequence(sequence)) if sequence.is_empty()) {
|
|
270
207
|
return self.replace_empty_inline_sequence(dot_path, value);
|
|
271
208
|
}
|
|
@@ -285,37 +222,7 @@ impl Document {
|
|
|
285
222
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
286
223
|
.unwrap_or_default();
|
|
287
224
|
|
|
288
|
-
let new_item =
|
|
289
|
-
let item_indent = format!("{} ", indent);
|
|
290
|
-
let lines: Vec<&str> = value.split('\n').collect();
|
|
291
|
-
|
|
292
|
-
let min_indent = lines
|
|
293
|
-
.iter()
|
|
294
|
-
.skip(1)
|
|
295
|
-
.filter(|line| !line.trim().is_empty())
|
|
296
|
-
.map(|line| line.len() - line.trim_start().len())
|
|
297
|
-
.min()
|
|
298
|
-
.unwrap_or(0);
|
|
299
|
-
|
|
300
|
-
let indented: Vec<String> = lines
|
|
301
|
-
.iter()
|
|
302
|
-
.enumerate()
|
|
303
|
-
.map(|(index, line)| {
|
|
304
|
-
if index == 0 {
|
|
305
|
-
line.to_string()
|
|
306
|
-
} else if line.trim().is_empty() {
|
|
307
|
-
String::new()
|
|
308
|
-
} else {
|
|
309
|
-
let relative = &line[min_indent..];
|
|
310
|
-
format!("{}{}", item_indent, relative)
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
.collect();
|
|
314
|
-
|
|
315
|
-
format!("- {}", indented.join("\n"))
|
|
316
|
-
} else {
|
|
317
|
-
format!("- {}", value)
|
|
318
|
-
};
|
|
225
|
+
let new_item = Self::format_sequence_item(value, &indent);
|
|
319
226
|
|
|
320
227
|
match position {
|
|
321
228
|
InsertPosition::Last => {
|
|
@@ -413,7 +320,7 @@ impl Document {
|
|
|
413
320
|
fn insert_map_key(&mut self, dot_path: &str, key: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
|
|
414
321
|
let current_node = self.navigate(dot_path)?;
|
|
415
322
|
|
|
416
|
-
let map = match current_node
|
|
323
|
+
let map = match find_block_map(¤t_node) {
|
|
417
324
|
Some(map) => map,
|
|
418
325
|
None => {
|
|
419
326
|
let flow_map = current_node.descendants().find(|descendant| descendant.kind() == SyntaxKind::FLOW_MAP);
|
|
@@ -449,34 +356,7 @@ impl Document {
|
|
|
449
356
|
.map(|entry| preceding_whitespace_indent(entry.syntax()))
|
|
450
357
|
.unwrap_or_default();
|
|
451
358
|
|
|
452
|
-
let
|
|
453
|
-
let new_entry_text = if is_block_value {
|
|
454
|
-
let value_indent = format!("{} ", indent);
|
|
455
|
-
let lines: Vec<&str> = value.lines().collect();
|
|
456
|
-
|
|
457
|
-
let min_indent = lines
|
|
458
|
-
.iter()
|
|
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_lines: Vec<String> = lines
|
|
465
|
-
.iter()
|
|
466
|
-
.map(|line| {
|
|
467
|
-
if line.trim().is_empty() {
|
|
468
|
-
String::new()
|
|
469
|
-
} else {
|
|
470
|
-
let relative = &line[min_indent..];
|
|
471
|
-
format!("{}{}", value_indent, relative)
|
|
472
|
-
}
|
|
473
|
-
})
|
|
474
|
-
.collect();
|
|
475
|
-
|
|
476
|
-
format!("{}:\n{}", key, indented_lines.join("\n"))
|
|
477
|
-
} else {
|
|
478
|
-
format!("{}: {}", key, value)
|
|
479
|
-
};
|
|
359
|
+
let new_entry_text = Self::format_map_entry(key, value, &indent);
|
|
480
360
|
|
|
481
361
|
match position {
|
|
482
362
|
InsertPosition::Last => {
|
|
@@ -561,7 +441,7 @@ impl Document {
|
|
|
561
441
|
|
|
562
442
|
let range = flow_map.text_range();
|
|
563
443
|
|
|
564
|
-
let source = self.
|
|
444
|
+
let source = self.source_text();
|
|
565
445
|
let start: usize = range.start().into();
|
|
566
446
|
let before = &source[..start];
|
|
567
447
|
let trimmed_length = before.trim_end_matches([' ', '\n']).len();
|
|
@@ -575,10 +455,7 @@ impl Document {
|
|
|
575
455
|
let (parent_path, map_key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
|
|
576
456
|
let parent_node = self.navigate(parent_path)?;
|
|
577
457
|
|
|
578
|
-
let map = parent_node
|
|
579
|
-
.descendants()
|
|
580
|
-
.find_map(BlockMap::cast)
|
|
581
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
458
|
+
let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
582
459
|
|
|
583
460
|
let entry = find_entry_by_key(&map, map_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
584
461
|
let entry_indent = preceding_whitespace_indent(entry.syntax());
|
|
@@ -601,14 +478,14 @@ impl Document {
|
|
|
601
478
|
fn replace_empty_inline_sequence(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
|
602
479
|
if dot_path.is_empty() {
|
|
603
480
|
let current_node = self.navigate(dot_path)?;
|
|
604
|
-
let
|
|
481
|
+
let flow_sequence = current_node
|
|
605
482
|
.descendants()
|
|
606
483
|
.find(|descendant| descendant.kind() == SyntaxKind::FLOW_SEQ)
|
|
607
484
|
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
608
485
|
|
|
609
486
|
let new_item = Self::format_sequence_item(value, "");
|
|
610
|
-
let range =
|
|
611
|
-
let source = self.
|
|
487
|
+
let range = flow_sequence.text_range();
|
|
488
|
+
let source = self.source_text();
|
|
612
489
|
let start: usize = range.start().into();
|
|
613
490
|
let before = &source[..start];
|
|
614
491
|
|
|
@@ -624,10 +501,7 @@ impl Document {
|
|
|
624
501
|
let (parent_path, key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
|
|
625
502
|
let parent_node = self.navigate(parent_path)?;
|
|
626
503
|
|
|
627
|
-
let map = parent_node
|
|
628
|
-
.descendants()
|
|
629
|
-
.find_map(BlockMap::cast)
|
|
630
|
-
.ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
504
|
+
let map = find_block_map(&parent_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
|
|
631
505
|
|
|
632
506
|
let entry = find_entry_by_key(&map, key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
633
507
|
let item_indent = format!("{} ", preceding_whitespace_indent(entry.syntax()));
|
|
@@ -656,7 +530,7 @@ impl Document {
|
|
|
656
530
|
}
|
|
657
531
|
|
|
658
532
|
fn trailing_inline_comment(&self, node: &SyntaxNode) -> Option<(String, rowan::TextSize)> {
|
|
659
|
-
let source = self.
|
|
533
|
+
let source = self.source_text();
|
|
660
534
|
let start: usize = node.text_range().end().into();
|
|
661
535
|
let rest = &source[start..];
|
|
662
536
|
let line_end = rest.find('\n').unwrap_or(rest.len());
|
|
@@ -670,6 +544,38 @@ impl Document {
|
|
|
670
544
|
Some((trailing[comment_start..].to_string(), rowan::TextSize::from((start + line_end) as u32)))
|
|
671
545
|
}
|
|
672
546
|
|
|
547
|
+
fn format_map_entry(key: &str, value: &str, indent: &str) -> String {
|
|
548
|
+
let is_block_value = value.contains('\n') || value.starts_with("- ");
|
|
549
|
+
|
|
550
|
+
if !is_block_value {
|
|
551
|
+
return format!("{}: {}", key, value);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
let value_indent = format!("{} ", indent);
|
|
555
|
+
let lines: Vec<&str> = value.lines().collect();
|
|
556
|
+
|
|
557
|
+
let min_indent = lines
|
|
558
|
+
.iter()
|
|
559
|
+
.filter(|line| !line.trim().is_empty())
|
|
560
|
+
.map(|line| line.len() - line.trim_start().len())
|
|
561
|
+
.min()
|
|
562
|
+
.unwrap_or(0);
|
|
563
|
+
|
|
564
|
+
let indented_lines: Vec<String> = lines
|
|
565
|
+
.iter()
|
|
566
|
+
.map(|line| {
|
|
567
|
+
if line.trim().is_empty() {
|
|
568
|
+
String::new()
|
|
569
|
+
} else {
|
|
570
|
+
let relative = &line[min_indent..];
|
|
571
|
+
format!("{}{}", value_indent, relative)
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
.collect();
|
|
575
|
+
|
|
576
|
+
format!("{}:\n{}", key, indented_lines.join("\n"))
|
|
577
|
+
}
|
|
578
|
+
|
|
673
579
|
fn format_sequence_item(value: &str, indent: &str) -> String {
|
|
674
580
|
if value.contains('\n') {
|
|
675
581
|
let item_indent = format!("{} ", indent);
|
data/rust/src/document/mod.rs
CHANGED
|
@@ -19,6 +19,7 @@ pub struct LocatedNode {
|
|
|
19
19
|
pub file_path: Option<String>,
|
|
20
20
|
pub selector: String,
|
|
21
21
|
pub line: usize,
|
|
22
|
+
pub location: Location,
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
use std::fs;
|
|
@@ -34,8 +35,9 @@ use crate::error::YerbaError;
|
|
|
34
35
|
use crate::QuoteStyle;
|
|
35
36
|
|
|
36
37
|
use crate::syntax::{
|
|
37
|
-
dedent_block_scalar, extract_scalar, extract_scalar_text,
|
|
38
|
-
|
|
38
|
+
column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_scalar_token,
|
|
39
|
+
first_collection, format_scalar_value, is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token,
|
|
40
|
+
raw_scalar_value, removal_range, FirstCollection, ScalarValue,
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
#[derive(Debug, Clone)]
|
|
@@ -148,13 +150,13 @@ impl Document {
|
|
|
148
150
|
.as_ref()
|
|
149
151
|
.ok_or_else(|| YerbaError::IoError(std::io::Error::new(std::io::ErrorKind::NotFound, "no file path associated with this document")))?;
|
|
150
152
|
|
|
151
|
-
fs::write(path, self.
|
|
153
|
+
fs::write(path, self.source_text())?;
|
|
152
154
|
|
|
153
155
|
Ok(())
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
pub fn save_to(&self, path: impl AsRef<Path>) -> Result<(), YerbaError> {
|
|
157
|
-
fs::write(path, self.
|
|
159
|
+
fs::write(path, self.source_text())?;
|
|
158
160
|
|
|
159
161
|
Ok(())
|
|
160
162
|
}
|
|
@@ -208,7 +210,7 @@ impl Document {
|
|
|
208
210
|
let mut current_nodes: Vec<Option<SyntaxNode>> = vec![Some(document.syntax().clone())];
|
|
209
211
|
|
|
210
212
|
if parsed.is_empty() {
|
|
211
|
-
if let Some(sequence) = document.syntax()
|
|
213
|
+
if let Some(sequence) = find_block_sequence(document.syntax()) {
|
|
212
214
|
current_nodes = sequence.entries().map(|entry| Some(entry.syntax().clone())).collect();
|
|
213
215
|
}
|
|
214
216
|
|
|
@@ -296,7 +298,20 @@ impl Document {
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
fn insert_after_node(&mut self, node: &SyntaxNode, text: &str) -> Result<(), YerbaError> {
|
|
299
|
-
let
|
|
301
|
+
let end: usize = node.text_range().end().into();
|
|
302
|
+
let source = self.source_text();
|
|
303
|
+
|
|
304
|
+
let rest = &source[end..];
|
|
305
|
+
let line_end = rest.find('\n').unwrap_or(rest.len());
|
|
306
|
+
let trailing = &rest[..line_end];
|
|
307
|
+
|
|
308
|
+
let position = if let Some(comment_start) = trailing.find('#') {
|
|
309
|
+
let _ = comment_start;
|
|
310
|
+
rowan::TextSize::from((end + line_end) as u32)
|
|
311
|
+
} else {
|
|
312
|
+
node.text_range().end()
|
|
313
|
+
};
|
|
314
|
+
|
|
300
315
|
let range = TextRange::new(position, position);
|
|
301
316
|
|
|
302
317
|
self.apply_edit(range, text)
|
|
@@ -412,15 +427,37 @@ impl Document {
|
|
|
412
427
|
}
|
|
413
428
|
|
|
414
429
|
fn apply_edit(&mut self, range: TextRange, replacement: &str) -> Result<(), YerbaError> {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
430
|
+
self.apply_edits(vec![(range, replacement.to_string())])
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
fn apply_edits(&mut self, mut edits: Vec<(TextRange, String)>) -> Result<(), YerbaError> {
|
|
434
|
+
if edits.is_empty() {
|
|
435
|
+
return Ok(());
|
|
436
|
+
}
|
|
418
437
|
|
|
419
|
-
|
|
420
|
-
new_source.replace_range(start..end, replacement);
|
|
438
|
+
edits.sort_by_key(|(range, _)| std::cmp::Reverse(range.start()));
|
|
421
439
|
|
|
440
|
+
let mut new_source = self.source_text();
|
|
441
|
+
|
|
442
|
+
for (range, replacement) in edits {
|
|
443
|
+
let start: usize = range.start().into();
|
|
444
|
+
let end: usize = range.end().into();
|
|
445
|
+
|
|
446
|
+
new_source.replace_range(start..end, &replacement);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
self.reparse(&new_source)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
fn source_text(&self) -> String {
|
|
453
|
+
self.root.text().to_string()
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
fn reparse(&mut self, new_source: &str) -> Result<(), YerbaError> {
|
|
457
|
+
let document = Self::parse(new_source)?;
|
|
422
458
|
let path = self.path.take();
|
|
423
|
-
|
|
459
|
+
|
|
460
|
+
*self = document;
|
|
424
461
|
self.path = path;
|
|
425
462
|
|
|
426
463
|
Ok(())
|
|
@@ -445,8 +482,8 @@ fn check_duplicate_keys(root: &SyntaxNode) -> Result<(), YerbaError> {
|
|
|
445
482
|
|
|
446
483
|
if let Some(&first_offset) = seen.get(&key_text) {
|
|
447
484
|
let source = root.text().to_string();
|
|
448
|
-
let first_line = source
|
|
449
|
-
let duplicate_line = source
|
|
485
|
+
let first_line = line_at(&source, first_offset.into());
|
|
486
|
+
let duplicate_line = line_at(&source, offset.into());
|
|
450
487
|
let line_content = source.lines().nth(duplicate_line - 1).unwrap_or("").to_string();
|
|
451
488
|
|
|
452
489
|
return Err(YerbaError::DuplicateKey {
|
|
@@ -471,21 +508,13 @@ pub(crate) fn compute_location(source: &str, start_offset: usize, end_offset: us
|
|
|
471
508
|
let start = start_offset.min(source.len());
|
|
472
509
|
let end = end_offset.min(source.len());
|
|
473
510
|
|
|
474
|
-
let before_start = &source[..start];
|
|
475
|
-
let start_line = before_start.chars().filter(|c| *c == '\n').count() + 1;
|
|
476
|
-
let start_column = start - before_start.rfind('\n').map(|p| p + 1).unwrap_or(0);
|
|
477
|
-
|
|
478
|
-
let before_end = &source[..end];
|
|
479
|
-
let end_line = before_end.chars().filter(|c| *c == '\n').count() + 1;
|
|
480
|
-
let end_column = end - before_end.rfind('\n').map(|p| p + 1).unwrap_or(0);
|
|
481
|
-
|
|
482
511
|
Location {
|
|
483
512
|
start_offset: start,
|
|
484
513
|
end_offset: end,
|
|
485
|
-
start_line,
|
|
486
|
-
start_column,
|
|
487
|
-
end_line,
|
|
488
|
-
end_column,
|
|
514
|
+
start_line: line_at(source, start),
|
|
515
|
+
start_column: column_at(source, start),
|
|
516
|
+
end_line: line_at(source, end),
|
|
517
|
+
end_column: column_at(source, end),
|
|
489
518
|
}
|
|
490
519
|
}
|
|
491
520
|
|
|
@@ -520,39 +549,31 @@ pub fn collect_selectors(value: &yaml_serde::Value, prefix: &str, selectors: &mu
|
|
|
520
549
|
}
|
|
521
550
|
|
|
522
551
|
pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
let sequence_position = sequence.syntax().text_range().start();
|
|
527
|
-
|
|
528
|
-
if map_position.is_none() || sequence_position <= map_position.unwrap() {
|
|
552
|
+
match first_collection(node) {
|
|
553
|
+
Some(FirstCollection::Sequence(sequence)) => {
|
|
529
554
|
let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
|
|
530
555
|
|
|
531
556
|
return yaml_serde::Value::Sequence(values);
|
|
532
557
|
}
|
|
533
|
-
}
|
|
534
558
|
|
|
535
|
-
|
|
536
|
-
|
|
559
|
+
Some(FirstCollection::Map(map)) => {
|
|
560
|
+
let mut mapping = yaml_serde::Mapping::new();
|
|
537
561
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
let value = entry
|
|
542
|
-
.value()
|
|
543
|
-
.map(|value_node| node_to_yaml_value(value_node.syntax()))
|
|
544
|
-
.unwrap_or(yaml_serde::Value::Null);
|
|
562
|
+
for entry in map.entries() {
|
|
563
|
+
let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
|
|
545
564
|
|
|
546
|
-
|
|
547
|
-
|
|
565
|
+
let value = entry
|
|
566
|
+
.value()
|
|
567
|
+
.map(|value_node| node_to_yaml_value(value_node.syntax()))
|
|
568
|
+
.unwrap_or(yaml_serde::Value::Null);
|
|
548
569
|
|
|
549
|
-
|
|
550
|
-
|
|
570
|
+
mapping.insert(yaml_serde::Value::String(key), value);
|
|
571
|
+
}
|
|
551
572
|
|
|
552
|
-
|
|
553
|
-
|
|
573
|
+
return yaml_serde::Value::Mapping(mapping);
|
|
574
|
+
}
|
|
554
575
|
|
|
555
|
-
|
|
576
|
+
None => {}
|
|
556
577
|
}
|
|
557
578
|
|
|
558
579
|
if let Some(block_scalar) = node.descendants().find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR) {
|
|
@@ -608,17 +629,13 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
|
|
|
608
629
|
}
|
|
609
630
|
|
|
610
631
|
pub(crate) fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
(condition[..index].trim(), "==", condition[index + 2..].trim())
|
|
619
|
-
} else {
|
|
620
|
-
return None;
|
|
621
|
-
};
|
|
632
|
+
const OPERATORS: [(&str, &str); 4] = [(" not_contains ", "not_contains"), (" contains ", "contains"), ("!=", "!="), ("==", "==")];
|
|
633
|
+
|
|
634
|
+
let (left, operator, right) = OPERATORS.iter().find_map(|(pattern, operator)| {
|
|
635
|
+
condition
|
|
636
|
+
.find(pattern)
|
|
637
|
+
.map(|index| (condition[..index].trim(), *operator, condition[index + pattern.len()..].trim()))
|
|
638
|
+
})?;
|
|
622
639
|
|
|
623
640
|
let right = right
|
|
624
641
|
.trim_start_matches('"')
|
|
@@ -654,7 +671,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
654
671
|
|
|
655
672
|
match segment {
|
|
656
673
|
SelectorSegment::AllItems => {
|
|
657
|
-
if let Some(sequence) = node
|
|
674
|
+
if let Some(sequence) = find_block_sequence(node) {
|
|
658
675
|
sequence.entries().map(|entry| entry.syntax().clone()).collect()
|
|
659
676
|
} else {
|
|
660
677
|
Vec::new()
|
|
@@ -662,7 +679,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
662
679
|
}
|
|
663
680
|
|
|
664
681
|
SelectorSegment::Index(index) => {
|
|
665
|
-
if let Some(sequence) = node
|
|
682
|
+
if let Some(sequence) = find_block_sequence(node) {
|
|
666
683
|
sequence.entries().nth(*index).map(|entry| vec![entry.syntax().clone()]).unwrap_or_default()
|
|
667
684
|
} else {
|
|
668
685
|
Vec::new()
|
|
@@ -670,7 +687,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
670
687
|
}
|
|
671
688
|
|
|
672
689
|
SelectorSegment::Key(key) => {
|
|
673
|
-
if let Some(map) = node
|
|
690
|
+
if let Some(map) = find_block_map(node) {
|
|
674
691
|
if let Some(entry) = find_entry_by_key(&map, key) {
|
|
675
692
|
if let Some(value) = entry.value() {
|
|
676
693
|
return vec![value.syntax().clone()];
|
|
@@ -685,23 +702,8 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
685
702
|
|
|
686
703
|
pub(crate) fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNode> {
|
|
687
704
|
let parsed = crate::selector::Selector::parse(path);
|
|
688
|
-
let mut current_nodes = vec![node.clone()];
|
|
689
|
-
|
|
690
|
-
for segment in parsed.segments() {
|
|
691
|
-
let mut next_nodes = Vec::new();
|
|
692
705
|
|
|
693
|
-
|
|
694
|
-
next_nodes.extend(resolve_segment(current, segment));
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
current_nodes = next_nodes;
|
|
698
|
-
|
|
699
|
-
if current_nodes.is_empty() {
|
|
700
|
-
break;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
current_nodes
|
|
706
|
+
navigate_remaining(node, parsed.segments())
|
|
705
707
|
}
|
|
706
708
|
|
|
707
709
|
#[derive(Debug, Clone)]
|