diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-08-23 06:43:32 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-08-23 06:43:32 +0100 |
commit | e055cfacdfe3b3451484dae5d6ed08aefba133ca (patch) | |
tree | 259613dfe4b76b63966d491f03742813bfa969e9 /crates/ra_assists/src/fill_match_arms.rs | |
parent | 0c35d82329bc0952bd179ec37933c0955fd5ed26 (diff) | |
parent | e84f93cb5b651696637d87b98653d7e8f9149086 (diff) |
Merge #1724
1724: Refactor fill_match_arms assist to use AstBuilder facilities r=matklad a=viorina
Co-authored-by: Ekaterina Babshukova <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/fill_match_arms.rs')
-rw-r--r-- | crates/ra_assists/src/fill_match_arms.rs | 195 |
1 files changed, 72 insertions, 123 deletions
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs index 85ff5c052..ce715a449 100644 --- a/crates/ra_assists/src/fill_match_arms.rs +++ b/crates/ra_assists/src/fill_match_arms.rs | |||
@@ -1,97 +1,91 @@ | |||
1 | use itertools::Itertools; | 1 | use std::iter; |
2 | use std::fmt::Write; | ||
3 | 2 | ||
4 | use hir::{db::HirDatabase, AdtDef, FieldSource, HasSource}; | 3 | use hir::{db::HirDatabase, AdtDef, HasSource}; |
5 | use ra_syntax::ast::{self, AstNode}; | 4 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
6 | 5 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 6 | use crate::{ast_editor::AstBuilder, Assist, AssistCtx, AssistId}; |
8 | |||
9 | fn is_trivial_arm(arm: &ast::MatchArm) -> bool { | ||
10 | fn single_pattern(arm: &ast::MatchArm) -> Option<ast::Pat> { | ||
11 | let (pat,) = arm.pats().collect_tuple()?; | ||
12 | Some(pat) | ||
13 | } | ||
14 | match single_pattern(arm) { | ||
15 | Some(ast::Pat::PlaceholderPat(..)) => true, | ||
16 | _ => false, | ||
17 | } | ||
18 | } | ||
19 | 7 | ||
20 | pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 8 | pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
21 | let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; | 9 | let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; |
10 | let match_arm_list = match_expr.match_arm_list()?; | ||
22 | 11 | ||
23 | // We already have some match arms, so we don't provide any assists. | 12 | // We already have some match arms, so we don't provide any assists. |
24 | // Unless if there is only one trivial match arm possibly created | 13 | // Unless if there is only one trivial match arm possibly created |
25 | // by match postfix complete. Trivial match arm is the catch all arm. | 14 | // by match postfix complete. Trivial match arm is the catch all arm. |
26 | if let Some(arm_list) = match_expr.match_arm_list() { | 15 | let mut existing_arms = match_arm_list.arms(); |
27 | let mut arm_iter = arm_list.arms(); | 16 | if let Some(arm) = existing_arms.next() { |
28 | let first = arm_iter.next(); | 17 | if !is_trivial(&arm) || existing_arms.next().is_some() { |
29 | 18 | return None; | |
30 | match &first { | ||
31 | // If there arm list is empty or there is only one trivial arm, then proceed. | ||
32 | Some(arm) if is_trivial_arm(arm) => { | ||
33 | if arm_iter.next() != None { | ||
34 | return None; | ||
35 | } | ||
36 | } | ||
37 | None => {} | ||
38 | |||
39 | _ => { | ||
40 | return None; | ||
41 | } | ||
42 | } | 19 | } |
43 | }; | 20 | }; |
44 | 21 | ||
45 | let expr = match_expr.expr()?; | 22 | let expr = match_expr.expr()?; |
46 | let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None); | 23 | let enum_def = { |
47 | let match_expr_ty = analyzer.type_of(ctx.db, &expr)?; | 24 | let file_id = ctx.frange.file_id; |
48 | let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() { | 25 | let analyzer = hir::SourceAnalyzer::new(ctx.db, file_id, expr.syntax(), None); |
49 | Some((AdtDef::Enum(e), _)) => Some(e), | 26 | resolve_enum_def(ctx.db, &analyzer, &expr)? |
50 | _ => None, | 27 | }; |
51 | })?; | 28 | let variant_list = enum_def.variant_list()?; |
52 | let enum_name = enum_def.name(ctx.db)?; | ||
53 | let db = ctx.db; | ||
54 | 29 | ||
55 | ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { | 30 | ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { |
56 | let mut buf = format!("match {} {{\n", expr.syntax().text().to_string()); | 31 | let variants = variant_list.variants(); |
57 | let variants = enum_def.variants(db); | 32 | let arms = variants.into_iter().filter_map(build_pat).map(|pat| { |
58 | for variant in variants { | 33 | AstBuilder::<ast::MatchArm>::from_pieces( |
59 | let name = match variant.name(db) { | 34 | iter::once(pat), |
60 | Some(it) => it, | 35 | &AstBuilder::<ast::Expr>::unit(), |
61 | None => continue, | 36 | ) |
62 | }; | 37 | }); |
63 | write!(&mut buf, " {}::{}", enum_name, name.to_string()).unwrap(); | 38 | let new_arm_list = AstBuilder::<ast::MatchArmList>::from_arms(arms); |
64 | |||
65 | let pat = variant | ||
66 | .fields(db) | ||
67 | .into_iter() | ||
68 | .map(|field| { | ||
69 | let name = field.name(db).to_string(); | ||
70 | let src = field.source(db); | ||
71 | match src.ast { | ||
72 | FieldSource::Named(_) => name, | ||
73 | FieldSource::Pos(_) => "_".to_string(), | ||
74 | } | ||
75 | }) | ||
76 | .collect::<Vec<_>>(); | ||
77 | 39 | ||
78 | match pat.first().map(|s| s.as_str()) { | ||
79 | Some("_") => write!(&mut buf, "({})", pat.join(", ")).unwrap(), | ||
80 | Some(_) => write!(&mut buf, "{{{}}}", pat.join(", ")).unwrap(), | ||
81 | None => (), | ||
82 | }; | ||
83 | |||
84 | buf.push_str(" => (),\n"); | ||
85 | } | ||
86 | buf.push_str("}"); | ||
87 | edit.target(match_expr.syntax().text_range()); | 40 | edit.target(match_expr.syntax().text_range()); |
88 | edit.set_cursor(expr.syntax().text_range().start()); | 41 | edit.set_cursor(expr.syntax().text_range().start()); |
89 | edit.replace_node_and_indent(match_expr.syntax(), buf); | 42 | edit.replace_node_and_indent(match_arm_list.syntax(), new_arm_list.syntax().text()); |
90 | }); | 43 | }); |
91 | 44 | ||
92 | ctx.build() | 45 | ctx.build() |
93 | } | 46 | } |
94 | 47 | ||
48 | fn is_trivial(arm: &ast::MatchArm) -> bool { | ||
49 | arm.pats().any(|pat| match pat { | ||
50 | ast::Pat::PlaceholderPat(..) => true, | ||
51 | _ => false, | ||
52 | }) | ||
53 | } | ||
54 | |||
55 | fn resolve_enum_def( | ||
56 | db: &impl HirDatabase, | ||
57 | analyzer: &hir::SourceAnalyzer, | ||
58 | expr: &ast::Expr, | ||
59 | ) -> Option<ast::EnumDef> { | ||
60 | let expr_ty = analyzer.type_of(db, &expr)?; | ||
61 | |||
62 | analyzer.autoderef(db, expr_ty).find_map(|ty| match ty.as_adt() { | ||
63 | Some((AdtDef::Enum(e), _)) => Some(e.source(db).ast), | ||
64 | _ => None, | ||
65 | }) | ||
66 | } | ||
67 | |||
68 | fn build_pat(var: ast::EnumVariant) -> Option<ast::Pat> { | ||
69 | let path = &AstBuilder::<ast::Path>::from_pieces(var.parent_enum().name()?, var.name()?); | ||
70 | |||
71 | let pat: ast::Pat = match var.kind() { | ||
72 | ast::StructKind::Tuple(field_list) => { | ||
73 | let pats = iter::repeat(AstBuilder::<ast::PlaceholderPat>::placeholder().into()) | ||
74 | .take(field_list.fields().count()); | ||
75 | AstBuilder::<ast::TupleStructPat>::from_pieces(path, pats).into() | ||
76 | } | ||
77 | ast::StructKind::Named(field_list) => { | ||
78 | let pats = field_list | ||
79 | .fields() | ||
80 | .map(|f| AstBuilder::<ast::BindPat>::from_name(&f.name().unwrap()).into()); | ||
81 | AstBuilder::<ast::StructPat>::from_pieces(path, pats).into() | ||
82 | } | ||
83 | ast::StructKind::Unit => AstBuilder::<ast::PathPat>::from_path(path).into(), | ||
84 | }; | ||
85 | |||
86 | Some(pat) | ||
87 | } | ||
88 | |||
95 | #[cfg(test)] | 89 | #[cfg(test)] |
96 | mod tests { | 90 | mod tests { |
97 | use crate::helpers::{check_assist, check_assist_target}; | 91 | use crate::helpers::{check_assist, check_assist_target}; |
@@ -108,7 +102,7 @@ mod tests { | |||
108 | Bs, | 102 | Bs, |
109 | Cs(String), | 103 | Cs(String), |
110 | Ds(String, String), | 104 | Ds(String, String), |
111 | Es{x: usize, y: usize} | 105 | Es{ x: usize, y: usize } |
112 | } | 106 | } |
113 | 107 | ||
114 | fn main() { | 108 | fn main() { |
@@ -122,7 +116,7 @@ mod tests { | |||
122 | Bs, | 116 | Bs, |
123 | Cs(String), | 117 | Cs(String), |
124 | Ds(String, String), | 118 | Ds(String, String), |
125 | Es{x: usize, y: usize} | 119 | Es{ x: usize, y: usize } |
126 | } | 120 | } |
127 | 121 | ||
128 | fn main() { | 122 | fn main() { |
@@ -132,7 +126,7 @@ mod tests { | |||
132 | A::Bs => (), | 126 | A::Bs => (), |
133 | A::Cs(_) => (), | 127 | A::Cs(_) => (), |
134 | A::Ds(_, _) => (), | 128 | A::Ds(_, _) => (), |
135 | A::Es{x, y} => (), | 129 | A::Es{ x, y } => (), |
136 | } | 130 | } |
137 | } | 131 | } |
138 | "#, | 132 | "#, |
@@ -170,7 +164,7 @@ mod tests { | |||
170 | fill_match_arms, | 164 | fill_match_arms, |
171 | r#" | 165 | r#" |
172 | enum A { | 166 | enum A { |
173 | Es{x: usize, y: usize} | 167 | Es{ x: usize, y: usize } |
174 | } | 168 | } |
175 | 169 | ||
176 | fn foo(a: &mut A) { | 170 | fn foo(a: &mut A) { |
@@ -180,57 +174,12 @@ mod tests { | |||
180 | "#, | 174 | "#, |
181 | r#" | 175 | r#" |
182 | enum A { | 176 | enum A { |
183 | Es{x: usize, y: usize} | 177 | Es{ x: usize, y: usize } |
184 | } | 178 | } |
185 | 179 | ||
186 | fn foo(a: &mut A) { | 180 | fn foo(a: &mut A) { |
187 | match <|>a { | 181 | match <|>a { |
188 | A::Es{x, y} => (), | 182 | A::Es{ x, y } => (), |
189 | } | ||
190 | } | ||
191 | "#, | ||
192 | ); | ||
193 | |||
194 | check_assist( | ||
195 | fill_match_arms, | ||
196 | r#" | ||
197 | enum E { X, Y} | ||
198 | |||
199 | fn main() { | ||
200 | match &E::X<|> | ||
201 | } | ||
202 | "#, | ||
203 | r#" | ||
204 | enum E { X, Y} | ||
205 | |||
206 | fn main() { | ||
207 | match <|>&E::X { | ||
208 | E::X => (), | ||
209 | E::Y => (), | ||
210 | } | ||
211 | } | ||
212 | "#, | ||
213 | ); | ||
214 | } | ||
215 | |||
216 | #[test] | ||
217 | fn fill_match_arms_no_body() { | ||
218 | check_assist( | ||
219 | fill_match_arms, | ||
220 | r#" | ||
221 | enum E { X, Y} | ||
222 | |||
223 | fn main() { | ||
224 | match E::X<|> | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | enum E { X, Y} | ||
229 | |||
230 | fn main() { | ||
231 | match <|>E::X { | ||
232 | E::X => (), | ||
233 | E::Y => (), | ||
234 | } | 183 | } |
235 | } | 184 | } |
236 | "#, | 185 | "#, |
@@ -242,7 +191,7 @@ mod tests { | |||
242 | check_assist_target( | 191 | check_assist_target( |
243 | fill_match_arms, | 192 | fill_match_arms, |
244 | r#" | 193 | r#" |
245 | enum E { X, Y} | 194 | enum E { X, Y } |
246 | 195 | ||
247 | fn main() { | 196 | fn main() { |
248 | match E::X<|> {} | 197 | match E::X<|> {} |