yerba 0.7.1 → 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.
@@ -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(&current_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, &quote_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.descendants().find_map(BlockSeq::cast).is_some()
95
+ if find_block_sequence(&current_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.descendants().find_map(BlockMap::cast) {
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.to_string();
190
- let before = &source[..offset];
155
+ let source = self.source_text();
191
156
 
192
- offset - before.rfind('\n').map(|p| p + 1).unwrap_or(0)
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.descendants().find_map(BlockSeq::cast) else {
205
+ let Some(sequence) = find_block_sequence(&current_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 = if value.contains('\n') {
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.descendants().find_map(BlockMap::cast) {
323
+ let map = match find_block_map(&current_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 is_block_value = value.contains('\n') || value.starts_with("- ");
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.to_string();
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 flow_seq = current_node
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 = flow_seq.text_range();
611
- let source = self.to_string();
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.root.text().to_string();
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);
@@ -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, find_entry_by_key, find_scalar_token, format_scalar_value, is_map_key, is_yaml_non_string,
38
- preceding_whitespace_indent, preceding_whitespace_token, removal_range, unescape_double_quoted, unescape_single_quoted, ScalarValue,
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.to_string())?;
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.to_string())?;
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().descendants().find_map(BlockSeq::cast) {
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 position = node.text_range().end();
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
- let source = self.root.text().to_string();
411
- let start: usize = range.start().into();
412
- let end: usize = range.end().into();
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
- let mut new_source = source;
415
- new_source.replace_range(start..end, replacement);
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
- *self = Self::parse(&new_source)?;
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[..first_offset.into()].matches('\n').count() + 1;
444
- let duplicate_line = source[..usize::from(offset)].matches('\n').count() + 1;
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
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
519
- let map_position = node.descendants().find_map(BlockMap::cast).map(|map| map.syntax().text_range().start());
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
- if let Some(map) = node.descendants().find_map(BlockMap::cast) {
531
- let mut mapping = yaml_serde::Mapping::new();
558
+ Some(FirstCollection::Map(map)) => {
559
+ let mut mapping = yaml_serde::Mapping::new();
532
560
 
533
- for entry in map.entries() {
534
- let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
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
- mapping.insert(yaml_serde::Value::String(key), value);
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
- return yaml_serde::Value::Mapping(mapping);
545
- }
569
+ mapping.insert(yaml_serde::Value::String(key), value);
570
+ }
546
571
 
547
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
548
- let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
572
+ return yaml_serde::Value::Mapping(mapping);
573
+ }
549
574
 
550
- return yaml_serde::Value::Sequence(values);
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
- let (left, operator, right) = if let Some(index) = condition.find(" not_contains ") {
607
- (condition[..index].trim(), "not_contains", condition[index + 14..].trim())
608
- } else if let Some(index) = condition.find(" contains ") {
609
- (condition[..index].trim(), "contains", condition[index + 10..].trim())
610
- } else if let Some(index) = condition.find("!=") {
611
- (condition[..index].trim(), "!=", condition[index + 2..].trim())
612
- } else if let Some(index) = condition.find("==") {
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.descendants().find_map(BlockSeq::cast) {
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.descendants().find_map(BlockSeq::cast) {
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.descendants().find_map(BlockMap::cast) {
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
- for current in &current_nodes {
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)]
@@ -31,7 +31,7 @@ impl Document {
31
31
  crate::schema::validate_value(&value, schema)
32
32
  };
33
33
 
34
- let source = self.to_string();
34
+ let source = self.source_text();
35
35
 
36
36
  for error in &mut errors {
37
37
  if !error.path.is_empty() {