yaml_edit/nodes/
tagged_node.rs1use super::{Lang, SyntaxNode};
2use crate::as_yaml::{AsYaml, YamlKind};
3use crate::lex::SyntaxKind;
4use crate::yaml::{Mapping, MappingEntry, Scalar, Sequence, Set};
5use rowan::ast::AstNode;
6
7ast_node!(
8 TaggedNode,
9 TAGGED_NODE,
10 "A YAML tagged scalar (tag + value)"
11);
12impl TaggedNode {
13 pub fn tag(&self) -> Option<String> {
15 for child in self.0.children_with_tokens() {
17 if let rowan::NodeOrToken::Token(token) = child {
18 if token.kind() == SyntaxKind::TAG {
19 return Some(token.text().to_string());
20 }
21 }
22 }
23 None
24 }
25
26 pub(crate) fn normalized_tag(&self) -> Option<String> {
29 self.tag().map(|t| crate::tag::normalize(&t).into_owned())
30 }
31
32 pub fn value(&self) -> Option<Scalar> {
34 for child in self.0.children() {
36 if child.kind() == SyntaxKind::SCALAR {
37 return Scalar::cast(child);
38 }
39 }
40 None
41 }
42
43 pub fn as_mapping(&self) -> Option<Mapping> {
46 self.0.children().find_map(Mapping::cast)
47 }
48
49 pub fn as_sequence(&self) -> Option<Sequence> {
52 self.0.children().find_map(Sequence::cast)
53 }
54
55 pub fn as_string(&self) -> Option<String> {
60 self.value().map(|scalar| scalar.as_string())
61 }
62
63 pub fn as_set(&self) -> Option<Set> {
66 Set::cast(self.0.clone())
67 }
68
69 pub fn as_ordered_mapping(&self) -> Option<Vec<MappingEntry>> {
75 if self.normalized_tag().as_deref() != Some(crate::tag::OMAP) {
76 return None;
77 }
78 self.extract_mapping_entries()
79 }
80
81 pub fn as_pairs(&self) -> Option<Vec<MappingEntry>> {
87 if self.normalized_tag().as_deref() != Some(crate::tag::PAIRS) {
88 return None;
89 }
90 self.extract_mapping_entries()
91 }
92
93 fn extract_mapping_entries(&self) -> Option<Vec<MappingEntry>> {
96 let sequence = self.as_sequence()?;
97 Some(
98 sequence
99 .items()
100 .filter_map(Mapping::cast)
101 .filter_map(|mapping| mapping.entries().next())
102 .collect(),
103 )
104 }
105}
106
107fn continuation_indent(node: &SyntaxNode) -> Option<usize> {
110 let mut tokens = node
111 .descendants_with_tokens()
112 .filter_map(|e| e.into_token())
113 .skip_while(|t| t.kind() != SyntaxKind::NEWLINE);
114 tokens.next()?;
115 let first = tokens.find(|t| t.kind() != SyntaxKind::NEWLINE);
116 Some(
117 first
118 .filter(|t| matches!(t.kind(), SyntaxKind::INDENT | SyntaxKind::WHITESPACE))
119 .map_or(0, |t| t.text().len()),
120 )
121}
122
123impl TaggedNode {
124 fn flow_quoted(&self) -> Option<SyntaxNode> {
127 let scalar = self.0.children().find_map(Scalar::cast)?;
128 if !scalar.needs_flow_quotes() {
129 return None;
130 }
131 let mut builder = rowan::GreenNodeBuilder::new();
132 builder.start_node(SyntaxKind::ROOT.into());
133 scalar.build_content(&mut builder, 0, true);
134 builder.finish_node();
135 let quoted = builder.finish().children().next()?.into_node()?.to_owned();
136 let green = self
137 .0
138 .green()
139 .replace_child(scalar.syntax().index(), quoted.into());
140 Some(SyntaxNode::new_root(green))
141 }
142}
143
144impl AsYaml for TaggedNode {
145 fn as_node(&self) -> Option<&SyntaxNode> {
146 Some(&self.0)
147 }
148
149 fn kind(&self) -> YamlKind {
150 self.tag().map_or(YamlKind::Scalar, |t| {
151 YamlKind::Tagged(std::borrow::Cow::Owned(t))
152 })
153 }
154
155 fn build_content(
161 &self,
162 builder: &mut rowan::GreenNodeBuilder,
163 indent: usize,
164 flow_context: bool,
165 ) -> bool {
166 let quoted = flow_context.then(|| self.flow_quoted()).flatten();
167 let node = quoted.as_ref().unwrap_or(&self.0);
168 let content_on_tag_line = self
169 .0
170 .children_with_tokens()
171 .take_while(|c| c.as_node().is_none())
172 .all(|c| c.kind() != SyntaxKind::NEWLINE);
173 let delta = if content_on_tag_line {
174 let delta = indent as isize - crate::column::line_indent(&self.0) as isize;
175 crate::as_yaml::clear_parent_column(&self.0, indent, delta)
176 } else {
177 continuation_indent(&self.0).map_or(0, |base| indent as isize - base as isize)
178 };
179 builder.start_node(SyntaxKind::TAGGED_NODE.into());
180 let ends_with_newline = crate::as_yaml::copy_node_content_reindent(builder, node, delta);
181 builder.finish_node();
182 ends_with_newline
183 }
184
185 fn is_inline(&self) -> bool {
186 crate::yaml::ValueNode::is_inline(self)
187 }
188}