yerba 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);
@@ -19,6 +19,7 @@ pub struct LocatedNode {
19
19
  pub file_path: Option<String>,
20
20
  pub selector: String,
21
21
  pub line: usize,
22
+ pub location: Location,
22
23
  }
23
24
 
24
25
  use std::fs;
@@ -34,8 +35,9 @@ use crate::error::YerbaError;
34
35
  use crate::QuoteStyle;
35
36
 
36
37
  use crate::syntax::{
37
- dedent_block_scalar, extract_scalar, extract_scalar_text, 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,
38
+ column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_scalar_token,
39
+ first_collection, format_scalar_value, is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token,
40
+ raw_scalar_value, removal_range, FirstCollection, ScalarValue,
39
41
  };
40
42
 
41
43
  #[derive(Debug, Clone)]
@@ -148,13 +150,13 @@ impl Document {
148
150
  .as_ref()
149
151
  .ok_or_else(|| YerbaError::IoError(std::io::Error::new(std::io::ErrorKind::NotFound, "no file path associated with this document")))?;
150
152
 
151
- fs::write(path, self.to_string())?;
153
+ fs::write(path, self.source_text())?;
152
154
 
153
155
  Ok(())
154
156
  }
155
157
 
156
158
  pub fn save_to(&self, path: impl AsRef<Path>) -> Result<(), YerbaError> {
157
- fs::write(path, self.to_string())?;
159
+ fs::write(path, self.source_text())?;
158
160
 
159
161
  Ok(())
160
162
  }
