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.
@@ -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.root.text().to_string();
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 entry_line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
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 line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
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 before_sequence = &source[..sequence_start];
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 current_kind {
147
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
148
- let text = token.text();
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 = match key_style {
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 current_kind {
214
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
215
- let text = token.text();
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
- QuoteStyle::Double => {
235
- if raw_value.contains('"') && current_kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
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
- if edits.is_empty() {
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.root.text().to_string();
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(|seq| seq.entries().next()) {
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 seq_start: usize = range.start().into();
487
- let line_start = source[..seq_start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
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
- edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
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.to_string();
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 line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
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(&current_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 before_sequence = &source[..sequence_start];
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.to_string();
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 entry_line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
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 sequence = current_node.descendants().find_map(BlockSeq::cast);
700
- let map = current_node.descendants().find_map(BlockMap::cast);
701
-
702
- let use_sequence = match (&sequence, &map) {
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
- if use_sequence {
710
- let entries: Vec<_> = sequence.unwrap().entries().collect();
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
- if edits.is_empty() {
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.root.text().to_string();
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
- (line, column)
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.root.text().to_string();
638
+ let source = self.source_text();
778
639
  let new_source = format!("---\n{}", source);
779
640
 
780
- let path = self.path.take();
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.root.text().to_string();
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
- let path = self.path.take();
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 current_kind {
842
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
843
- let text = token.text();
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 = match style {
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
- if edits.is_empty() {
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.root.text().to_string();
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[..offset].matches('\n').count() + 1;
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[..offset].matches('\n').count() + 1;
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 current_kind {
1051
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
1052
- let text = token.text();
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 line_start = source[..offset].rfind('\n').map(|p| p + 1).unwrap_or(0);
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 current_kind {
1103
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
1104
- let text = token.text();
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
- QuoteStyle::Double => {
1124
- if raw_value.contains('"') && current_kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
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
- if edits.is_empty() {
1155
- return Ok(warnings);
1156
- }
1157
-
1158
- edits.reverse();
917
+ self.apply_edits(edits)?;
1159
918
 
1160
- let mut new_source = source;
919
+ Ok(warnings)
920
+ }
921
+ }
1161
922
 
1162
- for (range, replacement) in edits {
1163
- let start: usize = range.start().into();
1164
- let end: usize = range.end().into();
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
- new_source.replace_range(start..end, &replacement);
930
+ Some(format_scalar_value(raw_value, SyntaxKind::DOUBLE_QUOTED_SCALAR))
1167
931
  }
1168
932
 
1169
- let path = self.path.take();
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
- Ok(warnings)
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
  }
@@ -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.root.text().to_string();
20
+ let source = self.source_text();
22
21
 
23
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
22
+ let sequence = match find_block_sequence(&current_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[..offset].matches('\n').count() + 1;
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(pos) => {
91
- let container = segments[..=pos].to_vec();
92
- let field = segments[pos + 1..].to_vec();
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
  }