aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Hall <[email protected]>2020-03-28 20:44:12 +0000
committerMatthew Hall <[email protected]>2020-03-28 20:58:46 +0000
commitecc2615ba2da0e083a8dbfbf203d1fd7fe0bcaaf (patch)
treee35cea1b482c65b98e1025da47e9670df61e92ed
parent1c2d4135db867efe335a0654d86429bea7bb9caf (diff)
Append new match arms rather than replacing all of them
This means we now retain comments when filling in match arms.
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs73
-rw-r--r--crates/ra_syntax/src/ast/edit.rs142
2 files changed, 180 insertions, 35 deletions
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index add82e5b1..c45981c5c 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -7,7 +7,7 @@ use itertools::Itertools;
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8 8
9use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
10use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; 10use ra_syntax::ast::{self, make, AstNode, NameOwner};
11 11
12use ast::{MatchArm, Pat}; 12use ast::{MatchArm, Pat};
13 13
@@ -97,10 +97,8 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
97 } 97 }
98 98
99 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { 99 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
100 arms.extend(missing_arms); 100 let new_arm_list =
101 101 match_arm_list.remove_placeholder().append_arms(missing_arms.into_iter());
102 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
103 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
104 102
105 edit.target(match_expr.syntax().text_range()); 103 edit.target(match_expr.syntax().text_range());
106 edit.set_cursor(expr.syntax().text_range().start()); 104 edit.set_cursor(expr.syntax().text_range().start());
@@ -655,4 +653,69 @@ mod tests {
655 "#, 653 "#,
656 ); 654 );
657 } 655 }
656
657 #[test]
658 fn fill_match_arms_preserves_comments() {
659 check_assist(
660 fill_match_arms,
661 r#"
662 enum A {
663 One,
664 Two,
665 }
666 fn foo(a: A) {
667 match a {
668 // TODO: Fill this in<|>
669 A::One => {}
670 // This is where the rest should be
671 }
672 }
673 "#,
674 r#"
675 enum A {
676 One,
677 Two,
678 }
679 fn foo(a: A) {
680 match <|>a {
681 // TODO: Fill this in
682 A::One => {}
683 // This is where the rest should be
684 A::Two => {}
685 }
686 }
687 "#,
688 );
689 }
690
691 #[test]
692 fn fill_match_arms_preserves_comments_empty() {
693 check_assist(
694 fill_match_arms,
695 r#"
696 enum A {
697 One,
698 Two,
699 }
700 fn foo(a: A) {
701 match a {
702 // TODO: Fill this in<|>
703 }
704 }
705 "#,
706 r#"
707 enum A {
708 One,
709 Two,
710 }
711 fn foo(a: A) {
712 match <|>a {
713 // TODO: Fill this in
714 A::One => {}
715 A::Two => {}
716 }
717 }
718 "#,
719 );
720 }
658} 721}
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
49fn make_multiline<N>(node: N) -> N
50where
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
49impl ast::ItemList { 81impl 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
115impl ast::RecordFieldList { 118impl ast::RecordFieldList {
@@ -334,6 +337,85 @@ impl ast::UseTree {
334 } 337 }
335} 338}
336 339
340impl 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]
338pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 420pub 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()