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 fn resolved_tag(&self) -> Option<String> {
34 let tag = self.tag()?;
35 Some(crate::tag::resolve(&tag, &crate::tag::declared_handles(&self.0)).into_owned())
36 }
37
38 pub fn value(&self) -> Option<Scalar> {
40 for child in self.0.children() {
42 if child.kind() == SyntaxKind::SCALAR {
43 return Scalar::cast(child);
44 }
45 }
46 None
47 }
48
49 pub fn as_mapping(&self) -> Option<Mapping> {
52 self.0.children().find_map(Mapping::cast)
53 }
54
55 pub fn as_sequence(&self) -> Option<Sequence> {
58 self.0.children().find_map(Sequence::cast)
59 }
60
61 pub fn as_string(&self) -> Option<String> {
66 self.value().map(|scalar| scalar.as_string())
67 }
68
69 pub fn as_set(&self) -> Option<Set> {
72 Set::cast(self.0.clone())
73 }
74
75 pub fn as_ordered_mapping(&self) -> Option<Vec<MappingEntry>> {
81 if self.resolved_tag().as_deref() != Some(crate::tag::OMAP) {
82 return None;
83 }
84 self.extract_mapping_entries()
85 }
86
87 pub fn as_pairs(&self) -> Option<Vec<MappingEntry>> {
93 if self.resolved_tag().as_deref() != Some(crate::tag::PAIRS) {
94 return None;
95 }
96 self.extract_mapping_entries()
97 }
98
99 fn extract_mapping_entries(&self) -> Option<Vec<MappingEntry>> {
102 let sequence = self.as_sequence()?;
103 Some(
104 sequence
105 .items()
106 .filter_map(Mapping::cast)
107 .filter_map(|mapping| mapping.entries().next())
108 .collect(),
109 )
110 }
111}
112
113fn continuation_indent(node: &SyntaxNode) -> Option<usize> {
116 let mut tokens = node
117 .descendants_with_tokens()
118 .filter_map(|e| e.into_token())
119 .skip_while(|t| t.kind() != SyntaxKind::NEWLINE);
120 tokens.next()?;
121 let first = tokens.find(|t| t.kind() != SyntaxKind::NEWLINE);
122 Some(
123 first
124 .filter(|t| matches!(t.kind(), SyntaxKind::INDENT | SyntaxKind::WHITESPACE))
125 .map_or(0, |t| t.text().len()),
126 )
127}
128
129impl TaggedNode {
130 fn flow_quoted(&self) -> Option<SyntaxNode> {
133 let scalar = self.0.children().find_map(Scalar::cast)?;
134 if !scalar.needs_flow_quotes() {
135 return None;
136 }
137 let mut builder = rowan::GreenNodeBuilder::new();
138 builder.start_node(SyntaxKind::ROOT.into());
139 scalar.build_content(&mut builder, 0, true);
140 builder.finish_node();
141 let quoted = builder.finish().children().next()?.into_node()?.to_owned();
142 let green = self
143 .0
144 .green()
145 .replace_child(scalar.syntax().index(), quoted.into());
146 Some(SyntaxNode::new_root(green))
147 }
148}
149
150impl AsYaml for TaggedNode {
151 fn as_node(&self) -> Option<&SyntaxNode> {
152 Some(&self.0)
153 }
154
155 fn kind(&self) -> YamlKind {
156 self.tag().map_or(YamlKind::Scalar, |t| {
157 YamlKind::Tagged(std::borrow::Cow::Owned(t))
158 })
159 }
160
161 fn build_content(
167 &self,
168 builder: &mut rowan::GreenNodeBuilder,
169 indent: usize,
170 flow_context: bool,
171 ) -> bool {
172 let quoted = flow_context.then(|| self.flow_quoted()).flatten();
173 let node = quoted.as_ref().unwrap_or(&self.0);
174 let content_on_tag_line = self
175 .0
176 .children_with_tokens()
177 .take_while(|c| c.as_node().is_none())
178 .all(|c| c.kind() != SyntaxKind::NEWLINE);
179 let delta = if content_on_tag_line {
180 let delta = indent as isize - crate::column::line_indent(&self.0) as isize;
181 crate::as_yaml::clear_parent_column(&self.0, indent, delta)
182 } else {
183 continuation_indent(&self.0).map_or(0, |base| indent as isize - base as isize)
184 };
185 builder.start_node(SyntaxKind::TAGGED_NODE.into());
186 let ends_with_newline = crate::as_yaml::copy_node_content_reindent(builder, node, delta);
187 builder.finish_node();
188 ends_with_newline
189 }
190
191 fn is_inline(&self) -> bool {
192 crate::yaml::ValueNode::is_inline(self)
193 }
194}