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> {
637 let Some(raw) = self.raw_tag(node) else {
638 return Ok(None);
639 };
640 let document = self.semantics.property_document(node).unwrap_or(node);
641 let handles = self
642 .semantics
643 .tag_directives(document)
644 .map(|(handle, prefix)| {
645 (
646 self.source.slice(handle).to_owned(),
647 self.source.slice(prefix).to_owned(),
648 )
649 })
650 .collect::<BTreeMap<_, _>>();
651 let span = self
652 .semantics
653 .properties(node)
654 .and_then(|properties| properties.tag)
655 .unwrap_or_else(|| self.node(node).map_or(Span::empty(0), Node::span));
656 resolve_tag(raw, &handles, span).map(|tag| Some(Cow::Owned(tag)))
657 }
658
659 #[must_use]
661 pub fn anchor(&self, node: NodeId) -> Option<&str> {
662 let span = self.semantics.properties(node)?.anchor?;
663 Some(self.source.slice(span))
664 }
665
666 #[must_use]
668 pub fn alias_name(&self, node: NodeId) -> Option<&str> {
669 let span = self.semantics.properties(node)?.alias?;
670 Some(self.source.slice(span))
671 }
672
673 #[must_use]
675 pub fn resolve_alias(&self, node: NodeId) -> Option<NodeId> {
676 let name = self.alias_name(node)?;
677 let document = self.semantics.property_document(node)?;
678 let alias_start = self.node(node)?.span.start;
679 self.semantics
680 .anchors()
681 .rev()
682 .find(|(span, target, anchor_document)| {
683 *anchor_document == document
684 && self
685 .node(*target)
686 .is_some_and(|node| node.span.start <= alias_start)
687 && self.source.slice(*span) == name
688 })
689 .map(|(_, target, _)| target)
690 }
691
692 fn semantic_span(&self, node: NodeId, metadata: &crate::semantic::SemanticNode) -> Span {
693 Span::new(
694 metadata.span_start,
695 self.node(node)
696 .map_or(metadata.end_offset, |node| node.span.end),
697 )
698 }
699
700 fn semantic_end_span(&self, node: NodeId, metadata: &crate::semantic::SemanticNode) -> Span {
701 if metadata.explicit_end()
702 && let Some(marker) = self.children(node).find_map(|child| {
703 let child = self.node(child)?;
704 (child.kind == NodeKind::DocumentMarker
705 && self.source.slice(child.span).starts_with("..."))
706 .then_some(child.span)
707 })
708 {
709 return marker;
710 }
711 if matches!(
712 metadata.kind,
713 SemanticKind::Mapping {
714 style: CollectionStyle::Flow
715 } | SemanticKind::Sequence {
716 style: CollectionStyle::Flow
717 }
718 ) {
719 return self.semantic_span(node, metadata);
720 }
721 Span::empty(metadata.end_offset)
722 }
723
724 pub fn documents(&self) -> impl Iterator<Item = NodeId> + '_ {
726 self.semantics.documents.iter().copied()
727 }
728
729 pub fn document_root(&self, index: usize) -> Result<Option<NodeId>, YamlError> {
739 if let Some(root) = self.root_override {
740 return (index == 0)
741 .then_some(Some(root))
742 .ok_or_else(|| self.document_index_error(index));
743 }
744 let document = self
745 .semantics
746 .documents
747 .get(index)
748 .copied()
749 .ok_or_else(|| self.document_index_error(index))?;
750 let root = self.semantic_children(document).next();
751 Ok(root.filter(|root| {
752 !matches!(self.semantic_kind(*root), Some(SemanticKind::Scalar { .. }))
753 || self.node(*root).is_some_and(|node| !node.span.is_empty())
754 }))
755 }
756
757 pub fn mapping_entries(&self, mapping: NodeId) -> impl Iterator<Item = (NodeId, NodeId)> + '_ {
759 let is_mapping = matches!(
760 self.semantic_kind(mapping),
761 Some(SemanticKind::Mapping { .. })
762 );
763 self.children(mapping).filter_map(move |entry| {
764 if !is_mapping || self.node(entry)?.kind != NodeKind::MappingEntry {
765 return None;
766 }
767 let mut children = self.semantic_children(entry);
768 Some((children.next()?, children.next()?))
769 })
770 }
771
772 pub fn sequence_items(&self, sequence: NodeId) -> impl Iterator<Item = NodeId> + '_ {
774 let is_sequence = matches!(
775 self.semantic_kind(sequence),
776 Some(SemanticKind::Sequence { .. })
777 );
778 self.children(sequence).filter_map(move |entry| {
779 if !is_sequence || self.node(entry)?.kind != NodeKind::SequenceEntry {
780 return None;
781 }
782 self.semantic_children(entry).next()
783 })
784 }
785
786 pub fn root_mapping(&self) -> Result<NodeId, YamlError> {
793 self.document_root_mapping(0)
794 }
795
796 pub fn document_root_mapping(&self, index: usize) -> Result<NodeId, YamlError> {
803 if let Some(root) = self.root_override {
804 return (index == 0)
805 .then_some(root)
806 .ok_or_else(|| self.document_index_error(index));
807 }
808 let document = self
809 .semantics
810 .documents
811 .get(index)
812 .copied()
813 .ok_or_else(|| self.document_index_error(index))?;
814 self.semantic_children(document)
815 .find(|child| {
816 self.node(*child).is_some_and(|node| {
817 matches!(node.kind, NodeKind::BlockMapping | NodeKind::FlowMapping)
818 }) && matches!(
819 self.semantic_kind(*child),
820 Some(SemanticKind::Mapping { .. })
821 )
822 })
823 .ok_or_else(|| {
824 YamlError::new(
825 Diagnostic::new(
826 DiagnosticKind::Semantic,
827 "document does not contain a root mapping",
828 self.node(document).map_or(Span::empty(0), |node| node.span),
829 )
830 .with_expected("a block or flow mapping node"),
831 )
832 })
833 }
834
835 pub fn read_document<T>(&self, index: usize) -> Result<T, YamlError>
842 where
843 T: FromYamlDoc,
844 {
845 let root = self
846 .document_root(index)?
847 .ok_or_else(|| self.empty_document_error(index))?;
848 let nested = self.rerooted_at(root)?;
849 T::from_yaml_doc(&nested)
850 }
851
852 pub fn write_document<T>(&mut self, index: usize, value: &T) -> Result<(), YamlError>
859 where
860 T: ToYamlDoc,
861 {
862 let root = self
863 .document_root(index)?
864 .ok_or_else(|| self.empty_document_error(index))?;
865 let mut nested = self.rerooted_at(root)?;
866 value.apply_to_yaml_doc(&mut nested)?;
867 self.queue_edits_from(&nested)
868 }
869
870 pub fn get_mapping_entry(
876 &self,
877 mapping: NodeId,
878 key: &str,
879 ) -> Result<Option<NodeId>, YamlError> {
880 Ok(self
881 .find_mapping_pair(mapping, key)?
882 .and_then(|(key, _)| self.containing_entry(key)))
883 }
884
885 pub fn get_mapping_value(
891 &self,
892 mapping: NodeId,
893 key: &str,
894 ) -> Result<Option<NodeId>, YamlError> {
895 Ok(self
896 .find_mapping_pair(mapping, key)?
897 .map(|(_, value)| value))
898 }
899
900 pub fn get_path(&self, path: &[&str]) -> Result<Option<NodeId>, YamlError> {
906 self.get_path_in_document(0, path)
907 }
908
909 pub fn get_path_in_document(
916 &self,
917 index: usize,
918 path: &[&str],
919 ) -> Result<Option<NodeId>, YamlError> {
920 let Some((first, rest)) = path.split_first() else {
921 return Ok(None);
922 };
923 let Some((_, mut current)) =
924 self.find_mapping_pair(self.document_root_mapping(index)?, first)?
925 else {
926 return Ok(None);
927 };
928 for segment in rest {
929 let Some((_, value)) = self.find_mapping_pair(current, segment)? else {
930 return Ok(None);
931 };
932 current = value;
933 }
934 Ok(Some(current))
935 }
936
937 fn document_index_error(&self, index: usize) -> YamlError {
938 YamlError::new(
939 Diagnostic::new(
940 DiagnosticKind::Semantic,
941 format!("document index {index} is out of range"),
942 Span::empty_from_usize(self.source.len()),
943 )
944 .with_expected("an existing document index"),
945 )
946 }
947
948 fn empty_document_error(&self, index: usize) -> YamlError {
949 YamlError::new(
950 Diagnostic::new(
951 DiagnosticKind::Typed,
952 format!("document {index} does not contain a YAML value"),
953 Span::empty_from_usize(self.source.len()),
954 )
955 .with_expected("a scalar, sequence, or mapping document root"),
956 )
957 }
958
959 fn find_mapping_pair(
960 &self,
961 mapping: NodeId,
962 key: &str,
963 ) -> Result<Option<(NodeId, NodeId)>, YamlError> {
964 for (key_node, value_node) in self.mapping_entries(mapping) {
965 if self.scalar_value(key_node)? == key {
966 return Ok(Some((key_node, value_node)));
967 }
968 }
969 Ok(None)
970 }
971
972 pub(crate) fn rerooted_at(&self, root: NodeId) -> Result<Self, YamlError> {
973 let root_node = self.expect_node(root)?;
974 if self.semantic_kind(root).is_none() {
975 return Err(YamlError::new(
976 Diagnostic::new(
977 DiagnosticKind::Semantic,
978 "typed overlay root does not have semantic metadata",
979 root_node.span,
980 )
981 .with_expected("a semantic YAML value"),
982 )
983 .with_position_from(&self.source));
984 }
985 let mut doc = self.clone();
986 doc.root_override = Some(root);
987 doc.edits.clear();
988 Ok(doc)
989 }
990
991 pub(crate) fn rerooted_without_tag(&self, root: NodeId) -> Result<Self, YamlError> {
992 let mut doc = self.rerooted_at(root)?;
993 doc.semantics.clear_tag(root);
994 Ok(doc)
995 }
996
997 pub(crate) fn rerooted_at_mapping(&self, mapping: NodeId) -> Result<Self, YamlError> {
998 let mapping_node = self.expect_node(mapping)?;
999 if !matches!(
1000 mapping_node.kind,
1001 NodeKind::BlockMapping | NodeKind::FlowMapping
1002 ) {
1003 return Err(YamlError::new(
1004 Diagnostic::new(
1005 DiagnosticKind::Semantic,
1006 format!("expected mapping, found {:?}", mapping_node.kind),
1007 mapping_node.span,
1008 )
1009 .with_expected("BlockMapping or FlowMapping"),
1010 )
1011 .with_position_from(&self.source));
1012 }
1013 if !matches!(
1014 self.semantic_kind(mapping),
1015 Some(SemanticKind::Mapping { .. })
1016 ) {
1017 return Err(YamlError::new(
1018 Diagnostic::new(
1019 DiagnosticKind::Semantic,
1020 "mapping does not have semantic metadata",
1021 self.expect_node(mapping)?.span,
1022 )
1023 .with_expected("a semantic mapping"),
1024 )
1025 .with_position_from(&self.source));
1026 }
1027 self.rerooted_at(mapping)
1028 }
1029
1030 pub(crate) fn queue_edits_from(&mut self, other: &YamlDoc) -> Result<(), YamlError> {
1031 for edit in &other.edits {
1032 self.queue_edit(edit.span, edit.replacement.clone())?;
1033 }
1034 Ok(())
1035 }
1036
1037 pub fn scalar_text(&self, node: NodeId) -> Result<&str, YamlError> {
1044 let node = self.expect_node_kind(node, NodeKind::Scalar)?;
1045 Ok(self.source.slice(node.span))
1046 }
1047
1048 pub fn scalar_value(&self, node: NodeId) -> Result<Cow<'_, str>, YamlError> {
1059 let node_ref = self.expect_node(node)?;
1060 if !matches!(
1061 node_ref.kind,
1062 NodeKind::Scalar | NodeKind::LiteralScalar | NodeKind::FoldedScalar
1063 ) {
1064 return Err(YamlError::new(
1065 Diagnostic::new(
1066 DiagnosticKind::Semantic,
1067 format!("expected scalar value, found {:?}", node_ref.kind),
1068 node_ref.span,
1069 )
1070 .with_expected("Scalar, LiteralScalar, or FoldedScalar"),
1071 )
1072 .with_position_from(&self.source));
1073 }
1074 let text = self.source.slice(node_ref.span);
1075 let properties = parse_node_properties(text, node_ref.span)?;
1076 let value_text = &text[properties.value_start..];
1077 if matches!(
1078 self.semantic_kind(node),
1079 Some(SemanticKind::Scalar {
1080 style: crate::YamlScalarStyle::Plain,
1081 ..
1082 })
1083 ) && !value_text.contains(['\n', '\r'])
1084 {
1085 return Ok(Cow::Borrowed(&value_text[..plain_scalar_end(value_text)]));
1086 }
1087 decode_scalar_value_with_content_indent(
1088 value_text,
1089 self.semantics
1090 .properties(node)
1091 .and_then(|properties| properties.content_indent)
1092 .map(|indent| indent as usize),
1093 )
1094 .map(Cow::Owned)
1095 }
1096
1097 pub fn borrowable_scalar_span(&self, node: NodeId) -> Result<Option<Span>, YamlError> {
1109 let node_ref = self.expect_node(node)?;
1110 if !matches!(
1111 self.semantic_kind(node),
1112 Some(SemanticKind::Scalar {
1113 style: crate::YamlScalarStyle::Plain,
1114 })
1115 ) {
1116 if matches!(
1117 node_ref.kind,
1118 NodeKind::Scalar | NodeKind::LiteralScalar | NodeKind::FoldedScalar
1119 ) {
1120 return Ok(None);
1121 }
1122 return Err(YamlError::new(
1123 Diagnostic::new(
1124 DiagnosticKind::Semantic,
1125 format!("expected scalar value, found {:?}", node_ref.kind),
1126 node_ref.span,
1127 )
1128 .with_expected("Scalar, LiteralScalar, or FoldedScalar"),
1129 )
1130 .with_position_from(&self.source));
1131 }
1132
1133 let text = self.source.slice(node_ref.span);
1134 if text.contains(['\n', '\r']) {
1135 return Ok(None);
1136 }
1137 let properties = parse_node_properties(text, node_ref.span)?;
1138 let value_text = &text[properties.value_start..];
1139 let value_len = plain_scalar_end(value_text);
1140 let start = Span::offset_from_usize(node_ref.span.start, properties.value_start);
1141 Ok(Some(Span::new(
1142 start,
1143 Span::offset_from_usize(start, value_len),
1144 )))
1145 }
1146
1147 pub fn set_scalar(&mut self, path: &[&str], value: &str) -> Result<(), YamlError> {
1161 let node = self.get_path(path)?.ok_or_else(|| {
1162 YamlError::new(
1163 Diagnostic::new(
1164 DiagnosticKind::Semantic,
1165 format!("path `{}` does not exist", path.join(".")),
1166 Span::empty(0),
1167 )
1168 .with_expected("an existing scalar node"),
1169 )
1170 })?;
1171
1172 let (span, style) = self.scalar_replacement_target(node)?;
1173 let replacement = format_scalar_value(value, style)?;
1174 self.queue_edit(span, replacement)
1175 }
1176
1177 pub fn replace_node_text(
1187 &mut self,
1188 node: NodeId,
1189 text: impl Into<String>,
1190 ) -> Result<(), YamlError> {
1191 let span = self.expect_node(node)?.span;
1192 self.queue_edit(span, text.into())
1193 }
1194
1195 pub fn insert_mapping_entry(
1206 &mut self,
1207 mapping: NodeId,
1208 key: &str,
1209 value: &str,
1210 style: MappingEntryStyle,
1211 ) -> Result<(), YamlError> {
1212 self.insert_mapping_entry_with_comment(mapping, key, value, style, None)
1213 }
1214
1215 pub fn insert_mapping_entry_with_comment(
1227 &mut self,
1228 mapping: NodeId,
1229 key: &str,
1230 value: &str,
1231 style: MappingEntryStyle,
1232 comment: Option<&str>,
1233 ) -> Result<(), YamlError> {
1234 let mapping_node = self.expect_node_kind(mapping, NodeKind::BlockMapping)?;
1235 let indent = match style {
1236 MappingEntryStyle::Inherit => self.node_indent(mapping_node),
1237 MappingEntryStyle::Indent(indent) => indent,
1238 };
1239 let insertion_offset = self.mapping_insertion_offset(mapping_node);
1240 let needs_leading_break =
1241 insertion_offset == self.source.len() && !self.source_ends_with_line_break();
1242 let preserve_paragraph_break = comment.is_some()
1243 && insertion_offset == self.source.len()
1244 && self.mapping_has_blank_line(mapping_node);
1245 let replacement = self.format_mapping_entry_replacement(
1246 indent,
1247 key,
1248 value,
1249 comment,
1250 needs_leading_break,
1251 preserve_paragraph_break,
1252 )?;
1253
1254 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1255 }
1256
1257 pub fn insert_mapping_value_with_comment<T>(
1265 &mut self,
1266 mapping: NodeId,
1267 key: &str,
1268 value: &T,
1269 style: MappingEntryStyle,
1270 comment: Option<&str>,
1271 ) -> Result<(), YamlError>
1272 where
1273 T: ToYamlFragment,
1274 {
1275 if matches!(
1276 self.semantic_kind(mapping),
1277 Some(SemanticKind::Mapping {
1278 style: CollectionStyle::Flow
1279 })
1280 ) {
1281 let fragment = self.typed_value_fragment(value)?;
1282 return self
1283 .queue_mapping_insert(mapping, key, &fragment)
1284 .map_err(YamlEditError::into_yaml_error);
1285 }
1286 let mapping_node = self.expect_node_kind(mapping, NodeKind::BlockMapping)?;
1287 let indent = match style {
1288 MappingEntryStyle::Inherit => self.node_indent(mapping_node),
1289 MappingEntryStyle::Indent(indent) => indent,
1290 };
1291 let insertion_offset = self.mapping_insertion_offset(mapping_node);
1292 let needs_leading_break =
1293 insertion_offset == self.source.len() && !self.source_ends_with_line_break();
1294 let preserve_paragraph_break = comment.is_some()
1295 && insertion_offset == self.source.len()
1296 && self.mapping_has_blank_line(mapping_node);
1297 let replacement = self.format_mapping_value_replacement(
1298 indent,
1299 key,
1300 value,
1301 comment,
1302 needs_leading_break,
1303 preserve_paragraph_break,
1304 )?;
1305
1306 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1307 }
1308
1309 fn typed_value_fragment<T>(&self, value: &T) -> Result<YamlFragment, YamlError>
1310 where
1311 T: ToYamlFragment,
1312 {
1313 let yaml = value.to_yaml_fragment(0, self.preferred_line_ending())?;
1314 YamlFragment::parse(&yaml).map_err(|error| {
1315 YamlError::new(
1316 Diagnostic::new(
1317 DiagnosticKind::Emitter,
1318 format!("typed YAML fragment is invalid: {error}"),
1319 Span::empty(0),
1320 )
1321 .with_expected("one valid YAML value"),
1322 )
1323 })
1324 }
1325
1326 pub fn insert_mapping_value_ordered_with_comment<T>(
1333 &mut self,
1334 mapping: NodeId,
1335 key: &str,
1336 value: &T,
1337 style: MappingEntryStyle,
1338 comment: Option<&str>,
1339 ordered_keys: &[&str],
1340 ) -> Result<(), YamlError>
1341 where
1342 T: ToYamlFragment,
1343 {
1344 let mut next_entry = None;
1345 if let Some(position) = ordered_keys.iter().position(|ordered| *ordered == key) {
1346 for later_key in &ordered_keys[position + 1..] {
1347 if let Some(entry) = self.get_mapping_entry(mapping, later_key)? {
1348 next_entry = Some(entry);
1349 break;
1350 }
1351 }
1352 }
1353
1354 if let Some(next_entry) = next_entry {
1355 self.insert_mapping_value_before_with_comment(next_entry, key, value, style, comment)
1356 } else {
1357 self.insert_mapping_value_with_comment(mapping, key, value, style, comment)
1358 }
1359 }
1360
1361 pub fn insert_mapping_entry_before_with_comment(
1369 &mut self,
1370 before_entry: NodeId,
1371 key: &str,
1372 value: &str,
1373 style: MappingEntryStyle,
1374 comment: Option<&str>,
1375 ) -> Result<(), YamlError> {
1376 let before_node = self.expect_node_kind(before_entry, NodeKind::MappingEntry)?;
1377 let indent = match style {
1378 MappingEntryStyle::Inherit => self.node_indent(before_node),
1379 MappingEntryStyle::Indent(indent) => indent,
1380 };
1381 let insertion_offset = self.line_start_for_offset(before_node.span.start as usize);
1382 let replacement =
1383 self.format_mapping_entry_replacement(indent, key, value, comment, false, false)?;
1384
1385 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1386 }
1387
1388 pub fn insert_mapping_value_before_with_comment<T>(
1395 &mut self,
1396 before_entry: NodeId,
1397 key: &str,
1398 value: &T,
1399 style: MappingEntryStyle,
1400 comment: Option<&str>,
1401 ) -> Result<(), YamlError>
1402 where
1403 T: ToYamlFragment,
1404 {
1405 let before_node = self.expect_node_kind(before_entry, NodeKind::MappingEntry)?;
1406 let mapping = before_node.parent().ok_or_else(|| {
1407 YamlError::new(
1408 Diagnostic::new(
1409 DiagnosticKind::Semantic,
1410 "mapping entry has no parent mapping",
1411 before_node.span,
1412 )
1413 .with_expected("a mapping parent"),
1414 )
1415 })?;
1416 if matches!(
1417 self.semantic_kind(mapping),
1418 Some(SemanticKind::Mapping {
1419 style: CollectionStyle::Flow
1420 })
1421 ) {
1422 let fragment = self.typed_value_fragment(value)?;
1423 return self
1424 .queue_mapping_insert_before(mapping, before_entry, key, &fragment)
1425 .map_err(YamlEditError::into_yaml_error);
1426 }
1427 let indent = match style {
1428 MappingEntryStyle::Inherit => self.node_indent(before_node),
1429 MappingEntryStyle::Indent(indent) => indent,
1430 };
1431 let insertion_offset = self.line_start_for_offset(before_node.span.start as usize);
1432 let replacement =
1433 self.format_mapping_value_replacement(indent, key, value, comment, false, false)?;
1434
1435 self.queue_edit(Span::empty_from_usize(insertion_offset), replacement)
1436 }
1437
1438 pub fn insert_mapping_entry_ordered_with_comment(
1450 &mut self,
1451 mapping: NodeId,
1452 key: &str,
1453 value: &str,
1454 style: MappingEntryStyle,
1455 comment: Option<&str>,
1456 ordered_keys: &[&str],
1457 ) -> Result<(), YamlError> {
1458 let mut next_entry = None;
1459 if let Some(position) = ordered_keys.iter().position(|ordered| *ordered == key) {
1460 for later_key in &ordered_keys[position + 1..] {
1461 if let Some(entry) = self.get_mapping_entry(mapping, later_key)? {
1462 next_entry = Some(entry);
1463 break;
1464 }
1465 }
1466 }
1467
1468 if let Some(next_entry) = next_entry {
1469 self.insert_mapping_entry_before_with_comment(next_entry, key, value, style, comment)
1470 } else {
1471 self.insert_mapping_entry_with_comment(mapping, key, value, style, comment)
1472 }
1473 }
1474
1475 pub fn remove_mapping_entry(&mut self, mapping: NodeId, key: &str) -> Result<(), YamlError> {
1485 let Some(entry) = self.get_mapping_entry(mapping, key)? else {
1486 return Ok(());
1487 };
1488 self.remove_collection_entries(mapping, &[entry])
1489 }
1490
1491 pub fn retain_mapping_entries(
1502 &mut self,
1503 mapping: NodeId,
1504 allowed_keys: &[&str],
1505 ) -> Result<(), YamlError> {
1506 let mapping_node = self.expect_node(mapping)?;
1507 if !matches!(
1508 mapping_node.kind,
1509 NodeKind::BlockMapping | NodeKind::FlowMapping
1510 ) {
1511 return Err(YamlError::new(
1512 Diagnostic::new(
1513 DiagnosticKind::Semantic,
1514 format!("expected mapping, found {:?}", mapping_node.kind),
1515 mapping_node.span,
1516 )
1517 .with_expected("BlockMapping or FlowMapping"),
1518 ));
1519 }
1520 let mut removals = Vec::new();
1521
1522 for entry in self.children(mapping) {
1523 let entry_node = self.expect_node(entry)?;
1524 if entry_node.kind != NodeKind::MappingEntry {
1525 continue;
1526 }
1527
1528 let Some(key_node) = self.children(entry).next() else {
1529 continue;
1530 };
1531 let key = self.scalar_value(key_node)?;
1532 if !allowed_keys.contains(&key.as_ref()) {
1533 removals.push(entry);
1534 }
1535 }
1536
1537 self.remove_collection_entries(mapping, &removals)
1538 }
1539
1540 pub(crate) fn remove_collection_entries(
1541 &mut self,
1542 collection: NodeId,
1543 removals: &[NodeId],
1544 ) -> Result<(), YamlError> {
1545 if removals.is_empty() {
1546 return Ok(());
1547 }
1548 let Some(style) = (match self.semantic_kind(collection) {
1549 Some(SemanticKind::Mapping { style } | SemanticKind::Sequence { style }) => Some(style),
1550 _ => None,
1551 }) else {
1552 return Err(YamlError::new(
1553 Diagnostic::new(
1554 DiagnosticKind::Semantic,
1555 "collection entry removal target is not a mapping or sequence",
1556 self.expect_node(collection)?.span,
1557 )
1558 .with_expected("a mapping or sequence"),
1559 ));
1560 };
1561 if style == CollectionStyle::Block {
1562 for entry in removals {
1563 self.remove_node(*entry)?;
1564 }
1565 return Ok(());
1566 }
1567
1568 let entries = self
1569 .children(collection)
1570 .filter(|node| {
1571 self.node(*node).is_some_and(|node| {
1572 matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry)
1573 })
1574 })
1575 .collect::<Vec<_>>();
1576 if removals.len() == entries.len() {
1577 let collection_node = self.expect_node(collection)?;
1578 let delimiter = match collection_node.kind {
1579 NodeKind::FlowMapping => '}',
1580 NodeKind::FlowSequence => ']',
1581 _ => unreachable!("flow semantic collection must have a flow CST node"),
1582 };
1583 if let Some(relative) = self.source.slice(collection_node.span).rfind(delimiter) {
1584 let close = Span::offset_from_usize(collection_node.span.start, relative);
1585 if let Some(edit) = self
1586 .edits
1587 .iter_mut()
1588 .find(|edit| edit.span == Span::empty(close))
1589 && let Some(replacement) = edit.replacement.strip_prefix(", ")
1590 {
1591 edit.replacement = replacement.to_owned();
1592 }
1593 }
1594 }
1595 let mut index = 0;
1596 while index < entries.len() {
1597 if !removals.contains(&entries[index]) {
1598 index += 1;
1599 continue;
1600 }
1601 let start = index;
1602 while index < entries.len() && removals.contains(&entries[index]) {
1603 index += 1;
1604 }
1605 let end = index;
1606 let first = self.expect_node(entries[start])?.span;
1607 let last = self.expect_node(entries[end - 1])?.span;
1608 let span = if let Some(next) = entries.get(end).copied() {
1609 Span::new(first.start, self.expect_node(next)?.span.start)
1610 } else if start > 0 {
1611 Span::new(self.expect_node(entries[start - 1])?.span.end, last.end)
1612 } else {
1613 Span::new(first.start, last.end)
1614 };
1615 self.queue_edit(span, String::new())?;
1616 }
1617 Ok(())
1618 }
1619
1620 pub fn remove_node(&mut self, node: NodeId) -> Result<(), YamlError> {
1630 let node = self.expect_node(node)?;
1631 let span = if matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry) {
1632 self.line_span_including_break(node.span)
1633 } else {
1634 node.span
1635 };
1636 self.queue_edit(span, String::new())
1637 }
1638
1639 pub(crate) fn scalar_replacement_target(
1640 &self,
1641 node: NodeId,
1642 ) -> Result<(Span, ScalarStyle), YamlError> {
1643 let node = self.expect_node_kind(node, NodeKind::Scalar)?;
1644 let text = self.source.slice(node.span);
1645 let properties = parse_node_properties(text, node.span)?;
1646 let value_text = &text[properties.value_start..];
1647 let value_start = Span::offset_from_usize(node.span.start, properties.value_start);
1648
1649 if value_text.starts_with('"') {
1650 let end = double_quoted_scalar_end(value_text).ok_or_else(|| {
1651 YamlError::new(
1652 Diagnostic::new(
1653 DiagnosticKind::Emitter,
1654 "could not find the end of the double-quoted scalar",
1655 node.span,
1656 )
1657 .with_expected("a closed double-quoted scalar"),
1658 )
1659 })?;
1660 return Ok((
1661 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1662 ScalarStyle::DoubleQuoted,
1663 ));
1664 }
1665
1666 if value_text.starts_with('\'') {
1667 let end = single_quoted_scalar_end(value_text).ok_or_else(|| {
1668 YamlError::new(
1669 Diagnostic::new(
1670 DiagnosticKind::Emitter,
1671 "could not find the end of the single-quoted scalar",
1672 node.span,
1673 )
1674 .with_expected("a closed single-quoted scalar"),
1675 )
1676 })?;
1677 return Ok((
1678 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1679 ScalarStyle::SingleQuoted,
1680 ));
1681 }
1682
1683 let end = plain_scalar_end(value_text);
1684 if end == 0 {
1685 return Err(YamlError::new(
1686 Diagnostic::new(
1687 DiagnosticKind::Emitter,
1688 "could not find plain scalar text to replace",
1689 node.span,
1690 )
1691 .with_expected("plain scalar text"),
1692 ));
1693 }
1694
1695 Ok((
1696 Span::new(value_start, Span::offset_from_usize(value_start, end)),
1697 ScalarStyle::Plain,
1698 ))
1699 }
1700
1701 fn directive_nodes(&self) -> impl Iterator<Item = NodeId> + '_ {
1702 self.root()
1703 .into_iter()
1704 .flat_map(|root| self.children(root))
1705 .filter(|node| {
1706 self.node(*node)
1707 .is_some_and(|node| node.kind == NodeKind::Directive)
1708 })
1709 }
1710
1711 fn parse_directive_node(&self, node: NodeId) -> Result<ParsedDirective, YamlError> {
1712 let node_ref = self.expect_node_kind(node, NodeKind::Directive)?;
1713 let body = strip_inline_comment(self.source.slice(node_ref.span)).trim();
1714 let mut parts = body.split_whitespace();
1715 let Some(name) = parts.next() else {
1716 return Err(directive_emit_error(
1717 "directive is missing a name",
1718 node_ref.span,
1719 "%YAML, %TAG, or reserved directive syntax",
1720 )
1721 .with_position_from(&self.source));
1722 };
1723
1724 Ok(match name {
1725 "%YAML" => ParsedDirective::Yaml(YamlDirective {
1726 version: parts.next().unwrap_or_default().to_owned(),
1727 node,
1728 }),
1729 "%TAG" => ParsedDirective::Tag(TagDirective {
1730 handle: parts.next().unwrap_or_default().to_owned(),
1731 prefix: parts.next().unwrap_or_default().to_owned(),
1732 node,
1733 }),
1734 _ => ParsedDirective::Reserved(ReservedDirective {
1735 name: name.to_owned(),
1736 parameters: parts.map(str::to_owned).collect(),
1737 node,
1738 }),
1739 })
1740 }
1741
1742 fn directive_content_span(&self, node: NodeId) -> Result<Span, YamlError> {
1743 let node = self.expect_node_kind(node, NodeKind::Directive)?;
1744 let text = self.source.slice(node.span);
1745 let end = strip_inline_comment(text)
1746 .trim_end_matches([' ', '\t'])
1747 .len();
1748 Ok(Span::new(
1749 node.span.start,
1750 Span::offset_from_usize(node.span.start, end),
1751 ))
1752 }
1753
1754 fn insert_directive_line(&mut self, replacement: String) -> Result<(), YamlError> {
1755 let insertion_offset = self.directive_insertion_offset();
1756 let mut line = replacement;
1757 line.push_str(self.preferred_line_ending());
1758 self.queue_edit(Span::empty_from_usize(insertion_offset), line)
1759 }
1760
1761 fn remove_directive_node(&mut self, node: NodeId) -> Result<(), YamlError> {
1762 let node = self.expect_node_kind(node, NodeKind::Directive)?;
1763 self.queue_edit(self.line_span_including_break(node.span), String::new())
1764 }
1765
1766 fn directive_insertion_offset(&self) -> usize {
1767 if let Some(last_directive) = self
1768 .directive_nodes()
1769 .filter_map(|node| self.node(node))
1770 .max_by_key(|node| node.span.start)
1771 {
1772 return self.line_span_including_break(last_directive.span).end as usize;
1773 }
1774
1775 self.root()
1776 .and_then(|root| self.children(root).next())
1777 .and_then(|node| self.node(node))
1778 .map_or(0, |node| {
1779 self.line_start_for_offset(node.span.start as usize)
1780 })
1781 }
1782
1783 pub(crate) fn expect_node(&self, node: NodeId) -> Result<&Node, YamlError> {
1784 self.node(node).ok_or_else(|| {
1785 YamlError::new(Diagnostic::new(
1786 DiagnosticKind::Semantic,
1787 format!("unknown node id {}", node.0),
1788 Span::empty_from_usize(self.source.len()),
1789 ))
1790 })
1791 }
1792
1793 pub(crate) fn expect_node_kind(
1794 &self,
1795 node: NodeId,
1796 expected: NodeKind,
1797 ) -> Result<&Node, YamlError> {
1798 let actual = self.expect_node(node)?;
1799 if actual.kind == expected {
1800 Ok(actual)
1801 } else {
1802 Err(YamlError::new(
1803 Diagnostic::new(
1804 DiagnosticKind::Semantic,
1805 format!("expected {expected:?}, found {:?}", actual.kind),
1806 actual.span,
1807 )
1808 .with_expected(format!("{expected:?}")),
1809 )
1810 .with_position_from(&self.source))
1811 }
1812 }
1813
1814 pub(crate) fn containing_entry(&self, value: NodeId) -> Option<NodeId> {
1815 self.node(value).and_then(Node::parent).filter(|parent| {
1816 self.node(*parent).is_some_and(|node| {
1817 matches!(node.kind, NodeKind::MappingEntry | NodeKind::SequenceEntry)
1818 })
1819 })
1820 }
1821
1822 fn mapping_has_blank_line(&self, mapping: &Node) -> bool {
1823 let start = self.line_start_for_offset(mapping.span.start as usize);
1824 let end = mapping.span.end as usize;
1825 let text = &self.source.as_str()[start..end];
1826 text.contains("\n\n") || text.contains("\r\n\r\n")
1827 }
1828
1829 fn format_mapping_entry_replacement(
1830 &self,
1831 indent: usize,
1832 key: &str,
1833 value: &str,
1834 comment: Option<&str>,
1835 needs_leading_break: bool,
1836 preserve_paragraph_break: bool,
1837 ) -> Result<String, YamlError> {
1838 validate_plain_mapping_fragment(key, "mapping key")?;
1839 validate_plain_mapping_fragment(value, "mapping value")?;
1840 if let Some(comment) = comment {
1841 validate_yaml_chars(comment)?;
1842 }
1843
1844 let line_ending = self.preferred_line_ending();
1845 let indent_text = " ".repeat(indent);
1846 let mut replacement = String::new();
1847 if needs_leading_break {
1848 replacement.push_str(line_ending);
1849 }
1850 if preserve_paragraph_break {
1851 replacement.push_str(line_ending);
1852 }
1853 if let Some(comment) = comment {
1854 for line in comment.lines() {
1855 replacement.push_str(&indent_text);
1856 replacement.push('#');
1857 if !line.is_empty() {
1858 replacement.push(' ');
1859 replacement.push_str(line.trim());
1860 }
1861 replacement.push_str(line_ending);
1862 }
1863 }
1864 replacement.push_str(&indent_text);
1865 replacement.push_str(key);
1866 replacement.push_str(": ");
1867 replacement.push_str(value);
1868 replacement.push_str(line_ending);
1869 Ok(replacement)
1870 }
1871
1872 fn format_mapping_value_replacement<T>(
1873 &self,
1874 indent: usize,
1875 key: &str,
1876 value: &T,
1877 comment: Option<&str>,
1878 needs_leading_break: bool,
1879 preserve_paragraph_break: bool,
1880 ) -> Result<String, YamlError>
1881 where
1882 T: ToYamlFragment,
1883 {
1884 validate_yaml_chars(key)?;
1885 if let Some(comment) = comment {
1886 validate_yaml_chars(comment)?;
1887 }
1888
1889 let line_ending = self.preferred_line_ending();
1890 let indent_text = " ".repeat(indent);
1891 let child_indent = indent + 2;
1892 let fragment = value.to_yaml_fragment(child_indent, line_ending)?;
1893 let mut replacement = String::new();
1894 if needs_leading_break {
1895 replacement.push_str(line_ending);
1896 }
1897 if preserve_paragraph_break {
1898 replacement.push_str(line_ending);
1899 }
1900 if let Some(comment) = comment {
1901 for line in comment.lines() {
1902 replacement.push_str(&indent_text);
1903 replacement.push('#');
1904 if !line.is_empty() {
1905 replacement.push(' ');
1906 replacement.push_str(line.trim());
1907 }
1908 replacement.push_str(line_ending);
1909 }
1910 }
1911 replacement.push_str(&indent_text);
1912 replacement.push_str(&crate::edit::emit_string_key(key));
1913 if fragment.contains('\n') || fragment.starts_with(' ') {
1914 replacement.push(':');
1915 replacement.push_str(line_ending);
1916 replacement.push_str(&fragment);
1917 } else {
1918 replacement.push_str(": ");
1919 replacement.push_str(&fragment);
1920 }
1921 replacement.push_str(line_ending);
1922 Ok(replacement)
1923 }
1924
1925 pub(crate) fn node_indent(&self, node: &Node) -> usize {
1926 let line_start = self.line_start_for_offset(node.span.start as usize);
1927 self.source.as_str()[line_start..node.span.start as usize]
1928 .bytes()
1929 .filter(|byte| *byte == b' ')
1930 .count()
1931 }
1932
1933 fn line_start_for_offset(&self, offset: usize) -> usize {
1934 let offset = Span::usize_to_u32(offset);
1935 match self.source.line_starts().binary_search(&offset) {
1936 Ok(index) => self.source.line_starts()[index] as usize,
1937 Err(index) => self.source.line_starts()[index.saturating_sub(1)] as usize,
1938 }
1939 }
1940
1941 pub(crate) fn find_nested_collection_after(
1942 &self,
1943 entry: &Node,
1944 parent_indent: usize,
1945 ) -> Option<NodeId> {
1946 self.nodes
1947 .iter()
1948 .enumerate()
1949 .filter(|(_, node)| {
1950 matches!(node.kind, NodeKind::BlockMapping | NodeKind::BlockSequence)
1951 && node.span.start >= entry.span.end
1952 && self.node_indent(node) > parent_indent
1953 })
1954 .min_by_key(|(_, node)| node.span.start)
1955 .map(|(index, _)| NodeId::from_usize(index))
1956 }
1957
1958 pub(crate) fn block_scalar_content_indent(&self, scalar: &Node) -> Option<usize> {
1959 let text = self.source.slice(scalar.span);
1960 let header_end = text.find(['\r', '\n'])?;
1961 let mut rest = &text[header_end..];
1962 while let Some(stripped) = rest.strip_prefix('\r').or_else(|| rest.strip_prefix('\n')) {
1963 rest = stripped;
1964 }
1965 for line in rest.lines() {
1966 if line.trim().is_empty() {
1967 continue;
1968 }
1969 return Some(line.bytes().take_while(|byte| *byte == b' ').count());
1970 }
1971 None
1972 }
1973
1974 pub(crate) fn queue_edit(&mut self, span: Span, replacement: String) -> Result<(), YamlError> {
1975 self.source.try_slice(span)?;
1976 validate_yaml_chars(&replacement)?;
1977
1978 if span.is_empty()
1979 && let Some(existing) = self
1980 .edits
1981 .iter_mut()
1982 .find(|edit| edit.span.is_empty() && edit.span.start == span.start)
1983 {
1984 existing.replacement.push_str(&replacement);
1985 return Ok(());
1986 }
1987
1988 if let Some(existing) = self
1989 .edits
1990 .iter()
1991 .find(|edit| edits_conflict(edit.span, span))
1992 {
1993 return Err(YamlError::new(
1994 Diagnostic::new(
1995 DiagnosticKind::Emitter,
1996 "edit overlaps an existing pending edit",
1997 span,
1998 )
1999 .with_note(format!(
2000 "existing edit covers bytes {}..{}",
2001 existing.span.start, existing.span.end
2002 )),
2003 )
2004 .with_position_from(&self.source));
2005 }
2006
2007 self.edits.push(Edit { span, replacement });
2008 Ok(())
2009 }
2010
2011 pub(crate) fn mapping_insertion_offset(&self, mapping: &Node) -> usize {
2012 node_link(mapping.last_child)
2013 .and_then(|child| self.node(child))
2014 .map_or(mapping.span.end as usize, |last_child| {
2015 self.line_span_including_break(last_child.span).end as usize
2016 })
2017 }
2018
2019 pub(crate) fn sequence_insertion_offset(&self, sequence: &Node) -> usize {
2020 node_link(sequence.last_child)
2021 .and_then(|child| self.node(child))
2022 .map_or(sequence.span.end as usize, |last_child| {
2023 self.line_span_including_break(last_child.span).end as usize
2024 })
2025 }
2026
2027 fn line_span_including_break(&self, span: Span) -> Span {
2028 let start = self.line_start_for_offset(span.start as usize);
2029 let mut end = span.end as usize;
2030 let bytes = self.source.as_str().as_bytes();
2031
2032 if end < bytes.len() {
2033 if bytes[end] == b'\r' {
2034 end += 1;
2035 if end < bytes.len() && bytes[end] == b'\n' {
2036 end += 1;
2037 }
2038 } else if bytes[end] == b'\n' {
2039 end += 1;
2040 }
2041 }
2042
2043 Span::from_usize(start, end)
2044 }
2045
2046 pub(crate) fn preferred_line_ending(&self) -> &str {
2047 let bytes = self.source.as_str().as_bytes();
2048 for (index, byte) in bytes.iter().enumerate() {
2049 if *byte == b'\r' {
2050 return if bytes.get(index + 1) == Some(&b'\n') {
2051 "\r\n"
2052 } else {
2053 "\r"
2054 };
2055 }
2056 if *byte == b'\n' {
2057 return if index > 0 && bytes[index - 1] == b'\r' {
2058 "\r\n"
2059 } else {
2060 "\n"
2061 };
2062 }
2063 }
2064 "\n"
2065 }
2066
2067 fn source_ends_with_line_break(&self) -> bool {
2068 self.source
2069 .as_str()
2070 .as_bytes()
2071 .last()
2072 .is_some_and(|byte| matches!(byte, b'\n' | b'\r'))
2073 }
2074
2075 fn document_append_prefix(&self, line_ending: &str) -> String {
2076 if self.source.as_str().is_empty() || self.source_ends_with_line_break() {
2077 String::new()
2078 } else {
2079 line_ending.to_owned()
2080 }
2081 }
2082}
2083impl fmt::Display for YamlDoc {
2084 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2085 if self.edits.is_empty() {
2086 return formatter.write_str(self.source.as_str());
2087 }
2088
2089 let mut output = self.source.as_str().to_owned();
2090 let mut edits = self.edits.clone();
2091 edits.sort_by_key(|edit| std::cmp::Reverse(edit.span.start));
2092
2093 for edit in edits {
2094 output.replace_range(
2095 edit.span.start as usize..edit.span.end as usize,
2096 &edit.replacement,
2097 );
2098 }
2099
2100 formatter.write_str(&output)
2101 }
2102}