@@ -208,7 +210,7 @@ impl Document {
208
210
  let mut current_nodes: Vec<Option<SyntaxNode>> = vec![Some(document.syntax().clone())];
209
211
 
210
212
  if parsed.is_empty() {
211
- if let Some(sequence) = document.syntax().descendants().find_map(BlockSeq::cast) {
213
+ if let Some(sequence) = find_block_sequence(document.syntax()) {
212
214
  current_nodes = sequence.entries().map(|entry| Some(entry.syntax().clone())).collect();
213
215
  }
214
216
 
@@ -296,7 +298,20 @@ impl Document {
296
298
  }
297
299
 
298
300
  fn insert_after_node(&mut self, node: &SyntaxNode, text: &str) -> Result<(), YerbaError> {
299
- let position = node.text_range().end();
301
+ let end: usize = node.text_range().end().into();
302
+ let source = self.source_text();
303
+
304
+ let rest = &source[end..];
305
+ let line_end = rest.find('\n').unwrap_or(rest.len());
306
+ let trailing = &rest[..line_end];
307
+
308
+ let position = if let Some(comment_start) = trailing.find('#') {
309
+ let _ = comment_start;
310
+ rowan::TextSize::from((end + line_end) as u32)
311
+ } else {
312
+ node.text_range().end()
313
+ };
314
+
300
315
  let range = TextRange::new(position, position);
301
316
 
302
317
  self.apply_edit(range, text)
@@ -412,15 +427,37 @@ impl Document {
412
427
  }
413
428
 
414
429
  fn apply_edit(&mut self, range: TextRange, replacement: &str) -> Result<(), YerbaError> {
415
- let source = self.root.text().to_string();
416
- let start: usize = range.start().into();
417
- let end: usize = range.end().into();
430
+ self.apply_edits(vec![(range, replacement.to_string())])
431
+ }
432
+
433
+ fn apply_edits(&mut self, mut edits: Vec<(TextRange, String)>) -> Result<(), YerbaError> {
434
+ if edits.is_empty() {
435
+ return Ok(());
436
+ }
418
437
 
419
- let mut new_source = source;
420
- new_source.replace_range(start..end, replacement);
438
+ edits.sort_by_key(|(range, _)| std::cmp::Reverse(range.start()));
421
439
 
440
+ let mut new_source = self.source_text();
441
+
442
+ for (range, replacement) in edits {
443
+ let start: usize = range.start().into();
444
+ let end: usize = range.end().into();
445
+
446
+ new_source.replace_range(start..end, &replacement);
447
+ }
448
+
449
+ self.reparse(&new_source)
450
+ }
451
+
452
+ fn source_text(&self) -> String {
453
+ self.root.text().to_string()
454
+ }
455
+
456
+ fn reparse(&mut self, new_source: &str) -> Result<(), YerbaError> {
457
+ let document = Self::parse(new_source)?;
422
458
  let path = self.path.take();
423
- *self = Self::parse(&new_source)?;
459
+
460
+ *self = document;
424
461
  self.path = path;
425
462
 
426
463
  Ok(())
@@ -445,8 +482,8 @@ fn check_duplicate_keys(root: &SyntaxNode) -> Result<(), YerbaError> {
445
482
 
446
483
  if let Some(&first_offset) = seen.get(&key_text) {
447
484
  let source = root.text().to_string();
448
- let first_line = source[..first_offset.into()].matches('\n').count() + 1;
449
- let duplicate_line = source[..usize::from(offset)].matches('\n').count() + 1;
485
+ let first_line = line_at(&source, first_offset.into());
486
+ let duplicate_line = line_at(&source, offset.into());
450
487
  let line_content = source.lines().nth(duplicate_line - 1).unwrap_or("").to_string();
451
488
 
452
489
  return Err(YerbaError::DuplicateKey {
@@ -471,21 +508,13 @@ pub(crate) fn compute_location(source: &str, start_offset: usize, end_offset: us
471
508
  let start = start_offset.min(source.len());
472
509
  let end = end_offset.min(source.len());
473
510
 
474
- let before_start = &source[..start];
475
- let start_line = before_start.chars().filter(|c| *c == '\n').count() + 1;
476
- let start_column = start - before_start.rfind('\n').map(|p| p + 1).unwrap_or(0);
477
-
478
- let before_end = &source[..end];
479
- let end_line = before_end.chars().filter(|c| *c == '\n').count() + 1;
480
- let end_column = end - before_end.rfind('\n').map(|p| p + 1).unwrap_or(0);
481
-
482
511
  Location {
483
512
  start_offset: start,
484
513
  end_offset: end,
485
- start_line,
486
- start_column,
487
- end_line,
488
- end_column,
514
+ start_line: line_at(source, start),
515
+ start_column: column_at(source, start),
516
+ end_line: line_at(source, end),
517
+ end_column: column_at(source, end),
489
518
  }
490
519
  }
491
520
 
@@ -520,39 +549,31 @@ pub fn collect_selectors(value: &yaml_serde::Value, prefix: &str, selectors: &mu
520
549
  }
521
550
 
522
551
  pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
523
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
524
- let map_position = node.descendants().find_map(BlockMap::cast).map(|map| map.syntax().text_range().start());
525
-
526
- let sequence_position = sequence.syntax().text_range().start();
527
-
528
- if map_position.is_none() || sequence_position <= map_position.unwrap() {
552
+ match first_collection(node) {
553
+ Some(FirstCollection::Sequence(sequence)) => {
529
554
  let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
530
555
 
531
556
  return yaml_serde::Value::Sequence(values);
532
557
  }
533
- }
534
558
 
535
- if let Some(map) = node.descendants().find_map(BlockMap::cast) {
536
- let mut mapping = yaml_serde::Mapping::new();
559
+ Some(FirstCollection::Map(map)) => {
560
+ let mut mapping = yaml_serde::Mapping::new();
537
561
 
538
- for entry in map.entries() {
539
- let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
540
-
541
- let value = entry
542
- .value()
543
- .map(|value_node| node_to_yaml_value(value_node.syntax()))
544
- .unwrap_or(yaml_serde::Value::Null);
562
+ for entry in map.entries() {
563
+ let key = entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())).unwrap_or_default();
545
564
 
546
- mapping.insert(yaml_serde::Value::String(key), value);
547
- }
565
+ let value = entry
566
+ .value()
567
+ .map(|value_node| node_to_yaml_value(value_node.syntax()))
568
+ .unwrap_or(yaml_serde::Value::Null);
548
569
 
549
- return yaml_serde::Value::Mapping(mapping);
550
- }
570
+ mapping.insert(yaml_serde::Value::String(key), value);
571
+ }
551
572
 
552
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
553
- let values: Vec<yaml_serde::Value> = sequence.entries().map(|entry| node_to_yaml_value(entry.syntax())).collect();
573
+ return yaml_serde::Value::Mapping(mapping);
574
+ }
554
575
 
555
- return yaml_serde::Value::Sequence(values);
576
+ None => {}
556
577
  }
557
578
 
558
579
  if let Some(block_scalar) = node.descendants().find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR) {
@@ -608,17 +629,13 @@ pub(crate) fn node_to_yaml_value(node: &SyntaxNode) -> yaml_serde::Value {
608
629
  }
609
630
 
610
631
  pub(crate) fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
611
- let (left, operator, right) = if let Some(index) = condition.find(" not_contains ") {
612
- (condition[..index].trim(), "not_contains", condition[index + 14..].trim())
613
- } else if let Some(index) = condition.find(" contains ") {
614
- (condition[..index].trim(), "contains", condition[index + 10..].trim())
615
- } else if let Some(index) = condition.find("!=") {
616
- (condition[..index].trim(), "!=", condition[index + 2..].trim())
617
- } else if let Some(index) = condition.find("==") {
618
- (condition[..index].trim(), "==", condition[index + 2..].trim())
619
- } else {
620
- return None;
621
- };
632
+ const OPERATORS: [(&str, &str); 4] = [(" not_contains ", "not_contains"), (" contains ", "contains"), ("!=", "!="), ("==", "==")];
633
+
634
+ let (left, operator, right) = OPERATORS.iter().find_map(|(pattern, operator)| {
635
+ condition
636
+ .find(pattern)
637
+ .map(|index| (condition[..index].trim(), *operator, condition[index + pattern.len()..].trim()))
638
+ })?;
622
639
 
623
640
  let right = right
624
641
  .trim_start_matches('"')
@@ -654,7 +671,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
654
671
 
655
672
  match segment {
656
673
  SelectorSegment::AllItems => {
657
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
674
+ if let Some(sequence) = find_block_sequence(node) {
658
675
  sequence.entries().map(|entry| entry.syntax().clone()).collect()
659
676
  } else {
660
677
  Vec::new()
@@ -662,7 +679,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
662
679
  }
663
680
 
664
681
  SelectorSegment::Index(index) => {
665
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
682
+ if let Some(sequence) = find_block_sequence(node) {
666
683
  sequence.entries().nth(*index).map(|entry| vec![entry.syntax().clone()]).unwrap_or_default()
667
684
  } else {
668
685
  Vec::new()
@@ -670,7 +687,7 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
670
687
  }
671
688
 
672
689
  SelectorSegment::Key(key) => {
673
- if let Some(map) = node.descendants().find_map(BlockMap::cast) {
690
+ if let Some(map) = find_block_map(node) {
674
691
  if let Some(entry) = find_entry_by_key(&map, key) {
675
692
  if let Some(value) = entry.value() {
676
693
  return vec![value.syntax().clone()];
@@ -685,23 +702,8 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
685
702
 
686
703
  pub(crate) fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNode> {
687
704
  let parsed = crate::selector::Selector::parse(path);
688
- let mut current_nodes = vec![node.clone()];
689
-
690
- for segment in parsed.segments() {
691
- let mut next_nodes = Vec::new();
692
705
 
693
- for current in &current_nodes {
694
- next_nodes.extend(resolve_segment(current, segment));
695
- }
696
-
697
- current_nodes = next_nodes;
698
-
699
- if current_nodes.is_empty() {
700
- break;
701
- }
702
- }
703
-
704
- current_nodes
706
+ navigate_remaining(node, parsed.segments())
705
707
  }
706
708
 
707
709
  #[derive(Debug, Clone)]
@@ -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() {