use crate::parser::tests::check;
use crate::{SyntaxKind, parse};
fn text_nodes(root: &crate::SyntaxNode) -> String {
root.descendants()
.filter(|n| n.kind() == SyntaxKind::TEXT)
.map(|n| n.text().to_string())
.collect()
}
#[test]
fn block_comment_mid_branch_stays_one_implicit_sequence() {
let src = "{ a /* c */ x | b }\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let sequences: Vec<_> = p
.syntax()
.descendants()
.filter(|n| n.kind() == SyntaxKind::IMPLICIT_SEQUENCE)
.collect();
assert_eq!(
sequences.len(),
1,
"expected exactly one IMPLICIT_SEQUENCE: {sequences:?}"
);
let branches: Vec<_> = sequences[0]
.children()
.filter(|n| n.kind() == SyntaxKind::BRANCH_CONTENT)
.collect();
assert_eq!(branches.len(), 2, "expected both branches intact");
let comment_inside_first_branch = branches[0]
.descendants_with_tokens()
.any(|c| c.kind() == SyntaxKind::BLOCK_COMMENT);
assert!(
comment_inside_first_branch,
"BLOCK_COMMENT should be nested inside the first BRANCH_CONTENT, \
not hoisted out of IMPLICIT_SEQUENCE"
);
assert!(
p.syntax()
.descendants()
.all(|n| n.kind() != SyntaxKind::ERROR && n.kind() != SyntaxKind::STRAY_CLOSING_BRACE),
"no ERROR/STRAY_CLOSING_BRACE nodes expected: {:#?}",
p.syntax()
);
}
#[test]
fn block_comment_elision_preserves_surrounding_whitespace() {
let p = parse("{ a /* c */ x | b }\n");
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(
text_nodes(&p.syntax()),
"a x b ",
"comment span alone should be elided, both adjoining spaces kept"
);
}
#[test]
fn block_comment_in_first_branch_only() {
let src = "{ a /* c */ x | b }\n";
check(src);
}
#[test]
fn block_comment_in_last_branch_only() {
let src = "{ a | b /* c */ x }\n";
check(src);
let p = parse(src);
let sequences: Vec<_> = p
.syntax()
.descendants()
.filter(|n| n.kind() == SyntaxKind::IMPLICIT_SEQUENCE)
.collect();
assert_eq!(sequences.len(), 1);
let branches: Vec<_> = sequences[0]
.children()
.filter(|n| n.kind() == SyntaxKind::BRANCH_CONTENT)
.collect();
assert_eq!(branches.len(), 2);
}
#[test]
fn block_comment_adjacent_to_pipe_both_sides() {
let src = "{ a /*c1*/| /*c2*/ b }\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let sequences: Vec<_> = p
.syntax()
.descendants()
.filter(|n| n.kind() == SyntaxKind::IMPLICIT_SEQUENCE)
.collect();
assert_eq!(sequences.len(), 1);
let branches: Vec<_> = sequences[0]
.children()
.filter(|n| n.kind() == SyntaxKind::BRANCH_CONTENT)
.collect();
assert_eq!(branches.len(), 2, "expected both branches intact");
}
#[test]
fn block_comment_in_inline_conditional_branch() {
check("{x: yes /* c */ indeed|no}\n");
}
#[test]
fn block_comment_between_interpolations_in_branch() {
let src = "{ {a} /* c */ {b} | c }\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let comment_inside_sequence = p
.syntax()
.descendants()
.find(|n| n.kind() == SyntaxKind::IMPLICIT_SEQUENCE)
.expect("expected an IMPLICIT_SEQUENCE")
.descendants_with_tokens()
.any(|c| c.kind() == SyntaxKind::BLOCK_COMMENT);
assert!(
comment_inside_sequence,
"BLOCK_COMMENT should stay nested inside IMPLICIT_SEQUENCE"
);
}
#[test]
fn line_comment_in_inline_branch_is_unterminated_block_not_fragmentation() {
let src = "{ a // c\n| b }\n";
let p = parse(src);
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let messages: Vec<&str> = p.errors().iter().map(|e| e.message.as_str()).collect();
assert_eq!(
messages,
vec![
"expected `}`",
"expected newline at end of content line",
"unexpected token",
"expected newline at end of content line",
],
"expected outcome unchanged from origin/main: {:#?}",
p.errors()
);
assert_eq!(
text_nodes(&p.syntax()),
"b ",
"the pre-comment `a` does not survive as TEXT -- the `{{` is left \
unterminated by the LINE_COMMENT's forced newline, unlike the \
fragmentation bug this file otherwise fixes"
);
}
#[test]
fn block_comment_in_multiline_branchless_cond_body() {
let src = "{x > 5:\n Big /* c */ number.\n}\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let body = p
.syntax()
.descendants()
.find(|n| n.kind() == SyntaxKind::BRANCHLESS_COND_BODY)
.expect("expected a BRANCHLESS_COND_BODY");
let comment_inside_body = body
.descendants_with_tokens()
.any(|c| c.kind() == SyntaxKind::BLOCK_COMMENT);
assert!(
comment_inside_body,
"BLOCK_COMMENT should stay nested inside BRANCHLESS_COND_BODY"
);
assert_eq!(
text_nodes(&p.syntax()),
"Big number.",
"comment span alone should be elided, both adjoining spaces kept"
);
}
#[test]
fn block_comment_in_multiline_branch_body() {
let src = "{\n- x > 5:\n Big /* c */ number.\n- else:\n Small.\n}\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let body = p
.syntax()
.descendants()
.find(|n| n.kind() == SyntaxKind::MULTILINE_BRANCH_BODY)
.expect("expected a MULTILINE_BRANCH_BODY");
let comment_inside_body = body
.descendants_with_tokens()
.any(|c| c.kind() == SyntaxKind::BLOCK_COMMENT);
assert!(
comment_inside_body,
"BLOCK_COMMENT should stay nested inside MULTILINE_BRANCH_BODY"
);
}
#[test]
fn block_comment_in_nested_alternative() {
let src = "{ a | { x /* c */ y | z } }\n";
let p = parse(src);
assert!(p.errors().is_empty(), "errors: {:?}", p.errors());
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
let sequences: Vec<_> = p
.syntax()
.descendants()
.filter(|n| n.kind() == SyntaxKind::IMPLICIT_SEQUENCE)
.collect();
assert_eq!(
sequences.len(),
2,
"expected outer and inner IMPLICIT_SEQUENCE"
);
for seq in &sequences {
let branches = seq
.children()
.filter(|n| n.kind() == SyntaxKind::BRANCH_CONTENT)
.count();
assert_eq!(branches, 2, "expected both branches intact in {seq:?}");
}
}
#[test]
fn stray_pipe_in_multiline_branch_body_does_not_hang() {
let src = "{\n- x:\n a | b\n}\n";
let p = parse(src);
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
}
#[test]
fn stray_r_brace_in_multiline_branchless_cond_body_does_not_hang() {
let src = "{x:\n a } b\n";
let p = parse(src);
assert_eq!(src, p.syntax().text().to_string(), "lossless round-trip");
}