1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::fmt;
4
5use crate::syntax::node_link;
6use crate::{
7 Children, CollectionStyle, Diagnostic, DiagnosticKind, FromYamlDoc, Node, NodeId, NodeKind,
8 Parser, ScalarStyle, SemanticKind, SemanticStore, Source, Span, ToYamlDoc, ToYamlFragment,
9 Token, YamlEditError, YamlError, YamlEvent, YamlFragment,
10 decode_scalar_value_with_content_indent, directive_emit_error, double_quoted_scalar_end,
11 edits_conflict, events_to_test_string, format_scalar_value, lex, parse_node_properties,
12 plain_scalar_end, resolve_tag, single_quoted_scalar_end, strip_inline_comment,
13 validate_plain_mapping_fragment, validate_tag_directive_parts_for_emit, validate_yaml_chars,
14 validate_yaml_directive_version_for_emit,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Edit {
20 pub span: Span,
22 pub replacement: String,
24}
25
26pub struct YamlEvents<'doc> {
28 doc: &'doc YamlDoc,
29 tasks: Vec<EventTask>,
30}
31
32impl Iterator for YamlEvents<'_> {
33 type Item = YamlEvent;
34
35 fn next(&mut self) -> Option<Self::Item> {
36 while let Some(task) = self.tasks.pop() {
37 match task {
38 EventTask::StreamStart => {
39 return Some(YamlEvent {
40 kind: crate::YamlEventKind::StreamStart,
41 span: Span::from_usize(0, self.doc.source.len()),
42 cst: None,
43 content_indent: None,
44 });
45 }
46 EventTask::Documents(index) => self.schedule_document(index),
47 EventTask::DocumentStart(document) => {
48 let Some(metadata) = self.doc.semantics.get(document) else {
49 continue;
50 };
51 return Some(YamlEvent {
52 kind: crate::YamlEventKind::DocumentStart {
53 explicit: metadata.explicit_start(),
54 },
55 span: self.doc.semantic_span(document, metadata),
56 cst: Some(document),
57 content_indent: None,
58 });
59 }
60 EventTask::DocumentChildren(next) => self.schedule_document_child(next),
61 EventTask::DocumentEnd(document) => {
62 let Some(metadata) = self.doc.semantics.get(document) else {
63 continue;
64 };
65 return Some(YamlEvent {
66 kind: crate::YamlEventKind::DocumentEnd {
67 explicit: metadata.explicit_end(),
68 },
69 span: self.doc.semantic_end_span(document, metadata),
70 cst: None,
71 content_indent: None,
72 });
73 }
74 EventTask::Node(node) => {
75 if let Some(event) = self.schedule_node(node) {
76 return Some(event);
77 }
78 }
79 EventTask::MappingEntries(next) => self.schedule_mapping_entry(next),
80 EventTask::SequenceEntries(next) => self.schedule_sequence_entry(next),
81 EventTask::CollectionEnd { node, mapping } => {
82 let Some(metadata) = self.doc.semantics.get(node) else {
83 continue;
84 };
85 return Some(YamlEvent {
86 kind: if mapping {
87 crate::YamlEventKind::MappingEnd
88 } else {
89 crate::YamlEventKind::SequenceEnd
90 },
91 span: self.doc.semantic_end_span(node, metadata),
92 cst: None,
93 content_indent: None,
94 });
95 }
96 EventTask::StreamEnd => {
97 return Some(YamlEvent {
98 kind: crate::YamlEventKind::StreamEnd,
99 span: Span::empty_from_usize(self.doc.source.len()),
100 cst: None,
101 content_indent: None,
102 });
103 }
104 }
105 }
106 None
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111enum EventTask {
112 StreamStart,
113 Documents(usize),
114 DocumentStart(NodeId),
115 DocumentChildren(u32),
116 DocumentEnd(NodeId),
117 Node(NodeId),
118 MappingEntries(u32),
119 SequenceEntries(u32),
120 CollectionEnd { node: NodeId, mapping: bool },
121 StreamEnd,
122}
123
124impl YamlEvents<'_> {
125 fn schedule_document(&mut self, index: usize) {
126 let Some(&document) = self.doc.semantics.documents.get(index) else {
127 return;
128 };
129 let Some(node) = self.doc.node(document) else {
130 self.tasks.push(EventTask::Documents(index + 1));
131 return;
132 };
133 self.tasks.push(EventTask::Documents(index + 1));
134 self.tasks.push(EventTask::DocumentEnd(document));
135 self.tasks
136 .push(EventTask::DocumentChildren(node.first_child));
137 self.tasks.push(EventTask::DocumentStart(document));
138 }
139
140 fn schedule_document_child(&mut self, next: u32) {
141 let Some(child) = node_link(next) else {
142 return;
143 };
144 self.tasks.push(EventTask::DocumentChildren(
145 self.doc.nodes[child.as_usize()].next_sibling,
146 ));
147 if self.doc.semantics.get(child).is_some() {
148 self.tasks.push(EventTask::Node(child));
149 }
150 }
151
152 fn schedule_node(&mut self, node: NodeId) -> Option<YamlEvent> {
153 let metadata = self.doc.semantics.get(node)?;
154 let span = self.doc.semantic_span(node, metadata);
155 match metadata.kind {
156 SemanticKind::Document => None,
157 SemanticKind::Mapping { style } => {
158 self.tasks.push(EventTask::CollectionEnd {
159 node,
160 mapping: true,
161 });
162 self.tasks.push(EventTask::MappingEntries(
163 self.doc.nodes[node.as_usize()].first_child,
164 ));
165 Some(YamlEvent {
166 kind: crate::YamlEventKind::MappingStart {
167 style,
168 tag: self
169 .doc
170 .resolved_tag(node)
171 .ok()
172 .flatten()
173 .map(Cow::into_owned),
174 anchor: self.doc.anchor(node).map(str::to_owned),
175 },
176 span,
177 cst: Some(node),
178 content_indent: None,
179 })
180 }
181 SemanticKind::Sequence { style } => {
182 self.tasks.push(EventTask::CollectionEnd {
183 node,
184 mapping: false,
185 });
186 self.tasks.push(EventTask::SequenceEntries(
187 self.doc.nodes[node.as_usize()].first_child,
188 ));
189 Some(YamlEvent {
190 kind: crate::YamlEventKind::SequenceStart {
191 style,
192 tag: self
193 .doc
194 .resolved_tag(node)
195 .ok()
196 .flatten()
197 .map(Cow::into_owned),
198 anchor: self.doc.anchor(node).map(str::to_owned),
199 },
200 span,
201 cst: Some(node),
202 content_indent: None,
203 })
204 }
205 SemanticKind::Scalar { style } => Some(YamlEvent {
206 kind: crate::YamlEventKind::Scalar {
207 style,
208 value: self
209 .doc
210 .scalar_value(node)
211 .map(Cow::into_owned)
212 .unwrap_or_default(),
213 tag: self
214 .doc
215 .resolved_tag(node)
216 .ok()
217 .flatten()
218 .map(Cow::into_owned),
219 anchor: self.doc.anchor(node).map(str::to_owned),
220 },
221 span,
222 cst: Some(node),
223 content_indent: self
224 .doc
225 .semantics
226 .properties(node)
227 .and_then(|properties| properties.content_indent),
228 }),
229 SemanticKind::Alias => Some(YamlEvent {
230 kind: crate::YamlEventKind::Alias {
231 name: self.doc.alias_name(node).unwrap_or_default().to_owned(),
232 },
233 span,
234 cst: Some(node),
235 content_indent: None,
236 }),
237 }
238 }
239
240 fn schedule_mapping_entry(&mut self, next: u32) {
241 let Some(entry) = node_link(next) else {
242 return;
243 };
244 self.tasks.push(EventTask::MappingEntries(
245 self.doc.nodes[entry.as_usize()].next_sibling,
246 ));
247 if self.doc.nodes[entry.as_usize()].kind != NodeKind::MappingEntry {
248 return;
249 }
250 let Some(key) = self.first_semantic_child(entry) else {
251 return;
252 };
253 let Some(value) = self.next_semantic_sibling(key) else {
254 return;
255 };
256 self.tasks.push(EventTask::Node(value));
257 self.tasks.push(EventTask::Node(key));
258 }
259
260 fn schedule_sequence_entry(&mut self, next: u32) {
261 let Some(entry) = node_link(next) else {
262 return;
263 };
264 self.tasks.push(EventTask::SequenceEntries(
265 self.doc.nodes[entry.as_usize()].next_sibling,
266 ));
267 if self.doc.nodes[entry.as_usize()].kind != NodeKind::SequenceEntry {
268 return;
269 }
270 if let Some(item) = self.first_semantic_child(entry) {
271 self.tasks.push(EventTask::Node(item));
272 }
273 }
274
275 fn first_semantic_child(&self, parent: NodeId) -> Option<NodeId> {
276 let next = self.doc.nodes[parent.as_usize()].first_child;
277 self.next_semantic(next)
278 }
279
280 fn next_semantic_sibling(&self, node: NodeId) -> Option<NodeId> {
281 let next = self.doc.nodes[node.as_usize()].next_sibling;
282 self.next_semantic(next)
283 }
284
285 fn next_semantic(&self, mut next: u32) -> Option<NodeId> {
286 while let Some(node) = node_link(next) {
287 if self.doc.semantics.get(node).is_some() {
288 return Some(node);
289 }
290 next = self.doc.nodes[node.as_usize()].next_sibling;
291 }
292 None
293 }
294}
295
296#[derive(Debug, Clone, PartialEq, Eq)]
298pub struct YamlDirective {
299 pub version: String,
301 pub node: NodeId,
303}
304
305#[derive(Debug, Clone, PartialEq, Eq)]
307pub struct TagDirective {
308 pub handle: String,
310 pub prefix: String,
312 pub node: NodeId,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
318pub struct ReservedDirective {
319 pub name: String,
321 pub parameters: Vec<String>,
323 pub node: NodeId,
325}
326
327#[derive(Debug, Clone, PartialEq, Eq)]
328enum ParsedDirective {
329 Yaml(YamlDirective),
330 Tag(TagDirective),
331 Reserved(ReservedDirective),
332}
333
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
336pub enum MappingEntryStyle {
337 #[default]
339 Inherit,
340 Indent(usize),
342}
343
344#[derive(Debug, Clone, PartialEq, Eq)]
346pub struct YamlDoc {
347 pub(crate) source: Source,
349 pub(crate) nodes: Vec<Node>,
351 pub(crate) semantics: SemanticStore,
353 pub(crate) root_override: Option<NodeId>,
355 pub(crate) edits: Vec<Edit>,
357}
358
359impl YamlDoc {
360 pub fn parse(input: &str) -> Result<Self, YamlError> {
370 Self::parse_owned(input.to_owned())
371 }
372
373 pub fn parse_owned(input: String) -> Result<Self, YamlError> {
380 let source = Source::new(input)?;
381 let parsed = Parser::new(&source)
382 .parse()
383 .map_err(|error| error.with_position_from(&source))?;
384 Ok(Self {
385 source,
386 nodes: parsed.nodes,
387 semantics: parsed.semantics,
388 root_override: None,
389 edits: Vec::new(),
390 })
391 }
392
393 pub fn commit_edits(&mut self) -> Result<(), YamlError> {
405 if self.edits.is_empty() {
406 return Ok(());
407 }
408
409 let edited = self.to_string();
410 *self = Self::parse(&edited)?;
411 Ok(())
412 }
413
414 #[must_use]
416 pub fn as_source(&self) -> &str {
417 self.source.as_str()
418 }
419
420 #[must_use]
422 pub const fn source(&self) -> &Source {
423 &self.source
424 }
425
426 pub fn tokens(&self) -> Result<Vec<Token>, YamlError> {
435 lex(&self.source).map_err(|error| error.with_position_from(&self.source))
436 }
437
438 #[must_use]
440 pub fn root(&self) -> Option<NodeId> {
441 (!self.nodes.is_empty()).then_some(NodeId(0))
442 }
443
444 #[must_use]
446 pub fn events(&self) -> YamlEvents<'_> {
447 let mut tasks = Vec::with_capacity(8);
448 tasks.push(EventTask::StreamEnd);
449 tasks.push(EventTask::Documents(0));
450 tasks.push(EventTask::StreamStart);
451 YamlEvents { doc: self, tasks }
452 }
453
454 #[must_use]
456 pub fn events_to_test_string(&self) -> String {
457 events_to_test_string(self.events())
458 }
459
460 #[must_use]
462 pub fn document_count(&self) -> usize {
463 self.root_override
464 .map_or(self.semantics.documents.len(), |_| 1)
465 }
466
467 pub fn append_document<T>(&mut self, value: &T) -> Result<(), YamlError>
477 where
478 T: ToYamlFragment,
479 {
480 let line_ending = self.preferred_line_ending();
481 let mut replacement = self.document_append_prefix(line_ending);
482 replacement.push_str("---");
483 replacement.push_str(line_ending);
484 let fragment = value.to_yaml_fragment(0, line_ending)?;
485 replacement.push_str(&fragment);
486 replacement.push_str(line_ending);
487 self.queue_edit(Span::empty_from_usize(self.source.len()), replacement)
488 }
489
490 pub fn append_empty_mapping_document(&mut self) -> Result<(), YamlError> {
497 self.append_document(&std::collections::BTreeMap::<String, String>::new())
498 }
499
500 #[must_use]
502 pub fn yaml_directive(&self) -> Option<YamlDirective> {
503 self.directive_nodes()
504 .filter_map(|node| self.parse_directive_node(node).ok())
505 .find_map(|directive| match directive {
506 ParsedDirective::Yaml(directive) => Some(directive),
507 ParsedDirective::Tag(_) | ParsedDirective::Reserved(_) => None,
508 })
509 }
510
511 #[must_use]
513 pub fn tag_directives(&self) -> Vec<TagDirective> {
514 self.directive_nodes()
515 .filter_map(|node| self.parse_directive_node(node).ok())
516 .filter_map(|directive| match directive {
517 ParsedDirective::Tag(directive) => Some(directive),
518 ParsedDirective::Yaml(_) | ParsedDirective::Reserved(_) => None,
519 })
520 .collect()
521 }
522
523 #[must_use]
525 pub fn reserved_directives(&self) -> Vec<ReservedDirective> {
526 self.directive_nodes()
527 .filter_map(|node| self.parse_directive_node(node).ok())
528 .filter_map(|directive| match directive {
529 ParsedDirective::Reserved(directive) => Some(directive),
530 ParsedDirective::Yaml(_) | ParsedDirective::Tag(_) => None,
531 })
532 .collect()
533 }
534
535 pub fn set_yaml_directive(&mut self, version: &str) -> Result<(), YamlError> {
542 validate_yaml_directive_version_for_emit(version)?;
543 let replacement = format!("%YAML {version}");
544 if let Some(directive) = self.yaml_directive() {
545 let span = self.directive_content_span(directive.node)?;
546 self.queue_edit(span, replacement)
547 } else {
548 self.insert_directive_line(replacement)
549 }
550 }
551
552 pub fn set_tag_directive(&mut self, handle: &str, prefix: &str) -> Result<(), YamlError> {
559 validate_tag_directive_parts_for_emit(handle, prefix)?;
560 let replacement = format!("%TAG {handle} {prefix}");
561 if let Some(directive) = self
562 .tag_directives()
563 .into_iter()
564 .find(|directive| directive.handle == handle)
565 {
566 let span = self.directive_content_span(directive.node)?;
567 self.queue_edit(span, replacement)
568 } else {
569 self.insert_directive_line(replacement)
570 }
571 }
572
573 pub fn remove_yaml_directive(&mut self) -> Result<(), YamlError> {
579 if let Some(directive) = self.yaml_directive() {
580 self.remove_directive_node(directive.node)?;
581 }
582 Ok(())
583 }
584
585 pub fn remove_tag_directive(&mut self, handle: &str) -> Result<(), YamlError> {
591 if let Some(directive) = self
592 .tag_directives()
593 .into_iter()
594 .find(|directive| directive.handle == handle)
595 {
596 self.remove_directive_node(directive.node)?;
597 }
598 Ok(())
599 }
600
601 #[must_use]
603 pub fn node(&self, node: NodeId) -> Option<&Node> {
604 self.nodes.get(node.0 as usize)
605 }
606
607 #[must_use]
609 pub fn children(&self, node: NodeId) -> Children<'_> {
610 Children::new(&self.nodes, node)
611 }
612
613 fn semantic_children(&self, node: NodeId) -> impl Iterator<Item = NodeId> + '_ {
614 self.children(node)
615 .filter(|child| self.semantics.get(*child).is_some())
616 }
617
618 #[must_use]
620 pub fn semantic_kind(&self, node: NodeId) -> Option<SemanticKind> {
621 self.semantics.get(node).map(|node| node.kind)
622 }
623
624 #[must_use]
626 pub fn raw_tag(&self, node: NodeId) -> Option<&str> {
627 let span = self.semantics.properties(node)?.tag?;
628 Some(self.source.slice(span))
629 }
630
631 pub fn resolved_tag(&self, node: NodeId) -> Result<Option<Cow<'_, str>>, YamlError> {
633 let Some(raw) = self.raw_tag(node) else {
634 return Ok(None);
635 };
636 let document = self.semantics.property_document(node).unwrap_or(node);
637 let handles = self
638 .semantics
639 .tag_directives(document)
640 .map(|(handle, prefix)| {
641 (
642 self.source.slice(handle).to_owned(),
643 self.source.slice(prefix).to_owned(),
644 )
645 })
646 .collect::<BTreeMap<_, _>>();
647 let span = self
648 .semantics
649 .properties(node)
650 .and_then(|properties| properties.tag)
651 .unwrap_or_else(|| self.node(node).map_or(Span::empty(0), Node::span));
652 resolve_tag(raw, &handles, span).map(|tag| Some(Cow::Owned(tag)))
653 }
654
655 #[must_use]
657 pub fn anchor(&self, node: NodeId) -> Option<&str> {
658 let span = self.semantics.properties(node)?.anchor?;
659 Some(self.source.slice(span))
660 }
661
662 #[must_use]
664 pub fn alias_name(&self, node: NodeId) -> Option<&str> {
665 let span = self.semantics.properties(node)?.alias?;
666 Some(self.source.slice(span))
667 }
668
669 #[must_use]
671 pub fn resolve_alias(&self, node: NodeId) -> Option<NodeId> {
672 let name = self.alias_name(node)?;
673 let document = self.semantics.property_document(node)?;
674 let alias_start = self.node(node)?.span.start;
675 self.semantics
676 .anchors()
677 .rev()
678 .find(|(span, target, anchor_document)| {
679 *anchor_document == document
680 && self
681 .node(*target)
682 .is_some_and(|node| node.span.start <= alias_start)
683 && self.source.slice(*span) == name
684 })
685 .map(|(_, target, _)| target)
686 }
687
688 fn semantic_span(&self, node: NodeId, metadata: &crate::semantic::SemanticNode) -> Span {
689 Span::new(
690 metadata.span_start,
691 self.node(node)
692 .map_or(metadata.end_offset, |node| node.span.end),
693 )
694 }
695
696 fn semantic_end_span(&self, node: NodeId, metadata: &crate::semantic::SemanticNode) -> Span {
697 if metadata.explicit_end()
698 && let Some(marker) = self.children(node).find_map(|child| {
699 let child = self.node(child)?;
700 (child.kind == NodeKind::DocumentMarker
701 && self.source.slice(child.span).starts_with("..."))
702 .then_some(child.span)
703 })
704 {
705 return marker;
706 }
707 if matches!(
708 metadata.kind,
709 SemanticKind::Mapping {
710 style: CollectionStyle::Flow
711 } | SemanticKind::Sequence {
712 style: CollectionStyle::Flow
713 }
714 ) {
715 return self.semantic_span(node, metadata);
716 }
717 Span::empty(metadata.end_offset)
718 }
719
720 pub fn documents(&self) -> impl Iterator<Item = NodeId> + '_ {
722 self.semantics.documents.iter().copied()
723 }
724
725 pub fn document_root(&self, index: usize) -> Result<Option<NodeId>, YamlError> {
735 if let Some(root) = self.root_override {
736 return (index == 0)
737 .then_some(Some(root))
738 .ok_or_else(|| self.document_index_error(index));
739 }
740 let document = self
741 .semantics
742 .documents
743 .get(index)
744 .copied()
745 .ok_or_else(|| self.document_index_error(index))?;
746 let root = self.semantic_children(document).next();
747 Ok(root.filter(|root| {
748 !matches!(self.semantic_kind(*root), Some(SemanticKind::Scalar { .. }))
749 || self.node(*root).is_some_and(|node| !node.span.is_empty())
750 }))
751 }
752
753 pub fn mapping_entries(&self, mapping: NodeId) -> impl Iterator<Item = (NodeId, NodeId)> + '_ {
755 let is_mapping = matches!(
756 self.semantic_kind(mapping),
757 Some(SemanticKind::Mapping { .. })
758 );
759 self.children(mapping).filter_map(move |entry| {
760 if !is_mapping || self.node(entry)?.kind != NodeKind::MappingEntry {
761 return None;
762 }
763 let mut children = self.semantic_children(entry);
764 Some((children.next()?, children.next()?))
765 })
766 }
767
768 pub fn sequence_items(&self, sequence: NodeId) -> impl Iterator<Item = NodeId> + '_ {
770 let is_sequence = matches!(
771 self.semantic_kind(sequence),
772 Some(SemanticKind::Sequence { .. })
773 );
774 self.children(sequence).filter_map(move |entry| {
775 if !is_sequence || self.node(entry)?.kind != NodeKind::SequenceEntry {
776 return None;
777 }
778 self.semantic_children(entry).next()
779 })
780 }
781
782 pub fn root_mapping(&self) -> Result<NodeId, YamlError> {
789 self.document_root_mapping(0)
790 }
791
792 pub fn document_root_mapping(&self, index: usize) -> Result<NodeId, YamlError> {
799 if let Some(root) = self.root_override {
800 return (index == 0)
801 .then_some(root)
802 .ok_or_else(|| self.document_index_error(index));
803 }
804 let document = self
805 .semantics
806 .documents
807 .get(index)
808 .copied()
809 .ok_or_else(|| self.document_index_error(index))?;
810 self.semantic_children(document)
811 .find(|child| {
812 self.node(*child).is_some_and(|node| {
813 matches!(node.kind, NodeKind::BlockMapping | NodeKind::FlowMapping)
814 }) && matches!(
815 self.semantic_kind(*child),
816 Some(SemanticKind::Mapping { .. })
817 )
818 })
819 .ok_or_else(|| {
820 YamlError::new(
821 Diagnostic::new(
822 DiagnosticKind::Semantic,
823 "document does not contain a root mapping",
824 self.node(document).map_or(Span::empty(0), |node| node.span),
825 )
826 .with_expected("a block or flow mapping node"),
827 )
828 })
829 }
830
831 pub fn read_document<T>(&self, index: usize) -> Result<T, YamlError>
838 where
839 T: FromYamlDoc,
840 {
841 let root = self
842 .document_root(index)?
843 .ok_or_else(|| self.empty_document_error(index))?;
844 let nested = self.rerooted_at(root)?;
845 T::from_yaml_doc(&nested)
846 }
847
848 pub fn write_document<T>(&mut self, index: usize, value: &T) -> Result<(), YamlError>
855 where
856 T: ToYamlDoc,
857 {
858 let root = self
859 .document_root(index)?
860 .ok_or_else(|| self.empty_document_error(index))?;
861 let mut nested = self.rerooted_at(root)?;
862 value.apply_to_yaml_doc(&mut nested)?;
863 self.queue_edits_from(&nested)
864 }
865
866 pub fn get_mapping_entry(
872 &self,
873 mapping: NodeId,
874 key: &str,
875 ) -> Result<Option<NodeId>, YamlError> {
876 Ok(self
877 .find_mapping_pair(mapping, key)?
878 .and_then(|(key, _)| self.containing_entry(key)))
879 }
880
881 pub fn get_mapping_value(
887 &self,
888 mapping: NodeId,
889 key: &str,
890 ) -> Result<Option<NodeId>, YamlError> {
891 Ok(self
892 .find_mapping_pair(mapping, key)?
893 .map(|(_, value)| value))
894 }
895
896 pub fn get_path(&self, path: &[&str]) -> Result<Option<NodeId>, YamlError> {
902 self.get_path_in_document(0, path)
903 }
904
905 pub fn get_path_in_document(
912 &self,
913 index: usize,
914 path: &[&str],
915 ) -> Result<Option<NodeId>, YamlError> {
916 let Some((first, rest)) = path.split_first() else {
917 return Ok(None);
918 };
919 let Some((_, mut current)) =
920 self.find_mapping_pair(self.document_root_mapping(index)?, first)?
921 else {
922 return Ok(None);
923 };
924 for segment in rest {
925 let Some((_, value)) = self.find_mapping_pair(current, segment)? else {
926 return Ok(None);
927 };
928 current = value;
929 }
930 Ok(Some(current))
931 }
932
933 fn document_index_error(&self, index: usize) -> YamlError {
934 YamlError::new(
935 Diagnostic::new(
936 DiagnosticKind::Semantic,
937 format!("document index {index} is out of range"),
938 Span::empty_from_usize(self.source.len()),
939 )
940 .with_expected("an existing document index"),
941 )
942 }
943
944 fn empty_document_error(&self, index: usize) -> YamlError {
945 YamlError::new(
946 Diagnostic::new(
947 DiagnosticKind::Typed,
948 format!("document {index} does not contain a YAML value"),
949 Span::empty_from_usize(self.source.len()),
950 )
951 .with_expected("a scalar, sequence, or mapping document root"),
952 )
953 }
954
955 fn find_mapping_pair(
956 &self,
957 mapping: NodeId,
958 key: &str,
959 ) -> Result<Option<(NodeId, NodeId)>, YamlError> {
960 for (key_node, value_node) in self.mapping_entries(mapping) {
961 if self.scalar_value(key_node)? == key {
962 return Ok(Some((key_node, value_node)));
963 }
964 }
965 Ok(None)
966 }
967
968 pub(crate) fn rerooted_at(&self, root: NodeId) -> Result<Self, YamlError> {
969 let root_node = self.expect_node(root)?;
970 if self.semantic_kind(root).is_none() {
971 return Err(YamlError::new(
972 Diagnostic::new(
973 DiagnosticKind::Semantic,
974 "typed overlay root does not have semantic metadata",
975 root_node.span,
976 )
977 .with_expected("a semantic YAML value"),
978 )
979 .with_position_from(&self.source));
980 }
981 let mut doc = self.clone();
982 doc.root_override = Some(root);
983 doc.edits.clear();
984 Ok(doc)
985 }
986
987 pub(crate) fn rerooted_without_tag(&self, root: NodeId) -> Result<Self, YamlError> {
988 let mut doc = self.rerooted_at(root)?;
989 doc.semantics.clear_tag(root);
990 Ok(doc)
991 }
992
993 pub(crate) fn rerooted_at_mapping(&self, mapping: NodeId) -> Result<Self, YamlError> {
994 let mapping_node = self.expect_node(mapping)?;
995 if !matches!(
996 mapping_node.kind,
997 NodeKind::BlockMapping | NodeKind::FlowMapping
998 ) {
999 return Err(YamlError::new(
1000 Diagnostic::new(
1001 DiagnosticKind::Semantic,
1002 format!("expected mapping, found {:?}", mapping_node.kind),
1003 mapping_node.span,
1004 )
1005 .with_expected("BlockMapping or FlowMapping"),
1006 )
1007 .with_position_from(&self.source));
1008 }
1009 if !matches!(
1010 self.semantic_kind(mapping),
1011 Some(SemanticKind::Mapping { .. })
1012 ) {
1013 return Err(YamlError::new(
1014 Diagnostic::new(
1015 DiagnosticKind::Semantic,
1016 "mapping does not have semantic metadata",
1017 self.expect_node(mapping)?.span,
1018 )
1019 .with_expected("a semantic mapping"),
1020 )
1021 .with_position_from(&self.source));
1022 }
1023 self.rerooted_at(mapping)
1024 }
1025
1026 pub(crate) fn queue_edits_from(&mut self, other: &YamlDoc) -> Result<(), YamlError> {
1027 for edit in &other.edits {
1028 self.queue_edit(edit.span, edit.replacement.clone())?;
1029 }
1030 Ok(())
1031 }
1032
1033 pub fn scalar_text(&self, node: NodeId) -> Result<&str, YamlError> {
1040 let node = self.expect_node_kind(node, NodeKind::Scalar)?;
1041 Ok(self.source.slice(node.span))
1042 }
1043
1044 pub fn scalar_value(&self, node: NodeId) -> Result<Cow<'_, str>, YamlError> {
1055 let node_ref = self.expect_node(node)?;
1056 if !matches!(
1057 node_ref.kind,
1058 NodeKind::Scalar | NodeKind::LiteralScalar | NodeKind::FoldedScalar
1059 ) {
1060 return Err(YamlError::new(
1061 Diagnostic::new(
1062 DiagnosticKind::Semantic,
1063 format!("expected scalar value, found {:?}", node_ref.kind),
1064 node_ref.span,
1065 )
1066 .with_expected("Scalar, LiteralScalar, or FoldedScalar"),
1067 )
1068 .with_position_from(&self.source));
1069 }
1070 let text = self.source.slice(node_ref.span);
1071 let properties = parse_node_properties(text, node_ref.span)?;
1072 let value_text = &text[properties.value_start..];
1073 if matches!(
1074 self.semantic_kind(node),
1075 Some(SemanticKind::Scalar {
1076 style: crate::YamlScalarStyle::Plain,
1077 ..
1078 })
1079 ) && !value_text.contains(['\n', '\r'])
1080 {
1081 return Ok(Cow::Borrowed(&value_text[..plain_scalar_end(value_text)]));
1082 }
1083 decode_scalar_value_with_content_indent(
1084 value_text,
1085 self.semantics
1086 .properties(node)
1087 .and_then(|properties| properties.content_indent)
1088 .map(|indent| indent as usize),
1089 )
1090 .map(Cow::Owned)
1091 }
1092
1093 pub fn borrowable_scalar_span(&self, node: NodeId) -> Result<Option<Span>, YamlError> {
1105 let node_ref = self.expect_node(node)?;
1106 if !matches!(
1107 self.semantic_kind(node),
1108 Some(SemanticKind::Scalar {
1109 style: crate::YamlScalarStyle::Plain,
1110 })
1111 ) {
1112 if matches!(
1113 node_ref.kind,
1114 NodeKind::Scalar | NodeKind::LiteralScalar | NodeKind::FoldedScalar
1115 ) {
1116 return Ok(None);
1117 }
1118 return Err(YamlError::new(
1119 Diagnostic::new(
1120 DiagnosticKind::Semantic,
1121 format!("expected scalar value, found {:?}", node_ref.kind),
1122 node_ref.span,
1123 )
1124 .with_expected("Scalar, LiteralScalar, or FoldedScalar"),
1125 )
1126 .with_position_from(&self.source));
1127 }
1128
1129 let text = self.source.slice(node_ref.span);
1130 if text.contains(['\n', '\r']) {
1131 return Ok(None);
1132 }
1133 let properties = parse_node_properties(text, node_ref.span)?;
1134 let value_text = &text[properties.value_start..];
1135 let value_len = plain_scalar_end(value_text);
1136 let start = Span::offset_from_usize(node_ref.span.start, properties.value_start);
1137 Ok(Some(Span::new(
1138 start,
1139 Span::offset_from_usize(start, value_len),
1140 )))
1141 }
1142
1143 pub fn set_scalar(&mut self, path: &[&str], value: &str) -> Result<(), YamlError> {
1157 let node = self.get_path(path)?.ok_or_else(|| {
1158 YamlError::new(
1159 Diagnostic::new(
1160 DiagnosticKind::Semantic,
1161 format!("path `{}` does not exist", path.join(".")),
1162 Span::empty(0),
1163 )
1164 .with_expected("an existing scalar node"),
1165 )
1166 })?;
1167
1168 let (span, style) = self.scalar_replacement_target(node)?;
1169 let replacement = format_scalar_value(value, style)?;
1170 self.queue_edit(span, replacement)
1171 }
1172
1173 pub fn replace_node_text(
1183 &mut self,
1184 node: NodeId,
1185 text: impl Into<String>,
1186 ) -> Result<(), YamlError> {
1187 let span = self.expect_node(node)?.span;
1188 self.queue_edit(span, text.into())
1189 }
1190
1191 pub fn insert_mapping_entry(
1202 &mut self,
1203 mapping: NodeId,
1204 key: &str,
1205 value: &str,
1206 style: MappingEntryStyle,
1207 ) -> Result<(), YamlError> {
1208 self.insert_mapping_entry_with_comment(mapping, key, value, style, None)
1209 }
1210
1211 pub fn insert_mapping_entry_with_comment(
1223 &mut self,
1224 mapping: NodeId,
1225 key: &str,
1226 value: &str,
1227 style: MappingEntryStyle,
1228 comment: Option<&str>,
1229 ) -> Result<(), YamlError> {
1230 let mapping_node = self.expect_node_kind(mapping, NodeKind::BlockMapping)?;
1231 let indent = match style {
1232 MappingEntryStyle::Inherit => self.node_indent(mapping_node),
1233 MappingEntryStyle::Indent(indent) => indent,
1234 };
1235 let insertion_offset = self.mapping_insertion_offset(mapping_node);
1236 let needs_leading_break =
1237 insertion_offset == self.source.len() && !self.source_ends_with_line_break();
1238 let preserve_paragraph_break = comment.is_some()
1239 && insertion_offset == self.source.len()
1240 && self.mapping_has_blank_line(mapping_node);
1241 let replacement = self.format_mapping_entry_replacement(
1242 indent,
1243 key,
1244 value,
1245 comment,
1246 needs_leading_break,
1247 preserve_paragraph_break,
1248 )?;
1249
1250 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1251 }
1252
1253 pub fn insert_mapping_value_with_comment<T>(
1261 &mut self,
1262 mapping: NodeId,
1263 key: &str,
1264 value: &T,
1265 style: MappingEntryStyle,
1266 comment: Option<&str>,
1267 ) -> Result<(), YamlError>
1268 where
1269 T: ToYamlFragment,
1270 {
1271 if matches!(
1272 self.semantic_kind(mapping),
1273 Some(SemanticKind::Mapping {
1274 style: CollectionStyle::Flow
1275 })
1276 ) {
1277 let fragment = self.typed_value_fragment(value)?;
1278 return self
1279 .queue_mapping_insert(mapping, key, &fragment)
1280 .map_err(YamlEditError::into_yaml_error);
1281 }
1282 let mapping_node = self.expect_node_kind(mapping, NodeKind::BlockMapping)?;
1283 let indent = match style {
1284 MappingEntryStyle::Inherit => self.node_indent(mapping_node),
1285 MappingEntryStyle::Indent(indent) => indent,
1286 };
1287 let insertion_offset = self.mapping_insertion_offset(mapping_node);
1288 let needs_leading_break =
1289 insertion_offset == self.source.len() && !self.source_ends_with_line_break();
1290 let preserve_paragraph_break = comment.is_some()
1291 && insertion_offset == self.source.len()
1292 && self.mapping_has_blank_line(mapping_node);
1293 let replacement = self.format_mapping_value_replacement(
1294 indent,
1295 key,
1296 value,
1297 comment,
1298 needs_leading_break,
1299 preserve_paragraph_break,
1300 )?;
1301
1302 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1303 }
1304
1305 fn typed_value_fragment<T>(&self, value: &T) -> Result<YamlFragment, YamlError>
1306 where
1307 T: ToYamlFragment,
1308 {
1309 let yaml = value.to_yaml_fragment(0, self.preferred_line_ending())?;
1310 YamlFragment::parse(&yaml).map_err(|error| {
1311 YamlError::new(
1312 Diagnostic::new(
1313 DiagnosticKind::Emitter,
1314 format!("typed YAML fragment is invalid: {error}"),
1315 Span::empty(0),
1316 )
1317 .with_expected("one valid YAML value"),
1318 )
1319 })
1320 }
1321
1322 pub fn insert_mapping_value_ordered_with_comment<T>(
1329 &mut self,
1330 mapping: NodeId,
1331 key: &str,
1332 value: &T,
1333 style: MappingEntryStyle,
1334 comment: Option<&str>,
1335 ordered_keys: &[&str],
1336 ) -> Result<(), YamlError>
1337 where
1338 T: ToYamlFragment,
1339 {
1340 let mut next_entry = None;
1341 if let Some(position) = ordered_keys.iter().position(|ordered| *ordered == key) {
1342 for later_key in &ordered_keys[position + 1..] {
1343 if let Some(entry) = self.get_mapping_entry(mapping, later_key)? {
1344 next_entry = Some(entry);
1345 break;
1346 }
1347 }
1348 }
1349
1350 if let Some(next_entry) = next_entry {
1351 self.insert_mapping_value_before_with_comment(next_entry, key, value, style, comment)
1352 } else {
1353 self.insert_mapping_value_with_comment(mapping, key, value, style, comment)
1354 }
1355 }
1356
1357 pub fn insert_mapping_entry_before_with_comment(
1365 &mut self,
1366 before_entry: NodeId,
1367 key: &str,
1368 value: &str,
1369 style: MappingEntryStyle,
1370 comment: Option<&str>,
1371 ) -> Result<(), YamlError> {
1372 let before_node = self.expect_node_kind(before_entry, NodeKind::MappingEntry)?;
1373 let indent = match style {
1374 MappingEntryStyle::Inherit => self.node_indent(before_node),
1375 MappingEntryStyle::Indent(indent) => indent,
1376 };
1377 let insertion_offset = self.line_start_for_offset(before_node.span.start as usize);
1378 let replacement =
1379 self.format_mapping_entry_replacement(indent, key, value, comment, false, false)?;
1380
1381 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1382 }
1383
1384 pub fn insert_mapping_value_before_with_comment<T>(
1391 &mut self,
1392 before_entry: NodeId,
1393 key: &str,
1394 value: &T,
1395 style: MappingEntryStyle,
1396 comment: Option<&str>,
1397 ) -> Result<(), YamlError>
1398 where
1399 T: ToYamlFragment,
1400 {
1401 let before_node = self.expect_node_kind(before_entry, NodeKind::MappingEntry)?;
1402 let mapping = before_node.parent().ok_or_else(|| {
1403 YamlError::new(
1404 Diagnostic::new(
1405 DiagnosticKind::Semantic,
1406 "mapping entry has no parent mapping",
1407 before_node.span,
1408 )
1409 .with_expected("a mapping parent"),
1410 )
1411 })?;
1412 if matches!(
1413 self.semantic_kind(mapping),
1414 Some(SemanticKind::Mapping {
1415 style: CollectionStyle::Flow
1416 })
1417 ) {
1418 let fragment = self.typed_value_fragment(value)?;
1419 return self
1420 .queue_mapping_insert_before(mapping, before_entry, key, &fragment)
1421 .map_err(YamlEditError::into_yaml_error);
1422 }
1423 let indent = match style {
1424 MappingEntryStyle::Inherit => self.node_indent(before_node),
1425 MappingEntryStyle::Indent(indent) => indent,
1426 };
1427 let insertion_offset = self.line_start_for_offset(before_node.span.start as usize);
1428 let replacement =
1429 self.format_mapping_value_replacement(indent, key, value, comment, false, false)?;
1430
1431 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1432 }
1433
1434 pub fn insert_mapping_entry_ordered_with_comment(
1446 &mut self,
1447 mapping: NodeId,
1448 key: &str,
1449 value: &str,
1450 style: MappingEntryStyle,
1451 comment: Option<&str>,
1452 ordered_keys: &[&str],
1453 ) -> Result<(), YamlError> {
1454 let mut next_entry = None;
1455 if let Some(position) = ordered_keys.iter().position(|ordered| *ordered == key) {
1456 for later_key in &ordered_keys[position + 1..] {
1457 if let Some(entry) = self.get_mapping_entry(mapping, later_key)? {
1458 next_entry = Some(entry);
1459 break;
1460 }
1461 }
1462 }
1463
1464 if let Some(next_entry) = next_entry {
1465 self.insert_mapping_entry_before_with_comment(next_entry, key, value, style, comment)
1466 } else {
1467 self.insert_mapping_entry_with_comment(mapping, key, value, style, comment)
1468 }
1469 }
1470
1471 pub fn remove_mapping_entry(&mut self, mapping: NodeId, key: &str) -> Result<(), YamlError> {
1481 let Some(entry) = self.get_mapping_entry(mapping, key)? else {
1482 return Ok(());
1483 };
1484 self.remove_collection_entries(mapping, &[entry])
1485 }
1486
1487 pub fn retain_mapping_entries(
1498 &mut self,
1499 mapping: NodeId,
1500 allowed_keys: &[&str],
1501 ) -> Result<(), YamlError> {
1502 let mapping_node = self.expect_node(mapping)?;
1503 if !matches!(
1504 mapping_node.kind,
1505 NodeKind::BlockMapping | NodeKind::FlowMapping
1506 ) {
1507 return Err(YamlError::new(
1508 Diagnostic::new(
1509 DiagnosticKind::Semantic,
1510 format!("expected mapping, found {:?}", mapping_node.kind),
1511 mapping_node.span,
1512 )
1513 .with_expected("BlockMapping or FlowMapping"),
1514 ));
1515 }
1516 let mut removals = Vec::new();
1517
1518 for entry in self.children(mapping) {
1519 let entry_node = self.expect_node(entry)?;
1520 if entry_node.kind != NodeKind::MappingEntry {
1521 continue;
1522 }
1523
1524 let Some(key_node) = self.children(entry).next() else {
1525 continue;
1526 };
1527 let key = self.scalar_value(key_node)?;
1528 if !allowed_keys.contains(&key.as_ref()) {
1529 removals.push(entry);
1530 }
1531 }
1532
1533 self.remove_collection_entries(mapping, &removals)
1534 }
1535
1536 pub(crate) fn remove_collection_entries(
1537 &mut self,
1538 collection: NodeId,
1539 removals: &[NodeId],
1540 ) -> Result<(), YamlError> {
1541 if removals.is_empty() {
1542 return Ok(());
1543 }
1544 let Some(style) = (match self.semantic_kind(collection) {
1545 Some(SemanticKind::Mapping { style } | SemanticKind::Sequence { style }) => Some(style),
1546 _ => None,
1547 }) else {
1548 return Err(YamlError::new(
1549 Diagnostic::new(
1550 DiagnosticKind::Semantic,
1551 "collection entry removal target is not a mapping or sequence",
1552 self.expect_node(collection)?.span,
1553 )
1554 .with_expected("a mapping or sequence"),
1555 ));
1556 };
1557 if style == CollectionStyle::Block {
1558 for entry in removals {
1559 self.remove_node(*entry)?;
1560 }
1561 return Ok(());
1562 }
1563
1564 let entries = self
1565 .children(collection)
1566 .filter(|node| {
1567 self.node(*node).is_some_and(|node| {
1568 matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry)
1569 })
1570 })
1571 .collect::<Vec<_>>();
1572 if removals.len() == entries.len() {
1573 let collection_node = self.expect_node(collection)?;
1574 let delimiter = match collection_node.kind {
1575 NodeKind::FlowMapping => '}',
1576 NodeKind::FlowSequence => ']',
1577 _ => unreachable!("flow semantic collection must have a flow CST node"),
1578 };
1579 if let Some(relative) = self.source.slice(collection_node.span).rfind(delimiter) {
1580 let close = Span::offset_from_usize(collection_node.span.start, relative);
1581 if let Some(edit) = self
1582 .edits
1583 .iter_mut()
1584 .find(|edit| edit.span == Span::empty(close))
1585 && let Some(replacement) = edit.replacement.strip_prefix(", ")
1586 {
1587 edit.replacement = replacement.to_owned();
1588 }
1589 }
1590 }
1591 let mut index = 0;
1592 while index < entries.len() {
1593 if !removals.contains(&entries[index]) {
1594 index += 1;
1595 continue;
1596 }
1597 let start = index;
1598 while index < entries.len() && removals.contains(&entries[index]) {
1599 index += 1;
1600 }
1601 let end = index;
1602 let first = self.expect_node(entries[start])?.span;
1603 let last = self.expect_node(entries[end - 1])?.span;
1604 let span = if let Some(next) = entries.get(end).copied() {
1605 Span::new(first.start, self.expect_node(next)?.span.start)
1606 } else if start > 0 {
1607 Span::new(self.expect_node(entries[start - 1])?.span.end, last.end)
1608 } else {
1609 Span::new(first.start, last.end)
1610 };
1611 self.queue_edit(span, String::new())?;
1612 }
1613 Ok(())
1614 }
1615
1616 pub fn remove_node(&mut self, node: NodeId) -> Result<(), YamlError> {
1626 let node = self.expect_node(node)?;
1627 let span = if matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry) {
1628 self.line_span_including_break(node.span)
1629 } else {
1630 node.span
1631 };
1632 self.queue_edit(span, String::new())
1633 }
1634
1635 pub(crate) fn scalar_replacement_target(
1636 &self,
1637 node: NodeId,
1638 ) -> Result<(Span, ScalarStyle), YamlError> {
1639 let node = self.expect_node_kind(node, NodeKind::Scalar)?;
1640 let text = self.source.slice(node.span);
1641 let properties = parse_node_properties(text, node.span)?;
1642 let value_text = &text[properties.value_start..];
1643 let value_start = Span::offset_from_usize(node.span.start, properties.value_start);
1644
1645 if value_text.starts_with('"') {
1646 let end = double_quoted_scalar_end(value_text).ok_or_else(|| {
1647 YamlError::new(
1648 Diagnostic::new(
1649 DiagnosticKind::Emitter,
1650 "could not find the end of the double-quoted scalar",
1651 node.span,
1652 )
1653 .with_expected("a closed double-quoted scalar"),
1654 )
1655 })?;
1656 return Ok((
1657 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1658 ScalarStyle::DoubleQuoted,
1659 ));
1660 }
1661
1662 if value_text.starts_with('\'') {
1663 let end = single_quoted_scalar_end(value_text).ok_or_else(|| {
1664 YamlError::new(
1665 Diagnostic::new(
1666 DiagnosticKind::Emitter,
1667 "could not find the end of the single-quoted scalar",
1668 node.span,
1669 )
1670 .with_expected("a closed single-quoted scalar"),
1671 )
1672 })?;
1673 return Ok((
1674 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1675 ScalarStyle::SingleQuoted,
1676 ));
1677 }
1678
1679 let end = plain_scalar_end(value_text);
1680 if end == 0 {
1681 return Err(YamlError::new(
1682 Diagnostic::new(
1683 DiagnosticKind::Emitter,
1684 "could not find plain scalar text to replace",
1685 node.span,
1686 )
1687 .with_expected("plain scalar text"),
1688 ));
1689 }
1690
1691 Ok((
1692 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1693 ScalarStyle::Plain,
1694 ))
1695 }
1696
1697 fn directive_nodes(&self) -> impl Iterator<Item = NodeId> + '_ {
1698 self.root()
1699 .into_iter()
1700 .flat_map(|root| self.children(root))
1701 .filter(|node| {
1702 self.node(*node)
1703 .is_some_and(|node| node.kind == NodeKind::Directive)
1704 })
1705 }
1706
1707 fn parse_directive_node(&self, node: NodeId) -> Result<ParsedDirective, YamlError> {
1708 let node_ref = self.expect_node_kind(node, NodeKind::Directive)?;
1709 let body = strip_inline_comment(self.source.slice(node_ref.span)).trim();
1710 let mut parts = body.split_whitespace();
1711 let Some(name) = parts.next() else {
1712 return Err(directive_emit_error(
1713 "directive is missing a name",
1714 node_ref.span,
1715 "%YAML, %TAG, or reserved directive syntax",
1716 )
1717 .with_position_from(&self.source));
1718 };
1719
1720 Ok(match name {
1721 "%YAML" => ParsedDirective::Yaml(YamlDirective {
1722 version: parts.next().unwrap_or_default().to_owned(),
1723 node,
1724 }),
1725 "%TAG" => ParsedDirective::Tag(TagDirective {
1726 handle: parts.next().unwrap_or_default().to_owned(),
1727 prefix: parts.next().unwrap_or_default().to_owned(),
1728 node,
1729 }),
1730 _ => ParsedDirective::Reserved(ReservedDirective {
1731 name: name.to_owned(),
1732 parameters: parts.map(str::to_owned).collect(),
1733 node,
1734 }),
1735 })
1736 }
1737
1738 fn directive_content_span(&self, node: NodeId) -> Result<Span, YamlError> {
1739 let node = self.expect_node_kind(node, NodeKind::Directive)?;
1740 let text = self.source.slice(node.span);
1741 let end = strip_inline_comment(text)
1742 .trim_end_matches([' ', '\t'])
1743 .len();
1744 Ok(Span::new(
1745 node.span.start,
1746 Span::offset_from_usize(node.span.start, end),
1747 ))
1748 }
1749
1750 fn insert_directive_line(&mut self, replacement: String) -> Result<(), YamlError> {
1751 let insertion_offset = self.directive_insertion_offset();
1752 let mut line = replacement;
1753 line.push_str(self.preferred_line_ending());
1754 self.queue_edit(Span::empty_from_usize(insertion_offset), line)
1755 }
1756
1757 fn remove_directive_node(&mut self, node: NodeId) -> Result<(), YamlError> {
1758 let node = self.expect_node_kind(node, NodeKind::Directive)?;
1759 self.queue_edit(self.line_span_including_break(node.span), String::new())
1760 }
1761
1762 fn directive_insertion_offset(&self) -> usize {
1763 if let Some(last_directive) = self
1764 .directive_nodes()
1765 .filter_map(|node| self.node(node))
1766 .max_by_key(|node| node.span.start)
1767 {
1768 return self.line_span_including_break(last_directive.span).end as usize;
1769 }
1770
1771 self.root()
1772 .and_then(|root| self.children(root).next())
1773 .and_then(|node| self.node(node))
1774 .map_or(0, |node| {
1775 self.line_start_for_offset(node.span.start as usize)
1776 })
1777 }
1778
1779 pub(crate) fn expect_node(&self, node: NodeId) -> Result<&Node, YamlError> {
1780 self.node(node).ok_or_else(|| {
1781 YamlError::new(Diagnostic::new(
1782 DiagnosticKind::Semantic,
1783 format!("unknown node id {}", node.0),
1784 Span::empty_from_usize(self.source.len()),
1785 ))
1786 })
1787 }
1788
1789 pub(crate) fn expect_node_kind(
1790 &self,
1791 node: NodeId,
1792 expected: NodeKind,
1793 ) -> Result<&Node, YamlError> {
1794 let actual = self.expect_node(node)?;
1795 if actual.kind == expected {
1796 Ok(actual)
1797 } else {
1798 Err(YamlError::new(
1799 Diagnostic::new(
1800 DiagnosticKind::Semantic,
1801 format!("expected {expected:?}, found {:?}", actual.kind),
1802 actual.span,
1803 )
1804 .with_expected(format!("{expected:?}")),
1805 )
1806 .with_position_from(&self.source))
1807 }
1808 }
1809
1810 pub(crate) fn containing_entry(&self, value: NodeId) -> Option<NodeId> {
1811 self.node(value).and_then(Node::parent).filter(|parent| {
1812 self.node(*parent).is_some_and(|node| {
1813 matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry)
1814 })
1815 })
1816 }
1817
1818 fn mapping_has_blank_line(&self, mapping: &Node) -> bool {
1819 let start = self.line_start_for_offset(mapping.span.start as usize);
1820 let end = mapping.span.end as usize;
1821 let text = &self.source.as_str()[start..end];
1822 text.contains("\n\n") || text.contains("\r\n\r\n")
1823 }
1824
1825 fn format_mapping_entry_replacement(
1826 &self,
1827 indent: usize,
1828 key: &str,
1829 value: &str,
1830 comment: Option<&str>,
1831 needs_leading_break: bool,
1832 preserve_paragraph_break: bool,
1833 ) -> Result<String, YamlError> {
1834 validate_plain_mapping_fragment(key, "mapping key")?;
1835 validate_plain_mapping_fragment(value, "mapping value")?;
1836 if let Some(comment) = comment {
1837 validate_yaml_chars(comment)?;
1838 }
1839
1840 let line_ending = self.preferred_line_ending();
1841 let indent_text = " ".repeat(indent);
1842 let mut replacement = String::new();
1843 if needs_leading_break {
1844 replacement.push_str(line_ending);
1845 }
1846 if preserve_paragraph_break {
1847 replacement.push_str(line_ending);
1848 }
1849 if let Some(comment) = comment {
1850 for line in comment.lines() {
1851 replacement.push_str(&indent_text);
1852 replacement.push('#');
1853 if !line.is_empty() {
1854 replacement.push(' ');
1855 replacement.push_str(line.trim());
1856 }
1857 replacement.push_str(line_ending);
1858 }
1859 }
1860 replacement.push_str(&indent_text);
1861 replacement.push_str(key);
1862 replacement.push_str(": ");
1863 replacement.push_str(value);
1864 replacement.push_str(line_ending);
1865 Ok(replacement)
1866 }
1867
1868 fn format_mapping_value_replacement<T>(
1869 &self,
1870 indent: usize,
1871 key: &str,
1872 value: &T,
1873 comment: Option<&str>,
1874 needs_leading_break: bool,
1875 preserve_paragraph_break: bool,
1876 ) -> Result<String, YamlError>
1877 where
1878 T: ToYamlFragment,
1879 {
1880 validate_yaml_chars(key)?;
1881 if let Some(comment) = comment {
1882 validate_yaml_chars(comment)?;
1883 }
1884
1885 let line_ending = self.preferred_line_ending();
1886 let indent_text = " ".repeat(indent);
1887 let child_indent = indent + 2;
1888 let fragment = value.to_yaml_fragment(child_indent, line_ending)?;
1889 let mut replacement = String::new();
1890 if needs_leading_break {
1891 replacement.push_str(line_ending);
1892 }
1893 if preserve_paragraph_break {
1894 replacement.push_str(line_ending);
1895 }
1896 if let Some(comment) = comment {
1897 for line in comment.lines() {
1898 replacement.push_str(&indent_text);
1899 replacement.push('#');
1900 if !line.is_empty() {
1901 replacement.push(' ');
1902 replacement.push_str(line.trim());
1903 }
1904 replacement.push_str(line_ending);
1905 }
1906 }
1907 replacement.push_str(&indent_text);
1908 replacement.push_str(&crate::edit::emit_string_key(key));
1909 if fragment.contains('\n') || fragment.starts_with(' ') {
1910 replacement.push(':');
1911 replacement.push_str(line_ending);
1912 replacement.push_str(&fragment);
1913 } else {
1914 replacement.push_str(": ");
1915 replacement.push_str(&fragment);
1916 }
1917 replacement.push_str(line_ending);
1918 Ok(replacement)
1919 }
1920
1921 pub(crate) fn node_indent(&self, node: &Node) -> usize {
1922 let line_start = self.line_start_for_offset(node.span.start as usize);
1923 self.source.as_str()[line_start..node.span.start as usize]
1924 .bytes()
1925 .filter(|byte| *byte == b' ')
1926 .count()
1927 }
1928
1929 fn line_start_for_offset(&self, offset: usize) -> usize {
1930 let offset = Span::usize_to_u32(offset);
1931 match self.source.line_starts().binary_search(&offset) {
1932 Ok(index) => self.source.line_starts()[index] as usize,
1933 Err(index) => self.source.line_starts()[index.saturating_sub(1)] as usize,
1934 }
1935 }
1936
1937 pub(crate) fn find_nested_collection_after(
1938 &self,
1939 entry: &Node,
1940 parent_indent: usize,
1941 ) -> Option<NodeId> {
1942 self.nodes
1943 .iter()
1944 .enumerate()
1945 .filter(|(_, node)| {
1946 matches!(node.kind, NodeKind::BlockMapping | NodeKind::BlockSequence)
1947 && node.span.start >= entry.span.end
1948 && self.node_indent(node) > parent_indent
1949 })
1950 .min_by_key(|(_, node)| node.span.start)
1951 .map(|(index, _)| NodeId::from_usize(index))
1952 }
1953
1954 pub(crate) fn block_scalar_content_indent(&self, scalar: &Node) -> Option<usize> {
1955 let text = self.source.slice(scalar.span);
1956 let header_end = text.find(['\r', '\n'])?;
1957 let mut rest = &text[header_end..];
1958 while let Some(stripped) = rest.strip_prefix('\r').or_else(|| rest.strip_prefix('\n')) {
1959 rest = stripped;
1960 }
1961 for line in rest.lines() {
1962 if line.trim().is_empty() {
1963 continue;
1964 }
1965 return Some(line.bytes().take_while(|byte| *byte == b' ').count());
1966 }
1967 None
1968 }
1969
1970 pub(crate) fn queue_edit(&mut self, span: Span, replacement: String) -> Result<(), YamlError> {
1971 self.source.try_slice(span)?;
1972 validate_yaml_chars(&replacement)?;
1973
1974 if span.is_empty()
1975 && let Some(existing) = self
1976 .edits
1977 .iter_mut()
1978 .find(|edit| edit.span.is_empty() && edit.span.start == span.start)
1979 {
1980 existing.replacement.push_str(&replacement);
1981 return Ok(());
1982 }
1983
1984 if let Some(existing) = self
1985 .edits
1986 .iter()
1987 .find(|edit| edits_conflict(edit.span, span))
1988 {
1989 return Err(YamlError::new(
1990 Diagnostic::new(
1991 DiagnosticKind::Emitter,
1992 "edit overlaps an existing pending edit",
1993 span,
1994 )
1995 .with_note(format!(
1996 "existing edit covers bytes {}..{}",
1997 existing.span.start, existing.span.end
1998 )),
1999 )
2000 .with_position_from(&self.source));
2001 }
2002
2003 self.edits.push(Edit { span, replacement });
2004 Ok(())
2005 }
2006
2007 pub(crate) fn mapping_insertion_offset(&self, mapping: &Node) -> usize {
2008 node_link(mapping.last_child)
2009 .and_then(|child| self.node(child))
2010 .map_or(mapping.span.end as usize, |last_child| {
2011 self.line_span_including_break(last_child.span).end as usize
2012 })
2013 }
2014
2015 pub(crate) fn sequence_insertion_offset(&self, sequence: &Node) -> usize {
2016 node_link(sequence.last_child)
2017 .and_then(|child| self.node(child))
2018 .map_or(sequence.span.end as usize, |last_child| {
2019 self.line_span_including_break(last_child.span).end as usize
2020 })
2021 }
2022
2023 fn line_span_including_break(&self, span: Span) -> Span {
2024 let start = self.line_start_for_offset(span.start as usize);
2025 let mut end = span.end as usize;
2026 let bytes = self.source.as_str().as_bytes();
2027
2028 if end < bytes.len() {
2029 if bytes[end] == b'\r' {
2030 end += 1;
2031 if end < bytes.len() && bytes[end] == b'\n' {
2032 end += 1;
2033 }
2034 } else if bytes[end] == b'\n' {
2035 end += 1;
2036 }
2037 }
2038
2039 Span::from_usize(start, end)
2040 }
2041
2042 pub(crate) fn preferred_line_ending(&self) -> &str {
2043 let bytes = self.source.as_str().as_bytes();
2044 for (index, byte) in bytes.iter().enumerate() {
2045 if *byte == b'\r' {
2046 return if bytes.get(index + 1) == Some(&b'\n') {
2047 "\r\n"
2048 } else {
2049 "\r"
2050 };
2051 }
2052 if *byte == b'\n' {
2053 return if index > 0 && bytes[index - 1] == b'\r' {
2054 "\r\n"
2055 } else {
2056 "\n"
2057 };
2058 }
2059 }
2060 "\n"
2061 }
2062
2063 fn source_ends_with_line_break(&self) -> bool {
2064 self.source
2065 .as_str()
2066 .as_bytes()
2067 .last()
2068 .is_some_and(|byte| matches!(byte, b'\n' | b'\r'))
2069 }
2070
2071 fn document_append_prefix(&self, line_ending: &str) -> String {
2072 if self.source.as_str().is_empty() || self.source_ends_with_line_break() {
2073 String::new()
2074 } else {
2075 line_ending.to_owned()
2076 }
2077 }
2078}
2079impl fmt::Display for YamlDoc {
2080 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2081 if self.edits.is_empty() {
2082 return formatter.write_str(self.source.as_str());
2083 }
2084
2085 let mut output = self.source.as_str().to_owned();
2086 let mut edits = self.edits.clone();
2087 edits.sort_by_key(|edit| std::cmp::Reverse(edit.span.start));
2088
2089 for edit in edits {
2090 output.replace_range(
2091 edit.span.start as usize..edit.span.end as usize,
2092 &edit.replacement,
2093 );
2094 }
2095
2096 formatter.write_str(&output)
2097 }
2098}