yerba 0.7.1-x86_64-linux-gnu → 0.7.3-x86_64-linux-gnu
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/exe/x86_64-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +5 -0
- data/ext/yerba/yerba.c +16 -0
- data/lib/yerba/3.2/yerba.so +0 -0
- data/lib/yerba/3.3/yerba.so +0 -0
- data/lib/yerba/3.4/yerba.so +0 -0
- data/lib/yerba/4.0/yerba.so +0 -0
- data/lib/yerba/document.rb +21 -1
- data/lib/yerba/map.rb +4 -0
- data/lib/yerba/node.rb +6 -0
- data/lib/yerba/sequence.rb +4 -0
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba.rb +1 -0
- data/rust/Cargo.lock +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 +85 -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 +12 -0
- data/rust/src/selector.rs +3 -3
- data/rust/src/syntax.rs +63 -47
- metadata +2 -2
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
|
@@ -34,8 +34,9 @@ use crate::error::YerbaError;
|
|
|
34
34
|
use crate::QuoteStyle;
|
|
35
35
|
|
|
36
36
|
use crate::syntax::{
|
|
37
|
-
dedent_block_scalar, extract_scalar, extract_scalar_text,
|
|
38
|
-
|
|
37
|
+
column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_scalar_token,
|
|
38
|
+
first_collection, format_scalar_value, is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token,
|
|
39
|
+
raw_scalar_value, removal_range, FirstCollection, ScalarValue,
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
#[derive(Debug, Clone)]
|
|
@@ -148,17 +149,22 @@ impl Document {
|
|
|
148
149
|
.as_ref()
|
|
149
150
|
.ok_or_else(|| YerbaError::IoError(std::io::Error::new(std::io::ErrorKind::NotFound, "no file path associated with this document")))?;
|
|
150
151
|
|
|
151
|
-
fs::write(path, self.
|
|
152
|
+
fs::write(path, self.source_text())?;
|
|
152
153
|
|
|
153
154
|
Ok(())
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
pub fn save_to(&self, path: impl AsRef<Path>) -> Result<(), YerbaError> {
|
|
157
|
-
fs::write(path, self.
|
|
158
|
+
fs::write(path, self.source_text())?;
|
|
158
159
|
|
|
159
160
|
Ok(())
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
pub fn source(&self, dot_path: &str) -> Result<String, YerbaError> {
|
|
164
|
+
let node = self.navigate(dot_path)?;
|
|
165
|
+
Ok(node.text().to_string())
|
|
166
|
+
}
|
|
167
|
+
|
|
162
168
|
pub fn navigate(&self, dot_path: &str) -> Result<SyntaxNode, YerbaError> {
|
|
163
169
|
Self::validate_path(dot_path)?;
|
|
164
170
|
|
|
@@ -203,7 +209,7 @@ impl Document {
|
|
|
203
209
|
let mut current_nodes: Vec<Option<SyntaxNode>> = vec![Some(document.syntax().clone())];
|
|
204
210
|
|
|
205
211
|
if parsed.is_empty() {
|
|
206
|
-
if let Some(sequence) = document.syntax()
|
|
212
|
+
if let Some(sequence) = find_block_sequence(document.syntax()) {
|
|
207
213
|
current_nodes = sequence.entries().map(|entry| Some(entry.syntax().clone())).collect();
|
|
208
214
|
}
|
|
209
215
|
|
|
@@ -291,7 +297,20 @@ impl Document {
|
|
|
291
297
|
}
|
|
292
298
|
|
|
293
299
|
fn insert_after_node(&mut self, node: &SyntaxNode, text: &str) -> Result<(), YerbaError> {
|
|
294
|
-
let
|
|
300
|
+
let end: usize = node.text_range().end().into();
|
|
301
|
+
let source = self.source_text();
|
|
302
|
+
|
|
303
|
+
let rest = &source[end..];
|
|
304
|
+
let line_end = rest.find('\n').unwrap_or(rest.len());
|
|
305
|
+
let trailing = &rest[..line_end];
|
|
306
|
+
|
|
307
|
+
let position = if let Some(comment_start) = trailing.find('#') {
|
|
308
|
+
let _ = comment_start;
|
|
309
|
+
rowan::TextSize::from((end + line_end) as u32)
|
|
310
|
+
} else {
|
|
311
|
+
node.text_range().end()
|
|
312
|
+
};
|
|
313
|
+
|
|
295
314
|
let range = TextRange::new(position, position);
|
|
296
315
|
|
|
297
316
|
self.apply_edit(range, text)
|
|
@@ -407,15 +426,37 @@ impl Document {
|
|
|
407
426
|
}
|
|
408
427
|
|
|
409
428
|
fn apply_edit(&mut self, range: TextRange, replacement: &str) -> Result<(), YerbaError> {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
429
|
+
self.apply_edits(vec![(range, replacement.to_string())])
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
fn apply_edits(&mut self, mut edits: Vec<(TextRange, String)>) -> Result<(), YerbaError> {
|
|
433
|
+
if edits.is_empty() {
|
|
434
|
+
return Ok(());
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
edits.sort_by_key(|(range, _)| std::cmp::Reverse(range.start()));
|
|
438
|
+
|
|
439
|
+
let mut new_source = self.source_text();
|
|
413
440
|
|
|
414
|
-
|
|
415
|
-
|
|
441
|
+
for (range, replacement) in edits {
|
|
442
|
+
let start: usize = range.start().into();
|
|
443
|
+
let end: usize = range.end().into();
|
|
416
444
|
|
|
445
|
+
new_source.replace_range(start..end, &replacement);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
self.reparse(&new_source)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
fn source_text(&self) -> String {
|
|
452
|
+
self.root.text().to_string()
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
fn reparse(&mut self, new_source: &str) -> Result<(), YerbaError> {
|
|
456
|
+
let document = Self::parse(new_source)?;
|
|
417
457
|
let path = self.path.take();
|
|
418
|
-
|
|
458
|
+
|
|
459
|
+
*self = document;
|
|
419
460
|
self.path = path;
|
|
420
461
|
|
|
421
462
|
Ok(())
|
|
@@ -440,8 +481,8 @@ fn check_duplicate_keys(root: &SyntaxNode) -> Result<(), YerbaError> {
|
|
|
440
481
|
|
|
441
482
|
if let Some(&first_offset) = seen.get(&key_text) {
|
|
442
483
|
let source = root.text().to_string();
|
|
443
|
-
let first_line = source
|
|
444
|
-
let duplicate_line = source
|
|
484
|
+
let first_line = line_at(&source, first_offset.into());
|
|
485
|
+
let duplicate_line = line_at(&source, offset.into());
|
|
445
486
|
let line_content = source.lines().nth(duplicate_line - 1).unwrap_or("").to_string();
|
|
446
487
|
|
|
447
488
|
return Err(YerbaError::DuplicateKey {
|
|
@@ -466,21 +507,13 @@ pub(crate) fn compute_location(source: &str, start_offset: usize, end_offset: us
|
|
|
466
507
|
let start = start_offset.min(source.len());
|
|
467
508
|
let end = end_offset.min(source.len());
|
|
468
509
|
|
|
469
|
-
let before_start = &source[..start];
|
|
470
|
-
let start_line = before_start.chars().filter(|c| *c == '\n').count() + 1;
|
|
471
|
-
let start_column = start - before_start.rfind('\n').map(|p| p + 1).unwrap_or(0);
|
|
472
|
-
|
|
473
|
-
let before_end = &source[..end];
|
|
474
|
-
let end_line = before_end.chars().filter(|c| *c == '\n').count() + 1;
|
|
475
|
-
let end_column = end - before_end.rfind('\n').map(|p| p + 1).unwrap_or(0);
|
|
476
|
-
|
|
477
510
|
Location {
|
|
478
511
|
start_offset: start,
|
|
479
512
|
end_offset: end,
|
|
480
|
-
start_line,
|
|
481
|
-
start_column,
|
|
482
|
-
end_line,
|
|
483
|
-
end_column,
|
|
513
|
+
start_line: line_at(source, start),
|
|
514
|
+
start_column: column_at(source, start),
|
|
515
|
+
end_line: line_at(source, end),
|
|
516
|
+
end_column: column_at(source, end),
|
|
484
517
|
}
|
|
485
518
|
}
|
|
486
519
|
|
|
@@ -515,39 +548,31 @@ pub fn collect_selectors(value: &yaml_serde::Value, prefix: &str, selectors: &mu
|
|
|
515
548
|
}
|
|
516
549
|
|
|
517
550
|
pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
let sequence_position = sequence.syntax().text_range().start();
|
|
522
|
-
|
|
523
|
-
if map_position.is_none() || sequence_position <= map_position.unwrap() {
|
|
551
|
+
match first_collection(node) {
|
|
552
|
+
Some(FirstCollection::Sequence(sequence)) => {
|
|
524
553
|
let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
|
|
525
554
|
|
|
526
555
|
return yaml_serde::Value::Sequence(values);
|
|
527
556
|
}
|
|
528
|
-
}
|
|
529
557
|
|
|
530
|
-
|
|
531
|
-
|
|
558
|
+
Some(FirstCollection::Map(map)) => {
|
|
559
|
+
let mut mapping = yaml_serde::Mapping::new();
|
|
532
560
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
let value = entry
|
|
537
|
-
.value()
|
|
538
|
-
.map(|value_node| node_to_yaml_value(value_node.syntax()))
|
|
539
|
-
.unwrap_or(yaml_serde::Value::Null);
|
|
561
|
+
for entry in map.entries() {
|
|
562
|
+
let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
|
|
540
563
|
|
|
541
|
-
|
|
542
|
-
|
|
564
|
+
let value = entry
|
|
565
|
+
.value()
|
|
566
|
+
.map(|value_node| node_to_yaml_value(value_node.syntax()))
|
|
567
|
+
.unwrap_or(yaml_serde::Value::Null);
|
|
543
568
|
|
|
544
|
-
|
|
545
|
-
|
|
569
|
+
mapping.insert(yaml_serde::Value::String(key), value);
|
|
570
|
+
}
|
|
546
571
|
|
|
547
|
-
|
|
548
|
-
|
|
572
|
+
return yaml_serde::Value::Mapping(mapping);
|
|
573
|
+
}
|
|
549
574
|
|
|
550
|
-
|
|
575
|
+
None => {}
|
|
551
576
|
}
|
|
552
577
|
|
|
553
578
|
if let Some(block_scalar) = node.descendants().find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR) {
|
|
@@ -603,17 +628,13 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
|
|
|
603
628
|
}
|
|
604
629
|
|
|
605
630
|
pub(crate) fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
}
|
|
613
|
-
(condition[..index].trim(), "==", condition[index + 2..].trim())
|
|
614
|
-
} else {
|
|
615
|
-
return None;
|
|
616
|
-
};
|
|
631
|
+
const OPERATORS: [(&str, &str); 4] = [(" not_contains ", "not_contains"), (" contains ", "contains"), ("!=", "!="), ("==", "==")];
|
|
632
|
+
|
|
633
|
+
let (left, operator, right) = OPERATORS.iter().find_map(|(pattern, operator)| {
|
|
634
|
+
condition
|
|
635
|
+
.find(pattern)
|
|
636
|
+
.map(|index| (condition[..index].trim(), *operator, condition[index + pattern.len()..].trim()))
|
|
637
|
+
})?;
|
|
617
638
|
|
|
618
639
|
let right = right
|
|
619
640
|
.trim_start_matches('"')
|
|
@@ -649,7 +670,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
649
670
|
|
|
650
671
|
match segment {
|
|
651
672
|
SelectorSegment::AllItems => {
|
|
652
|
-
if let Some(sequence) = node
|
|
673
|
+
if let Some(sequence) = find_block_sequence(node) {
|
|
653
674
|
sequence.entries().map(|entry| entry.syntax().clone()).collect()
|
|
654
675
|
} else {
|
|
655
676
|
Vec::new()
|
|
@@ -657,7 +678,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
657
678
|
}
|
|
658
679
|
|
|
659
680
|
SelectorSegment::Index(index) => {
|
|
660
|
-
if let Some(sequence) = node
|
|
681
|
+
if let Some(sequence) = find_block_sequence(node) {
|
|
661
682
|
sequence.entries().nth(*index).map(|entry| vec![entry.syntax().clone()]).unwrap_or_default()
|
|
662
683
|
} else {
|
|
663
684
|
Vec::new()
|
|
@@ -665,7 +686,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
665
686
|
}
|
|
666
687
|
|
|
667
688
|
SelectorSegment::Key(key) => {
|
|
668
|
-
if let Some(map) = node
|
|
689
|
+
if let Some(map) = find_block_map(node) {
|
|
669
690
|
if let Some(entry) = find_entry_by_key(&map, key) {
|
|
670
691
|
if let Some(value) = entry.value() {
|
|
671
692
|
return vec![value.syntax().clone()];
|
|
@@ -680,23 +701,8 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
|
|
|
680
701
|
|
|
681
702
|
pub(crate) fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNode> {
|
|
682
703
|
let parsed = crate::selector::Selector::parse(path);
|
|
683
|
-
let mut current_nodes = vec![node.clone()];
|
|
684
|
-
|
|
685
|
-
for segment in parsed.segments() {
|
|
686
|
-
let mut next_nodes = Vec::new();
|
|
687
704
|
|
|
688
|
-
|
|
689
|
-
next_nodes.extend(resolve_segment(current, segment));
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
current_nodes = next_nodes;
|
|
693
|
-
|
|
694
|
-
if current_nodes.is_empty() {
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
current_nodes
|
|
705
|
+
navigate_remaining(node, parsed.segments())
|
|
700
706
|
}
|
|
701
707
|
|
|
702
708
|
#[derive(Debug, Clone)]
|