diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-30 11:19:02 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-30 11:19:02 +0100 |
commit | d2ea3f25b53deac5785485ab5dfe5e6b3b893bf5 (patch) | |
tree | b979afc6883c973d4b95765a292e6e5c8b819e96 /crates/ra_syntax | |
parent | 3901198e87df9c85b4a2e996240ca05c6950900f (diff) | |
parent | ddb9cc47d17204e5d52529ca04a4093f8ed8ec08 (diff) |
Merge #3761
3761: Append new match arms rather than replacing all of them r=matklad a=mattyhall
This means we now retain comments when filling in match arms. This fixes #3687. This is my first contribution so apologies if it needs a rethink! I think in particular the way I find the position to append to and remove_if_only_whitespace are a little hairy.
Co-authored-by: Matthew Hall <[email protected]>
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 126 |
1 files changed, 96 insertions, 30 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 2304e00cf..baf9a1b4b 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,69 @@ 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 | self.replace_children(start..=end, &mut iter::empty()) | ||
368 | } | ||
369 | |||
370 | #[must_use] | ||
371 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
372 | let placeholder = self.arms().find(|arm| { | ||
373 | if let Some(ast::Pat::PlaceholderPat(_)) = arm.pat() { | ||
374 | return true; | ||
375 | } | ||
376 | false | ||
377 | }); | ||
378 | if let Some(placeholder) = placeholder { | ||
379 | let s: SyntaxElement = placeholder.syntax().clone().into(); | ||
380 | let e = s.clone(); | ||
381 | self.replace_children(s..=e, &mut iter::empty()) | ||
382 | } else { | ||
383 | self.clone() | ||
384 | } | ||
385 | } | ||
386 | |||
387 | #[must_use] | ||
388 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
389 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
390 | Some(t) => t, | ||
391 | None => return self.clone(), | ||
392 | }; | ||
393 | let position = InsertPosition::Before(r_curly.into()); | ||
394 | let arm_ws = tokens::WsBuilder::new(" "); | ||
395 | let match_indent = &leading_indent(self.syntax()).unwrap_or_default(); | ||
396 | let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent)); | ||
397 | let to_insert: ArrayVec<[SyntaxElement; 3]> = | ||
398 | [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into(); | ||
399 | self.insert_children(position, to_insert) | ||
400 | } | ||
401 | } | ||
402 | |||
337 | #[must_use] | 403 | #[must_use] |
338 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 404 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
339 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 405 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |