diff options
author | Aleksey Kladov <[email protected]> | 2020-02-07 14:53:31 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-02-07 16:28:02 +0000 |
commit | 561b4b11ff1d87ea1ff2477dcba6ae1f396573a3 (patch) | |
tree | 0da58d08d5a2ff27f43c3eb6163ba9aced2f5782 /crates/ra_assists/src/assists/fill_match_arms.rs | |
parent | aa64a84b493aa9c0b22f36b472a445d622cd2172 (diff) |
Name assist handlers
Diffstat (limited to 'crates/ra_assists/src/assists/fill_match_arms.rs')
-rw-r--r-- | crates/ra_assists/src/assists/fill_match_arms.rs | 290 |
1 files changed, 0 insertions, 290 deletions
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs deleted file mode 100644 index 0908fc246..000000000 --- a/crates/ra_assists/src/assists/fill_match_arms.rs +++ /dev/null | |||
@@ -1,290 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::iter; | ||
4 | |||
5 | use hir::{db::HirDatabase, Adt, HasSource}; | ||
6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | |||
10 | // Assist: fill_match_arms | ||
11 | // | ||
12 | // Adds missing clauses to a `match` expression. | ||
13 | // | ||
14 | // ``` | ||
15 | // enum Action { Move { distance: u32 }, Stop } | ||
16 | // | ||
17 | // fn handle(action: Action) { | ||
18 | // match action { | ||
19 | // <|> | ||
20 | // } | ||
21 | // } | ||
22 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // enum Action { Move { distance: u32 }, Stop } | ||
26 | // | ||
27 | // fn handle(action: Action) { | ||
28 | // match action { | ||
29 | // Action::Move { distance } => (), | ||
30 | // Action::Stop => (), | ||
31 | // } | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | ||
35 | let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; | ||
36 | let match_arm_list = match_expr.match_arm_list()?; | ||
37 | |||
38 | // We already have some match arms, so we don't provide any assists. | ||
39 | // Unless if there is only one trivial match arm possibly created | ||
40 | // by match postfix complete. Trivial match arm is the catch all arm. | ||
41 | let mut existing_arms = match_arm_list.arms(); | ||
42 | if let Some(arm) = existing_arms.next() { | ||
43 | if !is_trivial(&arm) || existing_arms.next().is_some() { | ||
44 | return None; | ||
45 | } | ||
46 | }; | ||
47 | |||
48 | let expr = match_expr.expr()?; | ||
49 | let (enum_def, module) = { | ||
50 | let analyzer = ctx.source_analyzer(expr.syntax(), None); | ||
51 | (resolve_enum_def(ctx.db, &analyzer, &expr)?, analyzer.module()?) | ||
52 | }; | ||
53 | let variants = enum_def.variants(ctx.db); | ||
54 | if variants.is_empty() { | ||
55 | return None; | ||
56 | } | ||
57 | |||
58 | let db = ctx.db; | ||
59 | |||
60 | ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { | ||
61 | let indent_level = IndentLevel::from_node(match_arm_list.syntax()); | ||
62 | |||
63 | let new_arm_list = { | ||
64 | let arms = variants | ||
65 | .into_iter() | ||
66 | .filter_map(|variant| build_pat(db, module, variant)) | ||
67 | .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())); | ||
68 | indent_level.increase_indent(make::match_arm_list(arms)) | ||
69 | }; | ||
70 | |||
71 | edit.target(match_expr.syntax().text_range()); | ||
72 | edit.set_cursor(expr.syntax().text_range().start()); | ||
73 | edit.replace_ast(match_arm_list, new_arm_list); | ||
74 | }) | ||
75 | } | ||
76 | |||
77 | fn is_trivial(arm: &ast::MatchArm) -> bool { | ||
78 | arm.pats().any(|pat| match pat { | ||
79 | ast::Pat::PlaceholderPat(..) => true, | ||
80 | _ => false, | ||
81 | }) | ||
82 | } | ||
83 | |||
84 | fn resolve_enum_def( | ||
85 | db: &impl HirDatabase, | ||
86 | analyzer: &hir::SourceAnalyzer, | ||
87 | expr: &ast::Expr, | ||
88 | ) -> Option<hir::Enum> { | ||
89 | let expr_ty = analyzer.type_of(db, &expr)?; | ||
90 | |||
91 | let result = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() { | ||
92 | Some(Adt::Enum(e)) => Some(e), | ||
93 | _ => None, | ||
94 | }); | ||
95 | result | ||
96 | } | ||
97 | |||
98 | fn build_pat( | ||
99 | db: &impl HirDatabase, | ||
100 | module: hir::Module, | ||
101 | var: hir::EnumVariant, | ||
102 | ) -> Option<ast::Pat> { | ||
103 | let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); | ||
104 | |||
105 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | ||
106 | let pat: ast::Pat = match var.source(db).value.kind() { | ||
107 | ast::StructKind::Tuple(field_list) => { | ||
108 | let pats = | ||
109 | iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count()); | ||
110 | make::tuple_struct_pat(path, pats).into() | ||
111 | } | ||
112 | ast::StructKind::Record(field_list) => { | ||
113 | let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into()); | ||
114 | make::record_pat(path, pats).into() | ||
115 | } | ||
116 | ast::StructKind::Unit => make::path_pat(path), | ||
117 | }; | ||
118 | |||
119 | Some(pat) | ||
120 | } | ||
121 | |||
122 | #[cfg(test)] | ||
123 | mod tests { | ||
124 | use crate::helpers::{check_assist, check_assist_target}; | ||
125 | |||
126 | use super::fill_match_arms; | ||
127 | |||
128 | #[test] | ||
129 | fn fill_match_arms_empty_body() { | ||
130 | check_assist( | ||
131 | fill_match_arms, | ||
132 | r#" | ||
133 | enum A { | ||
134 | As, | ||
135 | Bs, | ||
136 | Cs(String), | ||
137 | Ds(String, String), | ||
138 | Es{ x: usize, y: usize } | ||
139 | } | ||
140 | |||
141 | fn main() { | ||
142 | let a = A::As; | ||
143 | match a<|> {} | ||
144 | } | ||
145 | "#, | ||
146 | r#" | ||
147 | enum A { | ||
148 | As, | ||
149 | Bs, | ||
150 | Cs(String), | ||
151 | Ds(String, String), | ||
152 | Es{ x: usize, y: usize } | ||
153 | } | ||
154 | |||
155 | fn main() { | ||
156 | let a = A::As; | ||
157 | match <|>a { | ||
158 | A::As => (), | ||
159 | A::Bs => (), | ||
160 | A::Cs(_) => (), | ||
161 | A::Ds(_, _) => (), | ||
162 | A::Es { x, y } => (), | ||
163 | } | ||
164 | } | ||
165 | "#, | ||
166 | ); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn test_fill_match_arm_refs() { | ||
171 | check_assist( | ||
172 | fill_match_arms, | ||
173 | r#" | ||
174 | enum A { | ||
175 | As, | ||
176 | } | ||
177 | |||
178 | fn foo(a: &A) { | ||
179 | match a<|> { | ||
180 | } | ||
181 | } | ||
182 | "#, | ||
183 | r#" | ||
184 | enum A { | ||
185 | As, | ||
186 | } | ||
187 | |||
188 | fn foo(a: &A) { | ||
189 | match <|>a { | ||
190 | A::As => (), | ||
191 | } | ||
192 | } | ||
193 | "#, | ||
194 | ); | ||
195 | |||
196 | check_assist( | ||
197 | fill_match_arms, | ||
198 | r#" | ||
199 | enum A { | ||
200 | Es{ x: usize, y: usize } | ||
201 | } | ||
202 | |||
203 | fn foo(a: &mut A) { | ||
204 | match a<|> { | ||
205 | } | ||
206 | } | ||
207 | "#, | ||
208 | r#" | ||
209 | enum A { | ||
210 | Es{ x: usize, y: usize } | ||
211 | } | ||
212 | |||
213 | fn foo(a: &mut A) { | ||
214 | match <|>a { | ||
215 | A::Es { x, y } => (), | ||
216 | } | ||
217 | } | ||
218 | "#, | ||
219 | ); | ||
220 | } | ||
221 | |||
222 | #[test] | ||
223 | fn fill_match_arms_target() { | ||
224 | check_assist_target( | ||
225 | fill_match_arms, | ||
226 | r#" | ||
227 | enum E { X, Y } | ||
228 | |||
229 | fn main() { | ||
230 | match E::X<|> {} | ||
231 | } | ||
232 | "#, | ||
233 | "match E::X {}", | ||
234 | ); | ||
235 | } | ||
236 | |||
237 | #[test] | ||
238 | fn fill_match_arms_trivial_arm() { | ||
239 | check_assist( | ||
240 | fill_match_arms, | ||
241 | r#" | ||
242 | enum E { X, Y } | ||
243 | |||
244 | fn main() { | ||
245 | match E::X { | ||
246 | <|>_ => {}, | ||
247 | } | ||
248 | } | ||
249 | "#, | ||
250 | r#" | ||
251 | enum E { X, Y } | ||
252 | |||
253 | fn main() { | ||
254 | match <|>E::X { | ||
255 | E::X => (), | ||
256 | E::Y => (), | ||
257 | } | ||
258 | } | ||
259 | "#, | ||
260 | ); | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn fill_match_arms_qualifies_path() { | ||
265 | check_assist( | ||
266 | fill_match_arms, | ||
267 | r#" | ||
268 | mod foo { pub enum E { X, Y } } | ||
269 | use foo::E::X; | ||
270 | |||
271 | fn main() { | ||
272 | match X { | ||
273 | <|> | ||
274 | } | ||
275 | } | ||
276 | "#, | ||
277 | r#" | ||
278 | mod foo { pub enum E { X, Y } } | ||
279 | use foo::E::X; | ||
280 | |||
281 | fn main() { | ||
282 | match <|>X { | ||
283 | X => (), | ||
284 | foo::E::Y => (), | ||
285 | } | ||
286 | } | ||
287 | "#, | ||
288 | ); | ||
289 | } | ||
290 | } | ||