1use crate::tag;
23use crate::yaml::{Alias, Mapping, Scalar, Sequence, SyntaxNode, TaggedNode};
24use rowan::ast::AstNode;
25use std::borrow::Cow;
26use std::fmt;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum YamlKind {
31 Mapping,
33 Sequence,
35 Scalar,
37 Alias,
39 Document,
41 Tagged(Cow<'static, str>),
45}
46
47pub trait AsYaml {
66 fn as_node(&self) -> Option<&SyntaxNode>;
71
72 fn kind(&self) -> YamlKind;
74
75 fn build_content(
83 &self,
84 builder: &mut rowan::GreenNodeBuilder,
85 indent: usize,
86 flow_context: bool,
87 ) -> bool;
88
89 fn is_inline(&self) -> bool;
94
95 #[doc(hidden)]
98 fn build_flow_content(
99 &self,
100 builder: &mut rowan::GreenNodeBuilder,
101 indent: usize,
102 _quoting: FlowQuoting,
103 ) -> bool {
104 self.build_content(builder, indent, true)
105 }
106
107 #[doc(hidden)]
110 fn value_node(&self) -> Option<SyntaxNode> {
111 self.as_node().cloned()
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum FlowQuoting {
119 Minimal,
121 Double,
123}
124
125pub(crate) fn build_in<T: AsYaml + ?Sized>(
128 value: &T,
129 builder: &mut rowan::GreenNodeBuilder,
130 indent: usize,
131 flow: Option<FlowQuoting>,
132) -> bool {
133 match flow {
134 Some(quoting) => value.build_flow_content(builder, indent, quoting),
135 None => value.build_content(builder, indent, false),
136 }
137}
138
139pub fn yaml_eq<A, B>(a: &A, b: &B) -> bool
151where
152 A: AsYaml + ?Sized,
153 B: AsYaml + ?Sized,
154{
155 match (to_syntax(a), to_syntax(b)) {
156 (Some(a), Some(b)) => node_eq(&a, &b),
157 _ => false,
158 }
159}
160
161pub fn yaml_hash<T, H>(value: &T, state: &mut H)
168where
169 T: AsYaml + ?Sized,
170 H: std::hash::Hasher,
171{
172 use std::hash::Hash;
173 if let Some(Ok(Some(identity))) = to_syntax(value).map(|node| node_identity(&node, None)) {
174 identity.hash(state);
175 }
176}
177
178fn to_syntax<T: AsYaml + ?Sized>(v: &T) -> Option<SyntaxNode> {
180 use crate::lex::SyntaxKind;
181 if let Some(node) = v.value_node() {
182 return Some(node);
183 }
184 let mut builder = rowan::GreenNodeBuilder::new();
185 builder.start_node(SyntaxKind::ROOT.into());
186 v.build_content(&mut builder, 0, true);
190 builder.finish_node();
191 SyntaxNode::new_root(builder.finish()).first_child()
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
198pub(crate) struct NodeIdentity(Identity<NodeIdentity>);
199
200#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
204pub(crate) enum Identity<T> {
205 Scalar {
206 tag: String,
207 value: String,
208 },
209 Sequence {
210 tag: String,
211 items: Vec<T>,
212 },
213 Mapping {
214 tag: String,
215 entries: Vec<(T, T)>,
216 },
217 Alias {
219 tag: Option<String>,
220 name: String,
221 },
222}
223
224impl NodeIdentity {
225 pub(crate) fn key_string(&self) -> String {
231 match &self.0 {
232 Identity::Scalar { value, .. } => value.clone(),
233 _ => self.flow_text(),
234 }
235 }
236
237 fn flow_text(&self) -> String {
240 use crate::scalar::ScalarValue;
241 let tagged = |tag: &str, default: &str, content: String| {
242 if tag == default {
243 content
244 } else {
245 format!("{tag} {content}")
246 }
247 };
248 match &self.0 {
249 Identity::Scalar { tag, value } if tag == tag::STR => {
250 ScalarValue::string(value.as_str()).to_flow_string()
251 }
252 Identity::Scalar { tag, value } => {
253 let plain = resolve_scalar(ScalarValue::parse(value.as_str()));
254 if !value.is_empty() && plain.tag == *tag && plain.value == *value {
255 value.clone()
256 } else {
257 format!(
258 "{tag} {}",
259 ScalarValue::string(value.as_str()).to_flow_string()
260 )
261 }
262 }
263 Identity::Sequence { tag, items } => {
264 let items: Vec<_> = items.iter().map(Self::flow_text).collect();
265 tagged(tag, tag::SEQ, format!("[{}]", items.join(", ")))
266 }
267 Identity::Mapping { tag, entries } => {
268 let entries: Vec<_> = entries
269 .iter()
270 .map(|(k, v)| format!("{}: {}", k.flow_text(), v.flow_text()))
271 .collect();
272 tagged(tag, tag::MAP, format!("{{{}}}", entries.join(", ")))
273 }
274 Identity::Alias { tag, name } => match tag {
275 Some(tag) => format!("{tag} *{name}"),
276 None => format!("*{name}"),
277 },
278 }
279 }
280}
281
282fn collection_tag(tag: Option<String>, default: &str) -> String {
284 match tag.as_deref() {
285 None | Some("!") => default.to_string(),
287 Some(t) => tag::normalize(t).into_owned(),
288 }
289}
290
291struct ResolvedScalar {
293 tag: String,
294 value: String,
295}
296
297fn resolve_scalar(scalar: crate::scalar::ScalarValue) -> ResolvedScalar {
299 use crate::scalar::{ScalarType, ScalarValue};
300 let text = scalar.value();
301 let canonical = |parsed: Option<String>| parsed.unwrap_or_else(|| text.to_string());
302 let (tag, value) = match scalar.scalar_type() {
303 ScalarType::Integer => (
304 tag::INT.to_string(),
305 canonical(ScalarValue::canonical_integer(text)),
306 ),
307 ScalarType::Float => (
308 tag::FLOAT.to_string(),
309 canonical(
310 ScalarValue::parse_float(text).map(|f| ScalarValue::format_float(f.to_string())),
311 ),
312 ),
313 ScalarType::Boolean => (
314 tag::BOOL.to_string(),
315 canonical(ScalarValue::parse_bool(text).map(|b| b.to_string())),
316 ),
317 ScalarType::Null => (tag::NULL.to_string(), "null".to_string()),
318 _ => {
319 let tag = match scalar.tag() {
321 Some(t) if t != "!" => tag::normalize(t).into_owned(),
322 _ => tag::STR.to_string(),
323 };
324 (tag, text.to_string())
325 }
326 };
327 ResolvedScalar { tag, value }
328}
329
330pub(crate) fn node_identity(
337 node: &SyntaxNode,
338 registry: Option<&crate::anchor_resolution::AnchorRegistry>,
339) -> IdentityResult<NodeIdentity> {
340 let aliases = match registry {
341 Some(registry) => Aliases::Follow(registry),
342 None => Aliases::Undefined,
343 };
344 identity_with_tag(node, None, aliases, &mut Vec::new(), &mut Trees)
345}
346
347pub(crate) fn lookup_identity(node: &SyntaxNode) -> Option<NodeIdentity> {
354 identity_with_tag(node, None, Aliases::ByName, &mut Vec::new(), &mut Trees)
355 .expect("aliases are not followed")
356}
357
358#[derive(Clone, Copy)]
360enum Aliases<'a> {
361 Undefined,
363 Follow(&'a crate::anchor_resolution::AnchorRegistry),
365 ByName,
367}
368
369pub(crate) type IdentityResult<T> = Result<Option<T>, crate::anchor_resolution::AliasError>;
371
372pub(crate) fn key_identity(
376 key: &YamlNode,
377 registry: Option<&crate::anchor_resolution::AnchorRegistry>,
378) -> Result<NodeIdentity, crate::anchor_resolution::AliasError> {
379 Ok(node_identity(key.syntax(), registry)?.expect("a YamlNode is a value node"))
380}
381
382trait IdentityBuilder {
384 type Id: Ord;
385
386 fn build(&mut self, identity: Identity<Self::Id>) -> Self::Id;
387
388 fn known(&self, _node: &SyntaxNode) -> Option<IdentityResult<Self::Id>> {
391 None
392 }
393
394 fn remember(&mut self, _node: &SyntaxNode, _identity: &IdentityResult<Self::Id>) {}
395}
396
397struct Trees;
399
400impl IdentityBuilder for Trees {
401 type Id = NodeIdentity;
402
403 fn build(&mut self, identity: Identity<NodeIdentity>) -> NodeIdentity {
404 NodeIdentity(identity)
405 }
406}
407
408#[derive(Default)]
416pub(crate) struct SharedIdentities {
417 numbers: std::collections::HashMap<Identity<usize>, usize>,
418 nodes: std::collections::HashMap<SyntaxNode, IdentityResult<usize>>,
419}
420
421impl SharedIdentities {
422 pub(crate) fn identity(&mut self, node: &SyntaxNode) -> IdentityResult<usize> {
425 identity_with_tag(node, None, Aliases::Undefined, &mut Vec::new(), self)
426 }
427}
428
429impl IdentityBuilder for SharedIdentities {
430 type Id = usize;
431
432 fn build(&mut self, identity: Identity<usize>) -> usize {
433 let next = self.numbers.len();
434 *self.numbers.entry(identity).or_insert(next)
435 }
436
437 fn known(&self, node: &SyntaxNode) -> Option<IdentityResult<usize>> {
438 self.nodes.get(node).cloned()
439 }
440
441 fn remember(&mut self, node: &SyntaxNode, identity: &IdentityResult<usize>) {
442 self.nodes.insert(node.clone(), identity.clone());
443 }
444}
445
446fn identity_with_tag<B: IdentityBuilder>(
451 node: &SyntaxNode,
452 tag: Option<String>,
453 aliases: Aliases,
454 expanding: &mut Vec<SyntaxNode>,
455 builder: &mut B,
456) -> IdentityResult<B::Id> {
457 use crate::lex::SyntaxKind;
458
459 let holds_nodes = matches!(
462 node.kind(),
463 SyntaxKind::MAPPING | SyntaxKind::SEQUENCE | SyntaxKind::TAGGED_NODE
464 );
465 if tag.is_some() || !holds_nodes {
466 return build_identity(node, tag, aliases, expanding, builder);
467 }
468 if let Some(identity) = builder.known(node) {
469 return identity;
470 }
471 let identity = build_identity(node, None, aliases, expanding, builder);
472 builder.remember(node, &identity);
473 identity
474}
475
476fn build_identity<B: IdentityBuilder>(
479 node: &SyntaxNode,
480 tag: Option<String>,
481 aliases: Aliases,
482 expanding: &mut Vec<SyntaxNode>,
483 builder: &mut B,
484) -> IdentityResult<B::Id> {
485 use crate::anchor_resolution::AliasError;
486 use crate::lex::SyntaxKind;
487 use crate::scalar::ScalarValue;
488
489 let with_tag = |scalar: ScalarValue, tag: Option<String>| match tag {
490 Some(tag) => scalar.with_resolved_tag(tag),
491 None => scalar,
492 };
493 let scalar = |builder: &mut B, scalar: ScalarValue, tag: Option<String>| {
494 let ResolvedScalar { tag, value } = resolve_scalar(with_tag(scalar, tag));
495 builder.build(Identity::Scalar { tag, value })
496 };
497 match node.kind() {
498 SyntaxKind::KEY | SyntaxKind::VALUE => match node.first_child() {
499 Some(content) => identity_with_tag(&content, tag, aliases, expanding, builder),
500 None => Ok(None),
501 },
502 SyntaxKind::TAGGED_NODE => {
503 let Some(tagged) = TaggedNode::cast(node.clone()) else {
504 return Ok(None);
505 };
506 let tag = tag.or_else(|| tagged.resolved_tag());
509 match node.first_child() {
510 Some(content) => identity_with_tag(&content, tag, aliases, expanding, builder),
511 None => Ok(ScalarValue::from_tagged(&tagged).map(|s| scalar(builder, s, tag))),
512 }
513 }
514 SyntaxKind::ALIAS => {
515 let Some(alias) = Alias::cast(node.clone()) else {
516 return Ok(None);
517 };
518 let registry = match aliases {
519 Aliases::Follow(registry) => registry,
520 Aliases::ByName => {
521 return Ok(Some(builder.build(Identity::Alias {
522 tag,
523 name: alias.name(),
524 })))
525 }
526 Aliases::Undefined => {
527 return Err(AliasError::Undefined {
528 alias: alias.name(),
529 })
530 }
531 };
532 let target = registry.resolve_alias_through(&alias)?;
533 if expanding.contains(target) {
534 return Err(AliasError::Recursive {
535 alias: alias.name(),
536 });
537 }
538 expanding.push(target.clone());
539 let identity = identity_with_tag(target, tag, aliases, expanding, builder);
540 expanding.pop();
541 identity
542 }
543 SyntaxKind::SCALAR => {
544 Ok(Scalar::cast(node.clone())
545 .map(|s| scalar(builder, ScalarValue::from_scalar(&s), tag)))
546 }
547 SyntaxKind::MAPPING => {
548 let Some(mapping) = Mapping::cast(node.clone()) else {
549 return Ok(None);
550 };
551 let mut entries = Vec::new();
552 for (key, value) in mapping.pairs() {
553 let key = identity_with_tag(&key, None, aliases, expanding, builder)?;
554 let value = identity_with_tag(&value, None, aliases, expanding, builder)?;
555 let (Some(key), Some(value)) = (key, value) else {
556 return Ok(None);
557 };
558 entries.push((key, value));
559 }
560 entries.sort();
561 Ok(Some(builder.build(Identity::Mapping {
562 tag: collection_tag(tag, tag::MAP),
563 entries,
564 })))
565 }
566 SyntaxKind::SEQUENCE => {
567 let Some(sequence) = Sequence::cast(node.clone()) else {
568 return Ok(None);
569 };
570 let mut items = Vec::new();
571 for item in sequence.items() {
572 let Some(item) = identity_with_tag(&item, None, aliases, expanding, builder)?
573 else {
574 return Ok(None);
575 };
576 items.push(item);
577 }
578 Ok(Some(builder.build(Identity::Sequence {
579 tag: collection_tag(tag, tag::SEQ),
580 items,
581 })))
582 }
583 _ => Ok(None),
584 }
585}
586
587pub(crate) fn identity_of<T: AsYaml + ?Sized>(
590 v: &T,
591 registry: Option<&crate::anchor_resolution::AnchorRegistry>,
592) -> Result<Option<NodeIdentity>, crate::anchor_resolution::AliasError> {
593 match to_syntax(v) {
594 Some(node) => node_identity(&node, registry),
595 None => Ok(None),
596 }
597}
598
599pub(crate) fn scalar_canonical(node: &SyntaxNode) -> Option<(String, String)> {
604 match node_identity(node, None) {
605 Ok(Some(NodeIdentity(Identity::Scalar { tag, value }))) => Some((tag, value)),
606 _ => None,
607 }
608}
609
610fn node_eq(a: &SyntaxNode, b: &SyntaxNode) -> bool {
613 match (node_identity(a, None), node_identity(b, None)) {
614 (Ok(Some(a)), Ok(Some(b))) => a == b,
615 _ => false,
616 }
617}
618
619#[derive(Debug, Clone, PartialEq)]
646pub enum YamlNode {
647 Scalar(Scalar),
649 Mapping(Mapping),
651 Sequence(Sequence),
653 Alias(Alias),
655 TaggedNode(TaggedNode),
657}
658
659impl YamlNode {
660 pub fn from_syntax(node: SyntaxNode) -> Option<Self> {
665 use crate::lex::SyntaxKind;
666 match node.kind() {
667 SyntaxKind::SCALAR => Scalar::cast(node).map(YamlNode::Scalar),
668 SyntaxKind::MAPPING => Mapping::cast(node).map(YamlNode::Mapping),
669 SyntaxKind::SEQUENCE => Sequence::cast(node).map(YamlNode::Sequence),
670 SyntaxKind::ALIAS => Alias::cast(node).map(YamlNode::Alias),
671 SyntaxKind::TAGGED_NODE => TaggedNode::cast(node).map(YamlNode::TaggedNode),
672 _ => None,
673 }
674 }
675
676 pub(crate) fn from_syntax_peeled(node: SyntaxNode) -> Option<Self> {
682 use crate::lex::SyntaxKind;
683 let inner = if matches!(node.kind(), SyntaxKind::KEY | SyntaxKind::VALUE) {
684 node.children().next()?
685 } else {
686 node
687 };
688 Self::from_syntax(inner)
689 }
690
691 pub fn kind(&self) -> YamlKind {
693 match self {
694 YamlNode::Scalar(_) => YamlKind::Scalar,
695 YamlNode::Mapping(_) => YamlKind::Mapping,
696 YamlNode::Sequence(_) => YamlKind::Sequence,
697 YamlNode::Alias(_) => YamlKind::Alias,
698 YamlNode::TaggedNode(t) => t
699 .tag()
700 .map_or(YamlKind::Scalar, |tag| YamlKind::Tagged(Cow::Owned(tag))),
701 }
702 }
703
704 pub(crate) fn syntax(&self) -> &SyntaxNode {
706 match self {
707 YamlNode::Scalar(s) => s.syntax(),
708 YamlNode::Mapping(m) => m.syntax(),
709 YamlNode::Sequence(s) => s.syntax(),
710 YamlNode::Alias(a) => a.syntax(),
711 YamlNode::TaggedNode(t) => t.syntax(),
712 }
713 }
714
715 pub fn yaml_eq<O: AsYaml>(&self, other: &O) -> bool {
717 yaml_eq(self, other)
718 }
719
720 pub fn as_scalar(&self) -> Option<&Scalar> {
722 if let YamlNode::Scalar(s) = self {
723 Some(s)
724 } else {
725 None
726 }
727 }
728
729 pub fn as_mapping(&self) -> Option<&Mapping> {
731 if let YamlNode::Mapping(m) = self {
732 Some(m)
733 } else {
734 None
735 }
736 }
737
738 pub fn as_sequence(&self) -> Option<&Sequence> {
740 if let YamlNode::Sequence(s) = self {
741 Some(s)
742 } else {
743 None
744 }
745 }
746
747 pub(crate) fn mapping_content(&self) -> Option<Mapping> {
749 match self {
750 YamlNode::Mapping(m) => Some(m.clone()),
751 YamlNode::TaggedNode(t) => t.as_mapping(),
752 _ => None,
753 }
754 }
755
756 pub(crate) fn sequence_content(&self) -> Option<Sequence> {
758 match self {
759 YamlNode::Sequence(s) => Some(s.clone()),
760 YamlNode::TaggedNode(t) => t.as_sequence(),
761 _ => None,
762 }
763 }
764
765 pub fn as_tagged(&self) -> Option<&TaggedNode> {
767 if let YamlNode::TaggedNode(t) = self {
768 Some(t)
769 } else {
770 None
771 }
772 }
773
774 pub fn as_alias(&self) -> Option<&Alias> {
776 if let YamlNode::Alias(a) = self {
777 Some(a)
778 } else {
779 None
780 }
781 }
782
783 pub fn is_scalar(&self) -> bool {
785 matches!(self, YamlNode::Scalar(_))
786 }
787
788 pub fn is_mapping(&self) -> bool {
790 matches!(self, YamlNode::Mapping(_))
791 }
792
793 pub fn is_sequence(&self) -> bool {
795 matches!(self, YamlNode::Sequence(_))
796 }
797
798 pub fn is_tagged(&self) -> bool {
800 matches!(self, YamlNode::TaggedNode(_))
801 }
802
803 pub fn is_alias(&self) -> bool {
805 matches!(self, YamlNode::Alias(_))
806 }
807
808 pub fn byte_range(&self) -> crate::TextPosition {
810 self.syntax().text_range().into()
811 }
812
813 pub fn to_i64(&self) -> Option<i64> {
817 crate::scalar::ScalarValue::from_scalar(self.as_scalar()?).to_i64()
818 }
819
820 pub fn to_f64(&self) -> Option<f64> {
824 crate::scalar::ScalarValue::from_scalar(self.as_scalar()?).to_f64()
825 }
826
827 pub fn to_bool(&self) -> Option<bool> {
831 crate::scalar::ScalarValue::from_scalar(self.as_scalar()?).to_bool()
832 }
833
834 pub fn get(&self, key: impl crate::AsYaml) -> Option<YamlNode> {
838 self.as_mapping()?
839 .get_node(key)
840 .and_then(YamlNode::from_syntax)
841 }
842
843 pub fn get_item(&self, index: usize) -> Option<YamlNode> {
847 self.as_sequence()?
848 .items()
849 .nth(index)
850 .and_then(YamlNode::from_syntax)
851 }
852}
853
854impl AsYaml for YamlNode {
855 fn as_node(&self) -> Option<&SyntaxNode> {
856 Some(self.syntax())
857 }
858
859 fn kind(&self) -> YamlKind {
860 YamlNode::kind(self)
861 }
862
863 fn build_content(
864 &self,
865 builder: &mut rowan::GreenNodeBuilder,
866 indent: usize,
867 flow_context: bool,
868 ) -> bool {
869 match self {
870 YamlNode::Scalar(s) => s.build_content(builder, indent, flow_context),
871 YamlNode::Mapping(m) => m.build_content(builder, indent, flow_context),
872 YamlNode::Sequence(s) => s.build_content(builder, indent, flow_context),
873 YamlNode::Alias(a) => a.build_content(builder, indent, flow_context),
874 YamlNode::TaggedNode(t) => t.build_content(builder, indent, flow_context),
875 }
876 }
877
878 fn is_inline(&self) -> bool {
879 use crate::yaml::ValueNode;
880 match self {
881 YamlNode::Scalar(s) => ValueNode::is_inline(s),
882 YamlNode::Mapping(m) => ValueNode::is_inline(m),
883 YamlNode::Sequence(s) => ValueNode::is_inline(s),
884 YamlNode::Alias(_) => true,
885 YamlNode::TaggedNode(t) => ValueNode::is_inline(t),
886 }
887 }
888}
889
890impl PartialEq<str> for YamlNode {
892 fn eq(&self, other: &str) -> bool {
893 yaml_eq(self, &other)
894 }
895}
896
897impl PartialEq<&str> for YamlNode {
898 fn eq(&self, other: &&str) -> bool {
899 yaml_eq(self, other)
900 }
901}
902
903impl PartialEq<String> for YamlNode {
904 fn eq(&self, other: &String) -> bool {
905 yaml_eq(self, &other.as_str())
906 }
907}
908
909impl fmt::Display for YamlNode {
910 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
911 write!(f, "{}", self.syntax().text())
912 }
913}
914
915impl<T: AsYaml + ?Sized> AsYaml for &T {
922 fn as_node(&self) -> Option<&SyntaxNode> {
923 (*self).as_node()
924 }
925
926 fn kind(&self) -> YamlKind {
927 (*self).kind()
928 }
929
930 fn build_content(
931 &self,
932 builder: &mut rowan::GreenNodeBuilder,
933 indent: usize,
934 flow_context: bool,
935 ) -> bool {
936 (*self).build_content(builder, indent, flow_context)
937 }
938
939 fn is_inline(&self) -> bool {
940 (*self).is_inline()
941 }
942
943 fn build_flow_content(
944 &self,
945 builder: &mut rowan::GreenNodeBuilder,
946 indent: usize,
947 quoting: FlowQuoting,
948 ) -> bool {
949 (*self).build_flow_content(builder, indent, quoting)
950 }
951
952 fn value_node(&self) -> Option<SyntaxNode> {
953 (*self).value_node()
954 }
955}
956
957pub(crate) fn copy_node_content(builder: &mut rowan::GreenNodeBuilder, node: &SyntaxNode) {
959 for child in node.children_with_tokens() {
960 match child {
961 rowan::NodeOrToken::Node(n) => {
962 builder.start_node(n.kind().into());
963 copy_node_content(builder, &n);
964 builder.finish_node();
965 }
966 rowan::NodeOrToken::Token(t) => {
967 builder.token(t.kind().into(), t.text());
968 }
969 }
970 }
971}
972
973pub(crate) fn copy_node_content_reindent(
986 builder: &mut rowan::GreenNodeBuilder,
987 node: &SyntaxNode,
988 delta: isize,
989) -> bool {
990 use crate::lex::SyntaxKind;
991 let mut after_newline = false;
992 let pad = |n: isize| " ".repeat(n.max(0) as usize);
993
994 for child in node.children_with_tokens() {
995 match child {
996 rowan::NodeOrToken::Node(n) => {
997 if n.text_range().is_empty() {
1002 builder.start_node(n.kind().into());
1003 copy_node_content(builder, &n);
1004 builder.finish_node();
1005 continue;
1006 }
1007 if after_newline && delta > 0 {
1008 builder.token(SyntaxKind::INDENT.into(), &pad(delta));
1009 }
1010 builder.start_node(n.kind().into());
1011 after_newline = copy_node_content_reindent(builder, &n, delta);
1012 builder.finish_node();
1013 }
1014 rowan::NodeOrToken::Token(t) => match t.kind() {
1015 SyntaxKind::NEWLINE => {
1016 builder.token(t.kind().into(), t.text());
1017 after_newline = true;
1018 }
1019 SyntaxKind::WHITESPACE | SyntaxKind::INDENT => {
1020 if after_newline {
1021 let shifted = shift_line_start(t.text(), delta);
1022 if !shifted.is_empty() {
1023 builder.token(SyntaxKind::INDENT.into(), &shifted);
1024 }
1025 } else {
1026 builder.token(t.kind().into(), t.text());
1027 }
1028 after_newline = false;
1029 }
1030 _ => {
1031 if after_newline && delta > 0 {
1032 builder.token(SyntaxKind::INDENT.into(), &pad(delta));
1033 }
1034 builder.token(t.kind().into(), &shift_inner_lines(t.text(), delta));
1035 after_newline = false;
1036 }
1037 },
1038 }
1039 }
1040 after_newline
1041}
1042
1043pub(crate) fn clear_parent_column(node: &SyntaxNode, parent: usize, delta: isize) -> isize {
1048 let shallowest = node
1049 .text()
1050 .to_string()
1051 .split(['\n', '\r'])
1052 .skip(1)
1053 .filter(|line| !line.trim().is_empty())
1054 .map(crate::column::space_indent)
1055 .min();
1056 shallowest.map_or(delta, |column| {
1057 delta.max(parent as isize + 1 - column as isize)
1058 })
1059}
1060
1061pub(crate) fn shift_line_start(text: &str, delta: isize) -> String {
1065 if delta >= 0 {
1066 return format!("{}{text}", " ".repeat(delta as usize));
1067 }
1068 let spaces = crate::column::space_indent(text);
1069 text[spaces.min(delta.unsigned_abs())..].to_string()
1070}
1071
1072pub(crate) fn shift_inner_lines(text: &str, delta: isize) -> Cow<'_, str> {
1075 if delta == 0 || !text.contains(['\n', '\r']) {
1076 return Cow::Borrowed(text);
1077 }
1078 let mut lines = text.split_inclusive(['\n', '\r']);
1080 let mut shifted = lines.next().unwrap_or_default().to_string();
1081 for line in lines {
1082 if line.trim_end_matches(['\n', '\r']).is_empty() {
1083 shifted.push_str(line);
1084 } else {
1085 shifted.push_str(&shift_line_start(line, delta));
1086 }
1087 }
1088 Cow::Owned(shifted)
1089}
1090
1091pub(crate) fn source_reindent_base(node: &SyntaxNode, is_flow: bool) -> usize {
1096 if is_flow {
1097 crate::column::line_indent(node)
1098 } else {
1099 crate::column::node_column(node)
1100 }
1101}
1102
1103macro_rules! impl_as_yaml_scalar {
1106 ($to_scalar:expr; $($ty:ty),+ $(,)?) => {
1107 $(impl AsYaml for $ty {
1108 fn as_node(&self) -> Option<&crate::yaml::SyntaxNode> {
1109 None
1110 }
1111
1112 fn kind(&self) -> YamlKind {
1113 YamlKind::Scalar
1114 }
1115
1116 fn build_content(&self, builder: &mut rowan::GreenNodeBuilder, indent: usize, _flow_context: bool) -> bool {
1117 let to_scalar: fn(&$ty) -> crate::scalar::ScalarValue = $to_scalar;
1118 to_scalar(self).build(builder, indent, None)
1119 }
1120
1121 fn is_inline(&self) -> bool {
1122 true
1123 }
1124 })+
1125 };
1126}
1127
1128impl_as_yaml_scalar!(
1129 |v| crate::scalar::ScalarValue::plain(v.to_string()).with_type(crate::scalar::ScalarType::Integer);
1130 i64, i32, i16, i8, isize, u64, u32, u16, u8, usize
1131);
1132impl_as_yaml_scalar!(|v| crate::scalar::ScalarValue::from(*v); f64, f32, bool);
1133
1134impl AsYaml for String {
1135 fn as_node(&self) -> Option<&crate::yaml::SyntaxNode> {
1136 None
1137 }
1138
1139 fn kind(&self) -> YamlKind {
1140 YamlKind::Scalar
1141 }
1142
1143 fn build_content(
1144 &self,
1145 builder: &mut rowan::GreenNodeBuilder,
1146 indent: usize,
1147 flow_context: bool,
1148 ) -> bool {
1149 self.as_str().build_content(builder, indent, flow_context)
1150 }
1151
1152 fn is_inline(&self) -> bool {
1153 true
1154 }
1155
1156 fn build_flow_content(
1157 &self,
1158 builder: &mut rowan::GreenNodeBuilder,
1159 indent: usize,
1160 quoting: FlowQuoting,
1161 ) -> bool {
1162 self.as_str().build_flow_content(builder, indent, quoting)
1163 }
1164}
1165
1166impl AsYaml for &str {
1167 fn as_node(&self) -> Option<&crate::yaml::SyntaxNode> {
1168 None
1169 }
1170
1171 fn kind(&self) -> YamlKind {
1172 YamlKind::Scalar
1173 }
1174
1175 fn build_content(
1176 &self,
1177 builder: &mut rowan::GreenNodeBuilder,
1178 indent: usize,
1179 flow_context: bool,
1180 ) -> bool {
1181 crate::scalar::ScalarValue::string(*self).build_content(builder, indent, flow_context)
1182 }
1183
1184 fn is_inline(&self) -> bool {
1185 true
1186 }
1187
1188 fn build_flow_content(
1189 &self,
1190 builder: &mut rowan::GreenNodeBuilder,
1191 indent: usize,
1192 quoting: FlowQuoting,
1193 ) -> bool {
1194 crate::scalar::ScalarValue::string(*self).build_flow_content(builder, indent, quoting)
1195 }
1196}
1197
1198impl<T: AsYaml> AsYaml for Option<T> {
1200 fn as_node(&self) -> Option<&crate::yaml::SyntaxNode> {
1201 self.as_ref().and_then(AsYaml::as_node)
1202 }
1203
1204 fn kind(&self) -> YamlKind {
1205 match self {
1206 Some(v) => v.kind(),
1207 None => YamlKind::Scalar,
1208 }
1209 }
1210
1211 fn build_content(
1212 &self,
1213 builder: &mut rowan::GreenNodeBuilder,
1214 indent: usize,
1215 flow_context: bool,
1216 ) -> bool {
1217 match self {
1218 Some(v) => v.build_content(builder, indent, flow_context),
1219 None => crate::scalar::ScalarValue::null().build_content(builder, indent, flow_context),
1220 }
1221 }
1222
1223 fn is_inline(&self) -> bool {
1224 match self {
1225 Some(v) => v.is_inline(),
1226 None => true,
1227 }
1228 }
1229
1230 fn build_flow_content(
1231 &self,
1232 builder: &mut rowan::GreenNodeBuilder,
1233 indent: usize,
1234 quoting: FlowQuoting,
1235 ) -> bool {
1236 match self {
1237 Some(v) => v.build_flow_content(builder, indent, quoting),
1238 None => self.build_content(builder, indent, true),
1239 }
1240 }
1241}
1242
1243pub(crate) fn build_tagged(
1247 builder: &mut rowan::GreenNodeBuilder,
1248 tag: &str,
1249 block: bool,
1250 indent: usize,
1251 content: impl FnOnce(&mut rowan::GreenNodeBuilder) -> bool,
1252) -> bool {
1253 use crate::lex::SyntaxKind;
1254
1255 builder.start_node(SyntaxKind::TAGGED_NODE.into());
1256 builder.token(SyntaxKind::TAG.into(), tag);
1257 if block {
1258 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1259 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
1260 } else {
1261 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1262 }
1263 let ends_with_newline = content(builder);
1264 builder.finish_node(); ends_with_newline
1266}
1267
1268pub(crate) fn build_mapping<K: AsYaml, V: AsYaml>(
1272 builder: &mut rowan::GreenNodeBuilder,
1273 entries: impl ExactSizeIterator<Item = (K, V)>,
1274 indent: usize,
1275 flow: Option<FlowQuoting>,
1276) -> bool {
1277 use crate::lex::SyntaxKind;
1278
1279 let len = entries.len();
1280 let block = flow.is_none() && len > 0;
1281 let flow = (!block).then_some(flow.unwrap_or(FlowQuoting::Minimal));
1283
1284 builder.start_node(SyntaxKind::MAPPING.into());
1285 if !block {
1286 builder.token(SyntaxKind::LEFT_BRACE.into(), "{");
1287 }
1288 for (i, (key, value)) in entries.enumerate() {
1289 if i > 0 && block && indent > 0 {
1290 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent));
1292 }
1293 builder.start_node(SyntaxKind::MAPPING_ENTRY.into());
1294 builder.start_node(SyntaxKind::KEY.into());
1295 crate::nodes::build_key_content(builder, &key, indent, flow);
1296 builder.finish_node(); builder.token(SyntaxKind::COLON.into(), ":");
1298 let starts_block = block && !value.is_inline();
1299 let ends_with_newline = if starts_block {
1300 builder.start_node(SyntaxKind::VALUE.into());
1301 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1302 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent + 2));
1303 build_in(&value, builder, indent + 2, None)
1304 } else {
1305 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1306 builder.start_node(SyntaxKind::VALUE.into());
1307 build_in(&value, builder, indent, flow)
1309 };
1310 builder.finish_node(); if block {
1312 if !ends_with_newline {
1313 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1314 }
1315 } else if i + 1 < len {
1316 builder.token(SyntaxKind::COMMA.into(), ",");
1317 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1318 }
1319 builder.finish_node(); }
1321 if !block {
1322 builder.token(SyntaxKind::RIGHT_BRACE.into(), "}");
1323 }
1324 builder.finish_node(); block
1326}
1327
1328pub(crate) fn build_sequence<T: AsYaml>(
1332 builder: &mut rowan::GreenNodeBuilder,
1333 items: impl ExactSizeIterator<Item = T>,
1334 indent: usize,
1335 flow: Option<FlowQuoting>,
1336) -> bool {
1337 use crate::lex::SyntaxKind;
1338
1339 let len = items.len();
1340 let block = flow.is_none() && len > 0;
1341 let flow = (!block).then_some(flow.unwrap_or(FlowQuoting::Minimal));
1342
1343 builder.start_node(SyntaxKind::SEQUENCE.into());
1344 if !block {
1345 builder.token(SyntaxKind::LEFT_BRACKET.into(), "[");
1346 }
1347 for (i, item) in items.enumerate() {
1348 if i > 0 && block && indent > 0 {
1349 builder.token(SyntaxKind::INDENT.into(), &" ".repeat(indent));
1351 }
1352 builder.start_node(SyntaxKind::SEQUENCE_ENTRY.into());
1353 if block {
1354 builder.token(SyntaxKind::DASH.into(), "-");
1355 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1356 let item_indent = if item.is_inline() { indent } else { indent + 2 };
1359 if !build_in(&item, builder, item_indent, None) {
1360 builder.token(SyntaxKind::NEWLINE.into(), "\n");
1361 }
1362 } else {
1363 build_in(&item, builder, indent, flow);
1364 if i + 1 < len {
1365 builder.token(SyntaxKind::COMMA.into(), ",");
1366 builder.token(SyntaxKind::WHITESPACE.into(), " ");
1367 }
1368 }
1369 builder.finish_node(); }
1371 if !block {
1372 builder.token(SyntaxKind::RIGHT_BRACKET.into(), "]");
1373 }
1374 builder.finish_node(); block
1376}
1377
1378pub(crate) fn build_set_content<T: AsYaml>(
1381 builder: &mut rowan::GreenNodeBuilder,
1382 members: impl ExactSizeIterator<Item = T>,
1383 indent: usize,
1384 flow: Option<FlowQuoting>,
1385) -> bool {
1386 let block = flow.is_none() && members.len() > 0;
1387 build_tagged(builder, tag::SET, block, indent, |builder| {
1388 let entries = members.map(|m| (m, crate::scalar::ScalarValue::null()));
1389 build_mapping(builder, entries, indent + 2, flow)
1390 })
1391}
1392
1393impl<T: AsYaml> AsYaml for std::collections::BTreeSet<T> {
1398 fn as_node(&self) -> Option<&crate::yaml::SyntaxNode> {
1399 None
1400 }
1401
1402 fn kind(&self) -> YamlKind {
1403 YamlKind::Tagged(Cow::Borrowed(tag::SET))
1404 }
1405
1406 fn build_content(
1407 &self,
1408 builder: &mut rowan::GreenNodeBuilder,
1409 indent: usize,
1410 flow_context: bool,
1411 ) -> bool {
1412 build_set_content(
1413 builder,
1414 self.iter(),
1415 indent,
1416 flow_context.then_some(FlowQuoting::Minimal),
1417 )
1418 }
1419
1420 fn is_inline(&self) -> bool {
1421 true
1423 }
1424
1425 fn build_flow_content(
1426 &self,
1427 builder: &mut rowan::GreenNodeBuilder,
1428 indent: usize,
1429 quoting: FlowQuoting,
1430 ) -> bool {
1431 build_set_content(builder, self.iter(), indent, Some(quoting))
1432 }
1433}