yerba 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/document/condition.rs +4 -4
- data/rust/src/document/delete.rs +8 -31
- data/rust/src/document/get.rs +10 -16
- data/rust/src/document/insert.rs +50 -144
- data/rust/src/document/mod.rs +80 -79
- data/rust/src/document/schema.rs +1 -1
- data/rust/src/document/set.rs +42 -38
- data/rust/src/document/sort.rs +150 -310
- data/rust/src/document/style.rs +78 -308
- data/rust/src/document/unique.rs +4 -5
- data/rust/src/selector.rs +3 -3
- data/rust/src/syntax.rs +63 -47
- metadata +1 -1
data/rust/src/document/style.rs
CHANGED
|
@@ -9,7 +9,7 @@ pub struct StyleEnforcement {
|
|
|
9
9
|
|
|
10
10
|
impl Document {
|
|
11
11
|
pub fn enforce_styles(&mut self, enforcement: &StyleEnforcement) -> Result<(), YerbaError> {
|
|
12
|
-
let source = self.
|
|
12
|
+
let source = self.source_text();
|
|
13
13
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
14
14
|
let mut converted_ranges: Vec<TextRange> = Vec::new();
|
|
15
15
|
|
|
@@ -41,8 +41,7 @@ impl Document {
|
|
|
41
41
|
|
|
42
42
|
if let Some(ref entry) = entry_node {
|
|
43
43
|
let entry_start: usize = entry.text_range().start().into();
|
|
44
|
-
let
|
|
45
|
-
let key_indent = entry_start - entry_line_start;
|
|
44
|
+
let key_indent = column_at(&source, entry_start);
|
|
46
45
|
let entry_text = entry.text().to_string();
|
|
47
46
|
|
|
48
47
|
if let Some(colon_offset) = entry_text.find(':') {
|
|
@@ -78,8 +77,7 @@ impl Document {
|
|
|
78
77
|
};
|
|
79
78
|
|
|
80
79
|
let entry_start: usize = parent_entry.text_range().start().into();
|
|
81
|
-
let
|
|
82
|
-
let key_indent_length = entry_start - line_start;
|
|
80
|
+
let key_indent_length = column_at(&source, entry_start);
|
|
83
81
|
let entry_indent_length = preceding_whitespace_indent(first_entry.syntax()).len();
|
|
84
82
|
let is_indented = entry_indent_length > key_indent_length;
|
|
85
83
|
let needs_change = (want_indented && !is_indented) || (want_compact && is_indented);
|
|
@@ -101,8 +99,7 @@ impl Document {
|
|
|
101
99
|
let sequence_start: usize = sequence_range.start().into();
|
|
102
100
|
let sequence_end: usize = sequence_range.end().into();
|
|
103
101
|
|
|
104
|
-
let
|
|
105
|
-
let line_start_of_first_entry = before_sequence.rfind('\n').map(|position| position + 1).unwrap_or(0);
|
|
102
|
+
let line_start_of_first_entry = line_start_at(&source, sequence_start);
|
|
106
103
|
let full_text = &source[line_start_of_first_entry..sequence_end];
|
|
107
104
|
|
|
108
105
|
let reindented: String = full_text
|
|
@@ -143,39 +140,12 @@ impl Document {
|
|
|
143
140
|
let target_kind = key_style.to_syntax_kind();
|
|
144
141
|
|
|
145
142
|
if current_kind != target_kind {
|
|
146
|
-
let raw_value = match
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
unescape_double_quoted(&text[1..text.len() - 1])
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
154
|
-
let text = token.text();
|
|
155
|
-
|
|
156
|
-
unescape_single_quoted(&text[1..text.len() - 1])
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
|
|
160
|
-
|
|
161
|
-
_ => continue,
|
|
143
|
+
let raw_value = match raw_scalar_value(token) {
|
|
144
|
+
Some(value) => value,
|
|
145
|
+
None => continue,
|
|
162
146
|
};
|
|
163
147
|
|
|
164
|
-
let new_text =
|
|
165
|
-
crate::KeyStyle::Double => {
|
|
166
|
-
let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
|
|
167
|
-
|
|
168
|
-
format!("\"{}\"", escaped)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
crate::KeyStyle::Single => {
|
|
172
|
-
let escaped = raw_value.replace('\'', "''");
|
|
173
|
-
|
|
174
|
-
format!("'{}'", escaped)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
crate::KeyStyle::Plain => raw_value,
|
|
178
|
-
};
|
|
148
|
+
let new_text = format_scalar_value(&raw_value, target_kind);
|
|
179
149
|
|
|
180
150
|
if new_text != token.text() {
|
|
181
151
|
edits.push((token.text_range(), new_text));
|
|
@@ -210,51 +180,18 @@ impl Document {
|
|
|
210
180
|
continue;
|
|
211
181
|
}
|
|
212
182
|
|
|
213
|
-
let raw_value = match
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
unescape_double_quoted(&text[1..text.len() - 1])
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
220
|
-
let text = token.text();
|
|
221
|
-
unescape_single_quoted(&text[1..text.len() - 1])
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
|
|
225
|
-
|
|
226
|
-
_ => continue,
|
|
183
|
+
let raw_value = match raw_scalar_value(token) {
|
|
184
|
+
Some(value) => value,
|
|
185
|
+
None => continue,
|
|
227
186
|
};
|
|
228
187
|
|
|
229
188
|
if current_kind == SyntaxKind::PLAIN_SCALAR && is_yaml_non_string(&raw_value) {
|
|
230
189
|
continue;
|
|
231
190
|
}
|
|
232
191
|
|
|
233
|
-
let new_text = match value_style {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
|
|
240
|
-
|
|
241
|
-
format!("\"{}\"", escaped)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
QuoteStyle::Single => {
|
|
245
|
-
let escaped = raw_value.replace('\'', "''");
|
|
246
|
-
format!("'{}'", escaped)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
QuoteStyle::Plain => {
|
|
250
|
-
if raw_value.contains('"') || raw_value.contains('\'') || raw_value.contains(':') || raw_value.contains('#') {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
raw_value
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
_ => continue,
|
|
192
|
+
let new_text = match inline_quote_replacement(&raw_value, current_kind, value_style) {
|
|
193
|
+
Some(new_text) => new_text,
|
|
194
|
+
None => continue,
|
|
258
195
|
};
|
|
259
196
|
|
|
260
197
|
if new_text != token.text() {
|
|
@@ -265,26 +202,7 @@ impl Document {
|
|
|
265
202
|
}
|
|
266
203
|
}
|
|
267
204
|
|
|
268
|
-
|
|
269
|
-
return Ok(());
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
|
|
273
|
-
|
|
274
|
-
let mut new_source = source;
|
|
275
|
-
|
|
276
|
-
for (range, replacement) in edits {
|
|
277
|
-
let start: usize = range.start().into();
|
|
278
|
-
let end: usize = range.end().into();
|
|
279
|
-
|
|
280
|
-
new_source.replace_range(start..end, &replacement);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
let path = self.path.take();
|
|
284
|
-
*self = Self::parse(&new_source)?;
|
|
285
|
-
self.path = path;
|
|
286
|
-
|
|
287
|
-
Ok(())
|
|
205
|
+
self.apply_edits(edits)
|
|
288
206
|
}
|
|
289
207
|
|
|
290
208
|
pub fn enforce_collection_style(&mut self, style: &str, dot_path: Option<&str>) -> Result<(), YerbaError> {
|
|
@@ -406,7 +324,7 @@ impl Document {
|
|
|
406
324
|
}
|
|
407
325
|
|
|
408
326
|
loop {
|
|
409
|
-
let source = self.
|
|
327
|
+
let source = self.source_text();
|
|
410
328
|
|
|
411
329
|
let scope_node = if scope_path.is_empty() {
|
|
412
330
|
self.root.clone()
|
|
@@ -465,7 +383,7 @@ impl Document {
|
|
|
465
383
|
Some(entry) => entry,
|
|
466
384
|
None => continue,
|
|
467
385
|
};
|
|
468
|
-
let first_entry = match BlockSeq::cast(node).and_then(|
|
|
386
|
+
let first_entry = match BlockSeq::cast(node).and_then(|sequence| sequence.entries().next()) {
|
|
469
387
|
Some(entry) => entry,
|
|
470
388
|
None => continue,
|
|
471
389
|
};
|
|
@@ -483,8 +401,8 @@ impl Document {
|
|
|
483
401
|
continue;
|
|
484
402
|
}
|
|
485
403
|
|
|
486
|
-
let
|
|
487
|
-
let line_start = source
|
|
404
|
+
let sequence_start: usize = range.start().into();
|
|
405
|
+
let line_start = line_start_at(&source, sequence_start);
|
|
488
406
|
let full_text = &source[line_start..usize::from(range.end())];
|
|
489
407
|
|
|
490
408
|
let reindented: String = full_text
|
|
@@ -508,18 +426,7 @@ impl Document {
|
|
|
508
426
|
return Ok(());
|
|
509
427
|
}
|
|
510
428
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
let mut new_source = source;
|
|
514
|
-
for (range, replacement) in edits {
|
|
515
|
-
let start: usize = range.start().into();
|
|
516
|
-
let end: usize = range.end().into();
|
|
517
|
-
new_source.replace_range(start..end, &replacement);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
let path = self.path.take();
|
|
521
|
-
*self = Self::parse(&new_source)?;
|
|
522
|
-
self.path = path;
|
|
429
|
+
self.apply_edits(edits)?;
|
|
523
430
|
}
|
|
524
431
|
}
|
|
525
432
|
|
|
@@ -540,7 +447,7 @@ impl Document {
|
|
|
540
447
|
}
|
|
541
448
|
|
|
542
449
|
let current_node = self.navigate(dot_path)?;
|
|
543
|
-
let source = self.
|
|
450
|
+
let source = self.source_text();
|
|
544
451
|
|
|
545
452
|
let entry_node = current_node
|
|
546
453
|
.ancestors()
|
|
@@ -548,13 +455,9 @@ impl Document {
|
|
|
548
455
|
.ok_or_else(|| YerbaError::ParseError("could not find parent map entry".to_string()))?;
|
|
549
456
|
|
|
550
457
|
let entry_start: usize = entry_node.text_range().start().into();
|
|
551
|
-
let
|
|
552
|
-
let key_indent_length = entry_start - line_start;
|
|
458
|
+
let key_indent_length = column_at(&source, entry_start);
|
|
553
459
|
|
|
554
|
-
let sequence = current_node
|
|
555
|
-
.descendants()
|
|
556
|
-
.find_map(BlockSeq::cast)
|
|
557
|
-
.ok_or_else(|| YerbaError::ParseError("could not find block sequence".to_string()))?;
|
|
460
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::ParseError("could not find block sequence".to_string()))?;
|
|
558
461
|
|
|
559
462
|
let first_entry = sequence.entries().next();
|
|
560
463
|
|
|
@@ -578,8 +481,7 @@ impl Document {
|
|
|
578
481
|
let sequence_start: usize = sequence_range.start().into();
|
|
579
482
|
let sequence_end: usize = sequence_range.end().into();
|
|
580
483
|
|
|
581
|
-
let
|
|
582
|
-
let line_start_of_first_entry = before_sequence.rfind('\n').map(|position| position + 1).unwrap_or(0);
|
|
484
|
+
let line_start_of_first_entry = line_start_at(&source, sequence_start);
|
|
583
485
|
let full_text = &source[line_start_of_first_entry..sequence_end];
|
|
584
486
|
|
|
585
487
|
let reindented: String = full_text
|
|
@@ -619,14 +521,13 @@ impl Document {
|
|
|
619
521
|
_ => {}
|
|
620
522
|
}
|
|
621
523
|
|
|
622
|
-
let source = self.
|
|
524
|
+
let source = self.source_text();
|
|
623
525
|
|
|
624
526
|
let entry_node = current_node.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY);
|
|
625
527
|
|
|
626
528
|
let (key_indent, colon_in_line) = if let Some(ref entry) = entry_node {
|
|
627
529
|
let entry_start: usize = entry.text_range().start().into();
|
|
628
|
-
let
|
|
629
|
-
let indent = entry_start - entry_line_start;
|
|
530
|
+
let indent = column_at(&source, entry_start);
|
|
630
531
|
|
|
631
532
|
let entry_text = entry.text().to_string();
|
|
632
533
|
let colon_offset = entry_text.find(':').map(|offset| entry_start + offset);
|
|
@@ -696,56 +597,18 @@ impl Document {
|
|
|
696
597
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
697
598
|
|
|
698
599
|
for current_node in &nodes {
|
|
699
|
-
let
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
(Some(sequence), Some(map)) => sequence.syntax().text_range().start() <= map.syntax().text_range().start(),
|
|
704
|
-
(Some(_), None) => true,
|
|
705
|
-
(None, Some(_)) => false,
|
|
706
|
-
(None, None) => continue,
|
|
600
|
+
let entry_nodes: Vec<SyntaxNode> = match first_collection(current_node) {
|
|
601
|
+
Some(FirstCollection::Sequence(sequence)) => sequence.entries().map(|entry| entry.syntax().clone()).collect(),
|
|
602
|
+
Some(FirstCollection::Map(map)) => map.entries().map(|entry| entry.syntax().clone()).collect(),
|
|
603
|
+
None => continue,
|
|
707
604
|
};
|
|
708
605
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if entries.len() > 1 {
|
|
713
|
-
for entry in entries.iter().skip(1) {
|
|
714
|
-
collect_blank_line_edits(entry.syntax(), blank_lines, &mut edits);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
} else {
|
|
718
|
-
let entries: Vec<_> = map.unwrap().entries().collect();
|
|
719
|
-
|
|
720
|
-
if entries.len() > 1 {
|
|
721
|
-
for entry in entries.iter().skip(1) {
|
|
722
|
-
collect_blank_line_edits(entry.syntax(), blank_lines, &mut edits);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
606
|
+
for entry_node in entry_nodes.iter().skip(1) {
|
|
607
|
+
collect_blank_line_edits(entry_node, blank_lines, &mut edits);
|
|
725
608
|
}
|
|
726
609
|
}
|
|
727
610
|
|
|
728
|
-
|
|
729
|
-
return Ok(());
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
|
|
733
|
-
|
|
734
|
-
let source = self.root.text().to_string();
|
|
735
|
-
let mut new_source = source;
|
|
736
|
-
|
|
737
|
-
for (range, replacement) in edits {
|
|
738
|
-
let start: usize = range.start().into();
|
|
739
|
-
let end: usize = range.end().into();
|
|
740
|
-
|
|
741
|
-
new_source.replace_range(start..end, &replacement);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
let path = self.path.take();
|
|
745
|
-
*self = Self::parse(&new_source)?;
|
|
746
|
-
self.path = path;
|
|
747
|
-
|
|
748
|
-
Ok(())
|
|
611
|
+
self.apply_edits(edits)
|
|
749
612
|
}
|
|
750
613
|
|
|
751
614
|
pub fn has_directives_marker(&self) -> bool {
|
|
@@ -753,7 +616,7 @@ impl Document {
|
|
|
753
616
|
}
|
|
754
617
|
|
|
755
618
|
pub fn directive_locations(&self) -> Vec<(usize, usize)> {
|
|
756
|
-
let source = self.
|
|
619
|
+
let source = self.source_text();
|
|
757
620
|
|
|
758
621
|
self
|
|
759
622
|
.root
|
|
@@ -761,10 +624,8 @@ impl Document {
|
|
|
761
624
|
.filter(|element| element.kind() == SyntaxKind::DIRECTIVES_END)
|
|
762
625
|
.map(|element| {
|
|
763
626
|
let offset: usize = element.text_range().start().into();
|
|
764
|
-
let line = source[..offset].matches('\n').count() + 1;
|
|
765
|
-
let column = offset - source[..offset].rfind('\n').map(|position| position + 1).unwrap_or(0) + 1;
|
|
766
627
|
|
|
767
|
-
(
|
|
628
|
+
(line_at(&source, offset), column_at(&source, offset) + 1)
|
|
768
629
|
})
|
|
769
630
|
.collect()
|
|
770
631
|
}
|
|
@@ -774,14 +635,10 @@ impl Document {
|
|
|
774
635
|
return Ok(());
|
|
775
636
|
}
|
|
776
637
|
|
|
777
|
-
let source = self.
|
|
638
|
+
let source = self.source_text();
|
|
778
639
|
let new_source = format!("---\n{}", source);
|
|
779
640
|
|
|
780
|
-
|
|
781
|
-
*self = Self::parse(&new_source)?;
|
|
782
|
-
self.path = path;
|
|
783
|
-
|
|
784
|
-
Ok(())
|
|
641
|
+
self.reparse(&new_source)
|
|
785
642
|
}
|
|
786
643
|
|
|
787
644
|
pub fn remove_directives(&mut self) -> Result<(), YerbaError> {
|
|
@@ -789,23 +646,17 @@ impl Document {
|
|
|
789
646
|
return Ok(());
|
|
790
647
|
}
|
|
791
648
|
|
|
792
|
-
let source = self.
|
|
649
|
+
let source = self.source_text();
|
|
793
650
|
let new_source = source
|
|
794
651
|
.strip_prefix("---\n")
|
|
795
652
|
.or_else(|| source.strip_prefix("---"))
|
|
796
653
|
.unwrap_or(&source)
|
|
797
654
|
.to_string();
|
|
798
655
|
|
|
799
|
-
|
|
800
|
-
*self = Self::parse(&new_source)?;
|
|
801
|
-
self.path = path;
|
|
802
|
-
|
|
803
|
-
Ok(())
|
|
656
|
+
self.reparse(&new_source)
|
|
804
657
|
}
|
|
805
658
|
|
|
806
659
|
pub fn enforce_key_style(&mut self, style: &crate::KeyStyle, dot_path: Option<&str>) -> Result<(), YerbaError> {
|
|
807
|
-
let source = self.root.text().to_string();
|
|
808
|
-
|
|
809
660
|
let scope_ranges: Vec<TextRange> = match dot_path {
|
|
810
661
|
Some(path) if !path.is_empty() => self.navigate_all_compact(path).iter().map(|node| node.text_range()).collect(),
|
|
811
662
|
_ => vec![self.root.text_range()],
|
|
@@ -838,35 +689,12 @@ impl Document {
|
|
|
838
689
|
continue;
|
|
839
690
|
}
|
|
840
691
|
|
|
841
|
-
let raw_value = match
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
unescape_double_quoted(&text[1..text.len() - 1])
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
848
|
-
let text = token.text();
|
|
849
|
-
unescape_single_quoted(&text[1..text.len() - 1])
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
|
|
853
|
-
|
|
854
|
-
_ => continue,
|
|
692
|
+
let raw_value = match raw_scalar_value(&token) {
|
|
693
|
+
Some(value) => value,
|
|
694
|
+
None => continue,
|
|
855
695
|
};
|
|
856
696
|
|
|
857
|
-
let new_text =
|
|
858
|
-
crate::KeyStyle::Double => {
|
|
859
|
-
let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
|
|
860
|
-
format!("\"{}\"", escaped)
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
crate::KeyStyle::Single => {
|
|
864
|
-
let escaped = raw_value.replace('\'', "''");
|
|
865
|
-
format!("'{}'", escaped)
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
crate::KeyStyle::Plain => raw_value,
|
|
869
|
-
};
|
|
697
|
+
let new_text = format_scalar_value(&raw_value, target_kind);
|
|
870
698
|
|
|
871
699
|
if new_text != token.text() {
|
|
872
700
|
edits.push((token.text_range(), new_text));
|
|
@@ -874,26 +702,7 @@ impl Document {
|
|
|
874
702
|
}
|
|
875
703
|
}
|
|
876
704
|
|
|
877
|
-
|
|
878
|
-
return Ok(());
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
edits.reverse();
|
|
882
|
-
|
|
883
|
-
let mut new_source = source;
|
|
884
|
-
|
|
885
|
-
for (range, replacement) in edits {
|
|
886
|
-
let start: usize = range.start().into();
|
|
887
|
-
let end: usize = range.end().into();
|
|
888
|
-
|
|
889
|
-
new_source.replace_range(start..end, &replacement);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
let path = self.path.take();
|
|
893
|
-
*self = Self::parse(&new_source)?;
|
|
894
|
-
self.path = path;
|
|
895
|
-
|
|
896
|
-
Ok(())
|
|
705
|
+
self.apply_edits(edits)
|
|
897
706
|
}
|
|
898
707
|
|
|
899
708
|
pub fn enforce_quote_style(
|
|
@@ -918,7 +727,7 @@ impl Document {
|
|
|
918
727
|
}
|
|
919
728
|
|
|
920
729
|
pub fn enforce_quotes_at(&mut self, style: &QuoteStyle, dot_path: Option<&str>) -> Result<Vec<String>, YerbaError> {
|
|
921
|
-
let source = self.
|
|
730
|
+
let source = self.source_text();
|
|
922
731
|
|
|
923
732
|
let scope_ranges: Vec<TextRange> = match dot_path {
|
|
924
733
|
Some(path) if !path.is_empty() => self.navigate_all_compact(path).iter().map(|node| node.text_range()).collect(),
|
|
@@ -1007,7 +816,7 @@ impl Document {
|
|
|
1007
816
|
QuoteStyle::Single => {
|
|
1008
817
|
if is_multiline {
|
|
1009
818
|
let offset: usize = token.text_range().start().into();
|
|
1010
|
-
let line = source
|
|
819
|
+
let line = line_at(&source, offset);
|
|
1011
820
|
|
|
1012
821
|
warnings.push(format!(
|
|
1013
822
|
"line {}: skipped block scalar → single (multiline content can't be single-quoted)",
|
|
@@ -1025,7 +834,7 @@ impl Document {
|
|
|
1025
834
|
QuoteStyle::Plain => {
|
|
1026
835
|
if is_multiline || trimmed.contains(':') || trimmed.contains('#') || trimmed.contains('"') || trimmed.contains('\'') {
|
|
1027
836
|
let offset: usize = token.text_range().start().into();
|
|
1028
|
-
let line = source
|
|
837
|
+
let line = line_at(&source, offset);
|
|
1029
838
|
let reason = if is_multiline { "multiline content" } else { "special characters" };
|
|
1030
839
|
|
|
1031
840
|
warnings.push(format!("line {}: skipped block scalar → plain ({} can't be plain)", line, reason));
|
|
@@ -1047,22 +856,9 @@ impl Document {
|
|
|
1047
856
|
}
|
|
1048
857
|
|
|
1049
858
|
if is_inline_scalar && is_block_scalar_target {
|
|
1050
|
-
let raw_value = match
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
unescape_double_quoted(&text[1..text.len() - 1])
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
1058
|
-
let text = token.text();
|
|
1059
|
-
|
|
1060
|
-
unescape_single_quoted(&text[1..text.len() - 1])
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
|
|
1064
|
-
|
|
1065
|
-
_ => continue,
|
|
859
|
+
let raw_value = match raw_scalar_value(&token) {
|
|
860
|
+
Some(value) => value,
|
|
861
|
+
None => continue,
|
|
1066
862
|
};
|
|
1067
863
|
|
|
1068
864
|
if is_yaml_non_string(&raw_value) {
|
|
@@ -1070,8 +866,7 @@ impl Document {
|
|
|
1070
866
|
}
|
|
1071
867
|
|
|
1072
868
|
let offset: usize = token.text_range().start().into();
|
|
1073
|
-
let
|
|
1074
|
-
let line_prefix = &source[line_start..offset];
|
|
869
|
+
let line_prefix = &source[line_start_at(&source, offset)..offset];
|
|
1075
870
|
let indent = line_prefix.len() - line_prefix.trim_start().len() + 2;
|
|
1076
871
|
let indent_str = " ".repeat(indent);
|
|
1077
872
|
let header = style.block_header();
|
|
@@ -1099,50 +894,18 @@ impl Document {
|
|
|
1099
894
|
continue;
|
|
1100
895
|
}
|
|
1101
896
|
|
|
1102
|
-
let raw_value = match
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
unescape_double_quoted(&text[1..text.len() - 1])
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
SyntaxKind::SINGLE_QUOTED_SCALAR => {
|
|
1109
|
-
let text = token.text();
|
|
1110
|
-
unescape_single_quoted(&text[1..text.len() - 1])
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
|
|
1114
|
-
|
|
1115
|
-
_ => continue,
|
|
897
|
+
let raw_value = match raw_scalar_value(&token) {
|
|
898
|
+
Some(value) => value,
|
|
899
|
+
None => continue,
|
|
1116
900
|
};
|
|
1117
901
|
|
|
1118
902
|
if current_kind == SyntaxKind::PLAIN_SCALAR && is_yaml_non_string(&raw_value) {
|
|
1119
903
|
continue;
|
|
1120
904
|
}
|
|
1121
905
|
|
|
1122
|
-
let new_text = match style {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
continue;
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
|
|
1129
|
-
format!("\"{}\"", escaped)
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
QuoteStyle::Single => {
|
|
1133
|
-
let escaped = raw_value.replace('\'', "''");
|
|
1134
|
-
format!("'{}'", escaped)
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
QuoteStyle::Plain => {
|
|
1138
|
-
if raw_value.contains('"') || raw_value.contains('\'') || raw_value.contains(':') || raw_value.contains('#') {
|
|
1139
|
-
continue;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
raw_value
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
_ => continue,
|
|
906
|
+
let new_text = match inline_quote_replacement(&raw_value, current_kind, style) {
|
|
907
|
+
Some(new_text) => new_text,
|
|
908
|
+
None => continue,
|
|
1146
909
|
};
|
|
1147
910
|
|
|
1148
911
|
if new_text != token.text() {
|
|
@@ -1151,25 +914,32 @@ impl Document {
|
|
|
1151
914
|
}
|
|
1152
915
|
}
|
|
1153
916
|
|
|
1154
|
-
|
|
1155
|
-
return Ok(warnings);
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
edits.reverse();
|
|
917
|
+
self.apply_edits(edits)?;
|
|
1159
918
|
|
|
1160
|
-
|
|
919
|
+
Ok(warnings)
|
|
920
|
+
}
|
|
921
|
+
}
|
|
1161
922
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
923
|
+
fn inline_quote_replacement(raw_value: &str, current_kind: SyntaxKind, style: &QuoteStyle) -> Option<String> {
|
|
924
|
+
match style {
|
|
925
|
+
QuoteStyle::Double => {
|
|
926
|
+
if raw_value.contains('"') && current_kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
|
|
927
|
+
return None;
|
|
928
|
+
}
|
|
1165
929
|
|
|
1166
|
-
|
|
930
|
+
Some(format_scalar_value(raw_value, SyntaxKind::DOUBLE_QUOTED_SCALAR))
|
|
1167
931
|
}
|
|
1168
932
|
|
|
1169
|
-
|
|
1170
|
-
*self = Self::parse(&new_source)?;
|
|
1171
|
-
self.path = path;
|
|
933
|
+
QuoteStyle::Single => Some(format_scalar_value(raw_value, SyntaxKind::SINGLE_QUOTED_SCALAR)),
|
|
1172
934
|
|
|
1173
|
-
|
|
935
|
+
QuoteStyle::Plain => {
|
|
936
|
+
if raw_value.contains('"') || raw_value.contains('\'') || raw_value.contains(':') || raw_value.contains('#') {
|
|
937
|
+
return None;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
Some(raw_value.to_string())
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
_ => None,
|
|
1174
944
|
}
|
|
1175
945
|
}
|
data/rust/src/document/unique.rs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
use yaml_parser::ast::BlockSeq;
|
|
2
|
-
|
|
3
1
|
use rowan::ast::AstNode;
|
|
4
2
|
|
|
5
3
|
use crate::document::{extract_scalar_text, navigate_from_node, Document};
|
|
6
4
|
use crate::error::YerbaError;
|
|
5
|
+
use crate::syntax::{find_block_sequence, line_at};
|
|
7
6
|
|
|
8
7
|
#[derive(Debug, Clone)]
|
|
9
8
|
pub struct DuplicateInfo {
|
|
@@ -18,9 +17,9 @@ impl Document {
|
|
|
18
17
|
|
|
19
18
|
pub fn unique_with_options(&mut self, dot_path: &str, by: &str, remove: bool, allow_blank_duplicates: bool) -> Result<Vec<DuplicateInfo>, YerbaError> {
|
|
20
19
|
let current_node = self.navigate(dot_path)?;
|
|
21
|
-
let source = self.
|
|
20
|
+
let source = self.source_text();
|
|
22
21
|
|
|
23
|
-
let sequence = match current_node
|
|
22
|
+
let sequence = match find_block_sequence(¤t_node) {
|
|
24
23
|
Some(sequence) => sequence,
|
|
25
24
|
None => return Ok(Vec::new()),
|
|
26
25
|
};
|
|
@@ -37,7 +36,7 @@ impl Document {
|
|
|
37
36
|
.iter()
|
|
38
37
|
.map(|entry| {
|
|
39
38
|
let offset: usize = entry.syntax().text_range().start().into();
|
|
40
|
-
let line = source
|
|
39
|
+
let line = line_at(&source, offset);
|
|
41
40
|
|
|
42
41
|
let value = if by_is_scalar {
|
|
43
42
|
Some(entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())).unwrap_or_default())
|
data/rust/src/selector.rs
CHANGED
|
@@ -87,9 +87,9 @@ impl Selector {
|
|
|
87
87
|
.rposition(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_)));
|
|
88
88
|
|
|
89
89
|
match last_bracket {
|
|
90
|
-
Some(
|
|
91
|
-
let container = segments[..=
|
|
92
|
-
let field = segments[
|
|
90
|
+
Some(position) => {
|
|
91
|
+
let container = segments[..=position].to_vec();
|
|
92
|
+
let field = segments[position + 1..].to_vec();
|
|
93
93
|
|
|
94
94
|
(Selector::Absolute(container), Selector::Relative(field))
|
|
95
95
|
}
|