use super::{Rule, Violation};
use crate::nodes::starts_a_flow_collection;
use crate::yaml::SyntaxNode;
pub(super) fn check_document_marker_placement(node: &SyntaxNode, violations: &mut Vec<Violation>) {
if let Some(parent) = node.parent() {
if matches!(
parent.kind(),
crate::SyntaxKind::STRING | crate::SyntaxKind::SCALAR
) {
violations.push(Violation::error_at(
Rule::InvalidDocumentMarker,
node.text_range(),
"Document marker inside string is invalid",
));
}
}
}
pub(super) fn check_single_content_node(node: &SyntaxNode, violations: &mut Vec<Violation>) {
use crate::SyntaxKind;
let Some(extra) = node
.children()
.filter(|c| {
matches!(
c.kind(),
SyntaxKind::SCALAR
| SyntaxKind::MAPPING
| SyntaxKind::SEQUENCE
| SyntaxKind::TAGGED_NODE
| SyntaxKind::ALIAS
)
})
.nth(1)
else {
return;
};
let what = match node.kind() {
SyntaxKind::DOCUMENT => "Document",
SyntaxKind::KEY => "Key",
SyntaxKind::SEQUENCE_ENTRY => "Sequence entry",
SyntaxKind::TAGGED_NODE => "Tagged node",
_ => "Value",
};
violations.push(Violation::error_at(
Rule::Other,
extra.text_range(),
format!("{what} holds more than one node"),
));
}
pub(super) fn check_document_level_anchors(node: &SyntaxNode, violations: &mut Vec<Violation>) {
for child in node.children_with_tokens() {
if let rowan::NodeOrToken::Token(token) = child {
if token.kind() == crate::SyntaxKind::ANCHOR {
let annotates_a_node = token
.siblings_with_tokens(rowan::Direction::Next)
.filter_map(|sibling| sibling.into_node())
.any(|n| {
matches!(
n.kind(),
crate::SyntaxKind::MAPPING
| crate::SyntaxKind::SEQUENCE
| crate::SyntaxKind::SCALAR
| crate::SyntaxKind::TAGGED_NODE
| crate::SyntaxKind::ALIAS
)
});
if !annotates_a_node {
violations.push(Violation::error_at(
Rule::Other,
token.text_range(),
"Anchor must be attached to a node, not at document level",
));
}
}
}
}
}
pub(super) fn check_doc_start_token_content(
token: &rowan::SyntaxToken<crate::Lang>,
violations: &mut Vec<Violation>,
) {
let mut found_newline = false;
let mut found_content = false;
let mut current = token.next_sibling_or_token();
while let Some(sibling) = current {
let next = match &sibling {
rowan::NodeOrToken::Token(t) => {
if t.kind() == crate::SyntaxKind::NEWLINE {
found_newline = true;
break;
}
t.next_sibling_or_token()
}
rowan::NodeOrToken::Node(n) => {
let is_flow = starts_a_flow_collection(n);
match n.kind() {
crate::SyntaxKind::MAPPING | crate::SyntaxKind::SEQUENCE if !is_flow => {
found_content = true;
break;
}
_ => {}
}
n.next_sibling_or_token()
}
};
current = next;
}
if found_content && !found_newline {
violations.push(Violation::error_at(
Rule::InvalidDocumentMarker,
token.text_range(),
"Content on same line as document start marker",
));
}
}