use crate::parser::ast::*;
macro_rules! trim_right_previous {
($vec: expr) => {
if let Some(last) = $vec.pop() {
if let Node::Text(mut s) = last {
s = s.trim_right().to_string();
if !s.is_empty() {
$vec.push(Node::Text(s));
}
} else {
$vec.push(last);
}
}
};
($cond: expr, $vec: expr) => {
if $cond {
trim_right_previous!($vec);
}
};
}
pub fn remove_whitespace(nodes: Vec<Node>, body_ws: Option<WS>) -> Vec<Node> {
let mut res = Vec::with_capacity(nodes.len());
let mut previous_was_text = false;
let mut trim_left_next = body_ws.map_or(false, |ws| ws.left);
for n in nodes {
match n {
Node::Text(s) => {
previous_was_text = true;
if !trim_left_next {
res.push(Node::Text(s));
continue;
}
trim_left_next = false;
let new_val = s.trim_left();
if !new_val.is_empty() {
res.push(Node::Text(new_val.to_string()));
}
continue;
}
Node::ImportMacro(ws, _, _)
| Node::Extends(ws, _)
| Node::Include(ws, _)
| Node::Set(ws, _)
| Node::Break(ws)
| Node::Continue(ws) => {
trim_right_previous!(previous_was_text && ws.left, res);
trim_left_next = ws.right;
}
Node::Raw(start_ws, ref s, end_ws) => {
trim_right_previous!(previous_was_text && start_ws.left, res);
previous_was_text = false;
trim_left_next = end_ws.right;
if start_ws.right || end_ws.left {
let val = if start_ws.right && end_ws.left {
s.trim()
} else if start_ws.right {
s.trim_left()
} else {
s.trim_right()
};
res.push(Node::Raw(start_ws, val.to_string(), end_ws));
continue;
}
}
Node::Forloop(start_ws, _, end_ws)
| Node::MacroDefinition(start_ws, _, end_ws)
| Node::FilterSection(start_ws, _, end_ws)
| Node::Block(start_ws, _, end_ws) => {
trim_right_previous!(previous_was_text && start_ws.left, res);
previous_was_text = false;
trim_left_next = end_ws.right;
let body_ws = WS { left: start_ws.right, right: end_ws.left };
match n {
Node::Forloop(_, mut forloop, _) => {
forloop.body = remove_whitespace(forloop.body, Some(body_ws));
res.push(Node::Forloop(start_ws, forloop, end_ws));
}
Node::MacroDefinition(_, mut macro_def, _) => {
macro_def.body = remove_whitespace(macro_def.body, Some(body_ws));
res.push(Node::MacroDefinition(start_ws, macro_def, end_ws));
}
Node::FilterSection(_, mut filter_section, _) => {
filter_section.body = remove_whitespace(filter_section.body, Some(body_ws));
res.push(Node::FilterSection(start_ws, filter_section, end_ws));
}
Node::Block(_, mut block, _) => {
block.body = remove_whitespace(block.body, Some(body_ws));
res.push(Node::Block(start_ws, block, end_ws));
}
_ => unreachable!(),
};
continue;
}
Node::If(If { conditions, otherwise }, end_ws) => {
trim_left_next = end_ws.right;
let mut new_conditions: Vec<(_, _, Vec<_>)> = Vec::with_capacity(conditions.len());
for mut condition in conditions {
if condition.0.left {
if new_conditions.is_empty() && previous_was_text {
trim_right_previous!(res);
} else if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
trim_right_previous!(body);
}
}
condition.2 = remove_whitespace(
condition.2,
Some(WS { left: condition.0.right, right: false }),
);
new_conditions.push(condition);
}
previous_was_text = false;
if let Some((else_ws, body)) = otherwise {
if else_ws.left {
if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
trim_right_previous!(body);
}
}
let mut else_body =
remove_whitespace(body, Some(WS { left: else_ws.right, right: false }));
if end_ws.left {
trim_right_previous!(else_body);
}
res.push(Node::If(
If { conditions: new_conditions, otherwise: Some((else_ws, else_body)) },
end_ws,
));
continue;
}
if end_ws.left {
if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
trim_right_previous!(true, body);
}
}
res.push(Node::If(If { conditions: new_conditions, otherwise }, end_ws));
continue;
}
Node::Super | Node::VariableBlock(_) => (),
};
previous_was_text = false;
res.push(n);
}
if let Some(whitespace) = body_ws {
trim_right_previous!(whitespace.right, res);
}
res
}