use vize_carton::Vec;
use vize_relief::ast::TemplateChildNode;
pub(super) fn condense_whitespace<'a>(
children: &mut Vec<'a, TemplateChildNode<'a>>,
is_pre_tag: fn(&str) -> bool,
) {
while !children.is_empty() {
if let TemplateChildNode::Text(ref text) = children[0]
&& text.content.chars().all(char::is_whitespace)
{
children.remove(0);
continue;
}
break;
}
while !children.is_empty() {
let last = children.len() - 1;
if let TemplateChildNode::Text(ref text) = children[last]
&& text.content.chars().all(char::is_whitespace)
{
children.remove(last);
continue;
}
break;
}
let mut i = 0;
while i < children.len() {
let action = if is_whitespace_text(&children[i]) {
let mut run_end = i + 1;
let mut has_newline = whitespace_has_newline(&children[i]);
while run_end < children.len() && is_whitespace_text(&children[run_end]) {
has_newline |= whitespace_has_newline(&children[run_end]);
run_end += 1;
}
let prev = (0..i)
.rev()
.find(|&idx| !is_whitespace_text(&children[idx]));
let next = (run_end..children.len()).find(|&idx| !is_whitespace_text(&children[idx]));
let prev_is_text = prev.is_some_and(|idx| is_text_like(&children[idx]));
let next_is_text = next.is_some_and(|idx| is_text_like(&children[idx]));
if !prev_is_text && !next_is_text && has_newline {
WhitespaceAction::Remove(run_end - i)
} else {
WhitespaceAction::Condense(run_end - i)
}
} else {
WhitespaceAction::Keep
};
match action {
WhitespaceAction::Remove(len) => {
for _ in 0..len {
children.remove(i);
}
continue;
}
WhitespaceAction::Condense(len) => {
if let TemplateChildNode::Text(ref mut text) = children[i] {
text.content = " ".into();
}
for _ in 1..len {
children.remove(i + 1);
}
}
WhitespaceAction::Keep => {}
}
if let TemplateChildNode::Element(ref mut el) = children[i]
&& !is_pre_tag(el.tag.as_str())
{
condense_whitespace(&mut el.children, is_pre_tag);
}
i += 1;
}
}
#[inline]
fn is_whitespace_text(child: &TemplateChildNode<'_>) -> bool {
matches!(child, TemplateChildNode::Text(text) if text.content.chars().all(char::is_whitespace))
}
#[inline]
fn whitespace_has_newline(child: &TemplateChildNode<'_>) -> bool {
matches!(
child,
TemplateChildNode::Text(text) if text.content.contains('\n') || text.content.contains('\r')
)
}
#[inline]
fn is_text_like(child: &TemplateChildNode<'_>) -> bool {
match child {
TemplateChildNode::Interpolation(_) => true,
TemplateChildNode::Text(text) => !text.content.chars().all(char::is_whitespace),
_ => false,
}
}
enum WhitespaceAction {
Keep,
Remove(usize),
Condense(usize),
}