aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax/src/ast
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-16 13:10:18 +0100
committerAleksey Kladov <[email protected]>2021-05-16 13:10:18 +0100
commite4a7b44e554e183fc66474bcbd8f7ace541c5536 (patch)
treee606c6fe7990c468ce58049b3074aa70542ce906 /crates/syntax/src/ast
parent4e142757e167ac16ce65ba1c743e131aba83cdc4 (diff)
internal: use mutable trees when filling match arms
Diffstat (limited to 'crates/syntax/src/ast')
-rw-r--r--crates/syntax/src/ast/edit.rs105
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs90
2 files changed, 69 insertions, 126 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 4b5f5c571..5e6c1d44e 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -29,38 +29,6 @@ impl ast::BinExpr {
29 } 29 }
30} 30}
31 31
32fn make_multiline<N>(node: N) -> N
33where
34 N: AstNode + Clone,
35{
36 let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
37 Some(it) => it,
38 None => return node,
39 };
40 let sibling = match l_curly.next_sibling_or_token() {
41 Some(it) => it,
42 None => return node,
43 };
44 let existing_ws = match sibling.as_token() {
45 None => None,
46 Some(tok) if tok.kind() != WHITESPACE => None,
47 Some(ws) => {
48 if ws.text().contains('\n') {
49 return node;
50 }
51 Some(ws.clone())
52 }
53 };
54
55 let indent = leading_indent(node.syntax()).unwrap_or_default();
56 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
57 let to_insert = iter::once(ws.ws().into());
58 match existing_ws {
59 None => node.insert_children(InsertPosition::After(l_curly), to_insert),
60 Some(ws) => node.replace_children(single_node(ws), to_insert),
61 }
62}
63
64impl ast::RecordExprFieldList { 32impl ast::RecordExprFieldList {
65 #[must_use] 33 #[must_use]
66 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { 34 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
@@ -214,79 +182,6 @@ impl ast::UseTree {
214 } 182 }
215} 183}
216 184
217impl ast::MatchArmList {
218 #[must_use]
219 pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
220 let mut res = self.clone();
221 res = res.strip_if_only_whitespace();
222 if !res.syntax().text().contains_char('\n') {
223 res = make_multiline(res);
224 }
225 items.into_iter().for_each(|it| res = res.append_arm(it));
226 res
227 }
228
229 fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
230 let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
231 iter.next(); // Eat the curly
232 let mut inner = iter.take_while(|it| it.kind() != T!['}']);
233 if !inner.clone().all(|it| it.kind() == WHITESPACE) {
234 return self.clone();
235 }
236 let start = match inner.next() {
237 Some(s) => s,
238 None => return self.clone(),
239 };
240 let end = match inner.last() {
241 Some(s) => s,
242 None => start.clone(),
243 };
244 self.replace_children(start..=end, &mut iter::empty())
245 }
246
247 #[must_use]
248 pub fn remove_placeholder(&self) -> ast::MatchArmList {
249 let placeholder =
250 self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
251 if let Some(placeholder) = placeholder {
252 self.remove_arm(&placeholder)
253 } else {
254 self.clone()
255 }
256 }
257
258 #[must_use]
259 fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
260 let start = arm.syntax().clone();
261 let end = if let Some(comma) = start
262 .siblings_with_tokens(Direction::Next)
263 .skip(1)
264 .find(|it| !it.kind().is_trivia())
265 .filter(|it| it.kind() == T![,])
266 {
267 comma
268 } else {
269 start.clone().into()
270 };
271 self.replace_children(start.into()..=end, None)
272 }
273
274 #[must_use]
275 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
276 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
277 Some(t) => t,
278 None => return self.clone(),
279 };
280 let position = InsertPosition::Before(r_curly);
281 let arm_ws = tokens::WsBuilder::new(" ");
282 let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
283 let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
284 let to_insert: ArrayVec<SyntaxElement, 3> =
285 [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
286 self.insert_children(position, to_insert)
287 }
288}
289
290#[must_use] 185#[must_use]
291pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 186pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
292 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 187 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index ca777d057..abab0269a 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -13,7 +13,7 @@ use crate::{
13 make, GenericParamsOwner, 13 make, GenericParamsOwner,
14 }, 14 },
15 ted::{self, Position}, 15 ted::{self, Position},
16 AstNode, AstToken, Direction, 16 AstNode, AstToken, Direction, SyntaxNode,
17}; 17};
18 18
19use super::NameOwner; 19use super::NameOwner;
@@ -297,7 +297,7 @@ impl ast::AssocItemList {
297 ), 297 ),
298 None => match self.l_curly_token() { 298 None => match self.l_curly_token() {
299 Some(l_curly) => { 299 Some(l_curly) => {
300 self.normalize_ws_between_braces(); 300 normalize_ws_between_braces(self.syntax());
301 (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") 301 (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
302 } 302 }
303 None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), 303 None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
@@ -309,25 +309,6 @@ impl ast::AssocItemList {
309 ]; 309 ];
310 ted::insert_all(position, elements); 310 ted::insert_all(position, elements);
311 } 311 }
312
313 fn normalize_ws_between_braces(&self) -> Option<()> {
314 let l = self.l_curly_token()?;
315 let r = self.r_curly_token()?;
316 let indent = IndentLevel::from_node(self.syntax());
317
318 match l.next_sibling_or_token() {
319 Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
320 if ws.next_sibling_or_token()?.into_token()? == r {
321 ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
322 }
323 }
324 Some(ws) if ws.kind() == T!['}'] => {
325 ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
326 }
327 _ => (),
328 }
329 Some(())
330 }
331} 312}
332 313
333impl ast::Fn { 314impl ast::Fn {
@@ -346,6 +327,73 @@ impl ast::Fn {
346 } 327 }
347} 328}
348 329
330impl ast::MatchArm {
331 pub fn remove(&self) {
332 if let Some(sibling) = self.syntax().prev_sibling_or_token() {
333 if sibling.kind() == SyntaxKind::WHITESPACE {
334 ted::remove(sibling);
335 }
336 }
337 if let Some(sibling) = self.syntax().next_sibling_or_token() {
338 if sibling.kind() == T![,] {
339 ted::remove(sibling);
340 }
341 }
342 ted::remove(self.syntax());
343 }
344}
345
346impl ast::MatchArmList {
347 pub fn add_arm(&self, arm: ast::MatchArm) {
348 normalize_ws_between_braces(self.syntax());
349 let position = match self.arms().last() {
350 Some(last_arm) => {
351 let curly = last_arm
352 .syntax()
353 .siblings_with_tokens(Direction::Next)
354 .find(|it| it.kind() == T![,]);
355 Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into()))
356 }
357 None => match self.l_curly_token() {
358 Some(it) => Position::after(it),
359 None => Position::last_child_of(self.syntax()),
360 },
361 };
362 let indent = IndentLevel::from_node(self.syntax()) + 1;
363 let elements = vec![
364 make::tokens::whitespace(&format!("\n{}", indent)).into(),
365 arm.syntax().clone().into(),
366 ];
367 ted::insert_all(position, elements);
368 }
369}
370
371fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
372 let l = node
373 .children_with_tokens()
374 .filter_map(|it| it.into_token())
375 .find(|it| it.kind() == T!['{'])?;
376 let r = node
377 .children_with_tokens()
378 .filter_map(|it| it.into_token())
379 .find(|it| it.kind() == T!['}'])?;
380
381 let indent = IndentLevel::from_node(node);
382
383 match l.next_sibling_or_token() {
384 Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
385 if ws.next_sibling_or_token()?.into_token()? == r {
386 ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
387 }
388 }
389 Some(ws) if ws.kind() == T!['}'] => {
390 ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
391 }
392 _ => (),
393 }
394 Some(())
395}
396
349#[cfg(test)] 397#[cfg(test)]
350mod tests { 398mod tests {
351 use std::fmt; 399 use std::fmt;