diff options
author | Matthew Hall <[email protected]> | 2020-03-28 20:44:12 +0000 |
---|---|---|
committer | Matthew Hall <[email protected]> | 2020-03-28 20:58:46 +0000 |
commit | ecc2615ba2da0e083a8dbfbf203d1fd7fe0bcaaf (patch) | |
tree | e35cea1b482c65b98e1025da47e9670df61e92ed /crates/ra_syntax/src/ast/edit.rs | |
parent | 1c2d4135db867efe335a0654d86429bea7bb9caf (diff) |
Append new match arms rather than replacing all of them
This means we now retain comments when filling in match arms.
Diffstat (limited to 'crates/ra_syntax/src/ast/edit.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 142 |
1 files changed, 112 insertions, 30 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 2304e00cf..437186888 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -46,12 +46,44 @@ impl ast::FnDef { | |||
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
49 | fn make_multiline<N>(node: N) -> N | ||
50 | where | ||
51 | N: AstNode + Clone, | ||
52 | { | ||
53 | let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
54 | Some(it) => it, | ||
55 | None => return node, | ||
56 | }; | ||
57 | let sibling = match l_curly.next_sibling_or_token() { | ||
58 | Some(it) => it, | ||
59 | None => return node, | ||
60 | }; | ||
61 | let existing_ws = match sibling.as_token() { | ||
62 | None => None, | ||
63 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
64 | Some(ws) => { | ||
65 | if ws.text().contains('\n') { | ||
66 | return node; | ||
67 | } | ||
68 | Some(ws.clone()) | ||
69 | } | ||
70 | }; | ||
71 | |||
72 | let indent = leading_indent(node.syntax()).unwrap_or_default(); | ||
73 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
74 | let to_insert = iter::once(ws.ws().into()); | ||
75 | match existing_ws { | ||
76 | None => node.insert_children(InsertPosition::After(l_curly), to_insert), | ||
77 | Some(ws) => node.replace_children(single_node(ws), to_insert), | ||
78 | } | ||
79 | } | ||
80 | |||
49 | impl ast::ItemList { | 81 | impl ast::ItemList { |
50 | #[must_use] | 82 | #[must_use] |
51 | pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList { | 83 | pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList { |
52 | let mut res = self.clone(); | 84 | let mut res = self.clone(); |
53 | if !self.syntax().text().contains_char('\n') { | 85 | if !self.syntax().text().contains_char('\n') { |
54 | res = res.make_multiline(); | 86 | res = make_multiline(res); |
55 | } | 87 | } |
56 | items.for_each(|it| res = res.append_item(it)); | 88 | items.for_each(|it| res = res.append_item(it)); |
57 | res | 89 | res |
@@ -81,35 +113,6 @@ impl ast::ItemList { | |||
81 | fn l_curly(&self) -> Option<SyntaxElement> { | 113 | fn l_curly(&self) -> Option<SyntaxElement> { |
82 | self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | 114 | self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) |
83 | } | 115 | } |
84 | |||
85 | fn make_multiline(&self) -> ast::ItemList { | ||
86 | let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
87 | Some(it) => it, | ||
88 | None => return self.clone(), | ||
89 | }; | ||
90 | let sibling = match l_curly.next_sibling_or_token() { | ||
91 | Some(it) => it, | ||
92 | None => return self.clone(), | ||
93 | }; | ||
94 | let existing_ws = match sibling.as_token() { | ||
95 | None => None, | ||
96 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
97 | Some(ws) => { | ||
98 | if ws.text().contains('\n') { | ||
99 | return self.clone(); | ||
100 | } | ||
101 | Some(ws.clone()) | ||
102 | } | ||
103 | }; | ||
104 | |||
105 | let indent = leading_indent(self.syntax()).unwrap_or_default(); | ||
106 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
107 | let to_insert = iter::once(ws.ws().into()); | ||
108 | match existing_ws { | ||
109 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), | ||
110 | Some(ws) => self.replace_children(single_node(ws), to_insert), | ||
111 | } | ||
112 | } | ||
113 | } | 116 | } |
114 | 117 | ||
115 | impl ast::RecordFieldList { | 118 | impl ast::RecordFieldList { |
@@ -334,6 +337,85 @@ impl ast::UseTree { | |||
334 | } | 337 | } |
335 | } | 338 | } |
336 | 339 | ||
340 | impl ast::MatchArmList { | ||
341 | #[must_use] | ||
342 | pub fn append_arms(&self, items: impl Iterator<Item = ast::MatchArm>) -> ast::MatchArmList { | ||
343 | let mut res = self.clone(); | ||
344 | res = res.strip_if_only_whitespace(); | ||
345 | if !res.syntax().text().contains_char('\n') { | ||
346 | res = make_multiline(res); | ||
347 | } | ||
348 | items.for_each(|it| res = res.append_arm(it)); | ||
349 | res | ||
350 | } | ||
351 | |||
352 | fn strip_if_only_whitespace(&self) -> ast::MatchArmList { | ||
353 | let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']); | ||
354 | iter.next(); // Eat the curly | ||
355 | let mut inner = iter.take_while(|it| it.kind() != T!['}']); | ||
356 | if !inner.clone().all(|it| it.kind() == WHITESPACE) { | ||
357 | return self.clone(); | ||
358 | } | ||
359 | let start = match inner.next() { | ||
360 | Some(s) => s, | ||
361 | None => return self.clone(), | ||
362 | }; | ||
363 | let end = match inner.last() { | ||
364 | Some(s) => s, | ||
365 | None => start.clone(), | ||
366 | }; | ||
367 | let res = self.replace_children(start..=end, &mut iter::empty()); | ||
368 | res | ||
369 | } | ||
370 | |||
371 | #[must_use] | ||
372 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
373 | let placeholder = self.arms().find(|arm| { | ||
374 | if let Some(ast::Pat::PlaceholderPat(_)) = arm.pat() { | ||
375 | return true; | ||
376 | } | ||
377 | false | ||
378 | }); | ||
379 | if let Some(placeholder) = placeholder { | ||
380 | let s: SyntaxElement = placeholder.syntax().clone().into(); | ||
381 | let e = s.clone(); | ||
382 | self.replace_children(s..=e, &mut iter::empty()) | ||
383 | } else { | ||
384 | self.clone() | ||
385 | } | ||
386 | } | ||
387 | |||
388 | #[must_use] | ||
389 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
390 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
391 | Some(t) => t, | ||
392 | None => return self.clone(), | ||
393 | }; | ||
394 | let mut sib = r_curly.prev_sibling_or_token(); | ||
395 | while let Some(s) = sib.clone() { | ||
396 | if let Some(tok) = s.as_token() { | ||
397 | if tok.kind() != WHITESPACE { | ||
398 | break; | ||
399 | } | ||
400 | sib = s.prev_sibling_or_token(); | ||
401 | } else { | ||
402 | break; | ||
403 | } | ||
404 | } | ||
405 | let indent = " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(); | ||
406 | let sib = match sib { | ||
407 | Some(s) => s, | ||
408 | None => return self.clone(), | ||
409 | }; | ||
410 | let position = InsertPosition::After(sib.into()); | ||
411 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
412 | let to_insert: ArrayVec<[SyntaxElement; 2]> = | ||
413 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
414 | let res = self.insert_children(position, to_insert); | ||
415 | res | ||
416 | } | ||
417 | } | ||
418 | |||
337 | #[must_use] | 419 | #[must_use] |
338 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 420 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
339 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 421 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |