tera/parser/
whitespace.rs1use crate::parser::ast::*;
2
3macro_rules! trim_right_previous {
4 ($vec: expr) => {
5 if let Some(last) = $vec.pop() {
6 if let Node::Text(mut s) = last {
7 s = s.trim_end().to_string();
8 if !s.is_empty() {
9 $vec.push(Node::Text(s));
10 }
11 } else {
12 $vec.push(last);
13 }
14 }
15 };
16 ($cond: expr, $vec: expr) => {
17 if $cond {
18 trim_right_previous!($vec);
19 }
20 };
21}
22
23pub fn remove_whitespace(nodes: Vec<Node>, body_ws: Option<WS>) -> Vec<Node> {
31 let mut res = Vec::with_capacity(nodes.len());
32
33 let mut previous_was_text = false;
35 let mut trim_left_next = body_ws.map_or(false, |ws| ws.left);
37
38 for n in nodes {
39 match n {
40 Node::Text(s) => {
41 previous_was_text = true;
42
43 if !trim_left_next {
44 res.push(Node::Text(s));
45 continue;
46 }
47 trim_left_next = false;
48
49 let new_val = s.trim_start();
50 if !new_val.is_empty() {
51 res.push(Node::Text(new_val.to_string()));
52 }
53 continue;
55 }
56 Node::VariableBlock(ws, _)
57 | Node::ImportMacro(ws, _, _)
58 | Node::Extends(ws, _)
59 | Node::Include(ws, _, _)
60 | Node::Set(ws, _)
61 | Node::Break(ws)
62 | Node::Comment(ws, _)
63 | Node::Continue(ws) => {
64 trim_right_previous!(previous_was_text && ws.left, res);
65 trim_left_next = ws.right;
66 }
67 Node::Raw(start_ws, ref s, end_ws) => {
68 trim_right_previous!(previous_was_text && start_ws.left, res);
69 previous_was_text = false;
70 trim_left_next = end_ws.right;
71
72 if start_ws.right || end_ws.left {
73 let val = if start_ws.right && end_ws.left {
74 s.trim()
75 } else if start_ws.right {
76 s.trim_start()
77 } else {
78 s.trim_end()
79 };
80
81 res.push(Node::Raw(start_ws, val.to_string(), end_ws));
82 continue;
83 }
84 }
85 Node::Forloop(start_ws, _, end_ws)
87 | Node::MacroDefinition(start_ws, _, end_ws)
88 | Node::FilterSection(start_ws, _, end_ws)
89 | Node::Block(start_ws, _, end_ws) => {
90 trim_right_previous!(previous_was_text && start_ws.left, res);
91 previous_was_text = false;
92 trim_left_next = end_ws.right;
93
94 let body_ws = WS { left: start_ws.right, right: end_ws.left };
96 match n {
97 Node::Forloop(_, mut forloop, _) => {
98 forloop.body = remove_whitespace(forloop.body, Some(body_ws));
99 res.push(Node::Forloop(start_ws, forloop, end_ws));
100 }
101 Node::MacroDefinition(_, mut macro_def, _) => {
102 macro_def.body = remove_whitespace(macro_def.body, Some(body_ws));
103 res.push(Node::MacroDefinition(start_ws, macro_def, end_ws));
104 }
105 Node::FilterSection(_, mut filter_section, _) => {
106 filter_section.body = remove_whitespace(filter_section.body, Some(body_ws));
107 res.push(Node::FilterSection(start_ws, filter_section, end_ws));
108 }
109 Node::Block(_, mut block, _) => {
110 block.body = remove_whitespace(block.body, Some(body_ws));
111 res.push(Node::Block(start_ws, block, end_ws));
112 }
113 _ => unreachable!(),
114 };
115 continue;
116 }
117 Node::If(If { conditions, otherwise }, end_ws) => {
119 trim_left_next = end_ws.right;
120 let mut new_conditions: Vec<(_, _, Vec<_>)> = Vec::with_capacity(conditions.len());
121
122 for mut condition in conditions {
123 if condition.0.left {
124 if new_conditions.is_empty() && previous_was_text {
126 trim_right_previous!(res);
127 } else if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
128 trim_right_previous!(body);
129 }
130 }
131
132 condition.2 = remove_whitespace(
135 condition.2,
136 Some(WS { left: condition.0.right, right: false }),
137 );
138 new_conditions.push(condition);
139 }
140
141 previous_was_text = false;
142
143 if let Some((else_ws, body)) = otherwise {
147 if else_ws.left {
148 if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
149 trim_right_previous!(body);
150 }
151 }
152 let mut else_body =
153 remove_whitespace(body, Some(WS { left: else_ws.right, right: false }));
154 if end_ws.left {
156 trim_right_previous!(else_body);
157 }
158 res.push(Node::If(
159 If { conditions: new_conditions, otherwise: Some((else_ws, else_body)) },
160 end_ws,
161 ));
162 continue;
163 }
164
165 if end_ws.left {
167 if let Some(&mut (_, _, ref mut body)) = new_conditions.last_mut() {
168 trim_right_previous!(true, body);
169 }
170 }
171
172 res.push(Node::If(If { conditions: new_conditions, otherwise }, end_ws));
173 continue;
174 }
175 Node::Super => (),
176 };
177
178 previous_was_text = false;
180 res.push(n);
181 }
182
183 if let Some(whitespace) = body_ws {
184 trim_right_previous!(whitespace.right, res);
185 }
186
187 res
188}