diff options
-rw-r--r-- | crates/ra_assists/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_assists/src/move_guard.rs | 260 | ||||
-rw-r--r-- | crates/ra_assists/src/move_guard_to_arm_body.rs | 115 | ||||
-rw-r--r-- | docs/user/features.md | 33 |
4 files changed, 294 insertions, 119 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index ae97a1ab5..28eb0226b 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -100,7 +100,7 @@ mod split_import; | |||
100 | mod remove_dbg; | 100 | mod remove_dbg; |
101 | pub mod auto_import; | 101 | pub mod auto_import; |
102 | mod add_missing_impl_members; | 102 | mod add_missing_impl_members; |
103 | mod move_guard_to_arm_body; | 103 | mod move_guard; |
104 | 104 | ||
105 | fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { | 105 | fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { |
106 | &[ | 106 | &[ |
@@ -119,7 +119,8 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis | |||
119 | add_missing_impl_members::add_missing_impl_members, | 119 | add_missing_impl_members::add_missing_impl_members, |
120 | add_missing_impl_members::add_missing_default_members, | 120 | add_missing_impl_members::add_missing_default_members, |
121 | inline_local_variable::inline_local_varialbe, | 121 | inline_local_variable::inline_local_varialbe, |
122 | move_guard_to_arm_body::move_guard_to_arm_body, | 122 | move_guard::move_guard_to_arm_body, |
123 | move_guard::move_arm_cond_to_match_guard, | ||
123 | ] | 124 | ] |
124 | } | 125 | } |
125 | 126 | ||
diff --git a/crates/ra_assists/src/move_guard.rs b/crates/ra_assists/src/move_guard.rs new file mode 100644 index 000000000..22ba91fb7 --- /dev/null +++ b/crates/ra_assists/src/move_guard.rs | |||
@@ -0,0 +1,260 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::{ | ||
3 | TextUnit, | ||
4 | SyntaxElement, | ||
5 | ast::{MatchArm, AstNode, AstToken, IfExpr}, | ||
6 | ast, | ||
7 | }; | ||
8 | |||
9 | use crate::{AssistCtx, Assist, AssistId}; | ||
10 | |||
11 | pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
12 | let match_arm = ctx.node_at_offset::<MatchArm>()?; | ||
13 | let guard = match_arm.guard()?; | ||
14 | let space_before_guard = guard.syntax().prev_sibling_or_token(); | ||
15 | |||
16 | let guard_conditions = guard.expr()?; | ||
17 | let arm_expr = match_arm.expr()?; | ||
18 | let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); | ||
19 | |||
20 | ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| { | ||
21 | edit.target(guard.syntax().range()); | ||
22 | let offseting_amount = match space_before_guard { | ||
23 | Some(SyntaxElement::Token(tok)) => { | ||
24 | if let Some(_) = ast::Whitespace::cast(tok) { | ||
25 | let ele = space_before_guard.unwrap().range(); | ||
26 | edit.delete(ele); | ||
27 | ele.len() | ||
28 | } else { | ||
29 | TextUnit::from(0) | ||
30 | } | ||
31 | } | ||
32 | _ => TextUnit::from(0), | ||
33 | }; | ||
34 | |||
35 | edit.delete(guard.syntax().range()); | ||
36 | edit.replace_node_and_indent(arm_expr.syntax(), buf); | ||
37 | edit.set_cursor(arm_expr.syntax().range().start() + TextUnit::from(3) - offseting_amount); | ||
38 | }); | ||
39 | ctx.build() | ||
40 | } | ||
41 | |||
42 | pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
43 | let match_arm: &MatchArm = ctx.node_at_offset::<MatchArm>()?; | ||
44 | let last_match_pat = match_arm.pats().last()?; | ||
45 | |||
46 | let arm_body = match_arm.expr()?; | ||
47 | let if_expr: &IfExpr = IfExpr::cast(arm_body.syntax())?; | ||
48 | let cond = if_expr.condition()?; | ||
49 | let then_block = if_expr.then_branch()?; | ||
50 | |||
51 | // Not support if with else branch | ||
52 | if let Some(_) = if_expr.else_branch() { | ||
53 | return None; | ||
54 | } | ||
55 | // Not support moving if let to arm guard | ||
56 | if let Some(_) = cond.pat() { | ||
57 | return None; | ||
58 | } | ||
59 | |||
60 | let buf = format!(" if {}", cond.syntax().text()); | ||
61 | |||
62 | ctx.add_action( | ||
63 | AssistId("move_arm_cond_to_match_guard"), | ||
64 | "move condition to match guard", | ||
65 | |edit| { | ||
66 | edit.target(if_expr.syntax().range()); | ||
67 | let then_only_expr = then_block.statements().next().is_none(); | ||
68 | |||
69 | match then_block.expr() { | ||
70 | Some(then_expr) if then_only_expr => { | ||
71 | edit.replace(if_expr.syntax().range(), then_expr.syntax().text()) | ||
72 | } | ||
73 | _ => edit.replace(if_expr.syntax().range(), then_block.syntax().text()), | ||
74 | } | ||
75 | |||
76 | edit.insert(last_match_pat.syntax().range().end(), buf); | ||
77 | edit.set_cursor(last_match_pat.syntax().range().end() + TextUnit::from(1)); | ||
78 | }, | ||
79 | ); | ||
80 | ctx.build() | ||
81 | } | ||
82 | |||
83 | #[cfg(test)] | ||
84 | mod tests { | ||
85 | use super::*; | ||
86 | |||
87 | use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable }; | ||
88 | |||
89 | #[test] | ||
90 | fn move_guard_to_arm_body_target() { | ||
91 | check_assist_target( | ||
92 | move_guard_to_arm_body, | ||
93 | r#" | ||
94 | fn f() { | ||
95 | let t = 'a'; | ||
96 | let chars = "abcd"; | ||
97 | match t { | ||
98 | '\r' <|>if chars.clone().next() == Some('\n') => false, | ||
99 | _ => true | ||
100 | } | ||
101 | } | ||
102 | "#, | ||
103 | r#"if chars.clone().next() == Some('\n')"#, | ||
104 | ); | ||
105 | } | ||
106 | |||
107 | #[test] | ||
108 | fn move_guard_to_arm_body_works() { | ||
109 | check_assist( | ||
110 | move_guard_to_arm_body, | ||
111 | r#" | ||
112 | fn f() { | ||
113 | let t = 'a'; | ||
114 | let chars = "abcd"; | ||
115 | match t { | ||
116 | '\r' <|>if chars.clone().next() == Some('\n') => false, | ||
117 | _ => true | ||
118 | } | ||
119 | } | ||
120 | "#, | ||
121 | r#" | ||
122 | fn f() { | ||
123 | let t = 'a'; | ||
124 | let chars = "abcd"; | ||
125 | match t { | ||
126 | '\r' => if chars.clone().next() == Some('\n') { <|>false }, | ||
127 | _ => true | ||
128 | } | ||
129 | } | ||
130 | "#, | ||
131 | ); | ||
132 | } | ||
133 | |||
134 | #[test] | ||
135 | fn move_guard_to_arm_body_works_complex_match() { | ||
136 | check_assist( | ||
137 | move_guard_to_arm_body, | ||
138 | r#" | ||
139 | fn f() { | ||
140 | match x { | ||
141 | <|>y @ 4 | y @ 5 if y > 5 => true, | ||
142 | _ => false | ||
143 | } | ||
144 | } | ||
145 | "#, | ||
146 | r#" | ||
147 | fn f() { | ||
148 | match x { | ||
149 | y @ 4 | y @ 5 => if y > 5 { <|>true }, | ||
150 | _ => false | ||
151 | } | ||
152 | } | ||
153 | "#, | ||
154 | ); | ||
155 | } | ||
156 | |||
157 | #[test] | ||
158 | fn move_arm_cond_to_match_guard_works() { | ||
159 | check_assist( | ||
160 | move_arm_cond_to_match_guard, | ||
161 | r#" | ||
162 | fn f() { | ||
163 | let t = 'a'; | ||
164 | let chars = "abcd"; | ||
165 | match t { | ||
166 | '\r' => if chars.clone().next() == Some('\n') { <|>false }, | ||
167 | _ => true | ||
168 | } | ||
169 | } | ||
170 | "#, | ||
171 | r#" | ||
172 | fn f() { | ||
173 | let t = 'a'; | ||
174 | let chars = "abcd"; | ||
175 | match t { | ||
176 | '\r' <|>if chars.clone().next() == Some('\n') => false, | ||
177 | _ => true | ||
178 | } | ||
179 | } | ||
180 | "#, | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn move_arm_cond_to_match_guard_if_let_not_works() { | ||
186 | check_assist_not_applicable( | ||
187 | move_arm_cond_to_match_guard, | ||
188 | r#" | ||
189 | fn f() { | ||
190 | let t = 'a'; | ||
191 | let chars = "abcd"; | ||
192 | match t { | ||
193 | '\r' => if let Some(_) = chars.clone().next() { <|>false }, | ||
194 | _ => true | ||
195 | } | ||
196 | } | ||
197 | "#, | ||
198 | ); | ||
199 | } | ||
200 | |||
201 | #[test] | ||
202 | fn move_arm_cond_to_match_guard_if_empty_body_works() { | ||
203 | check_assist( | ||
204 | move_arm_cond_to_match_guard, | ||
205 | r#" | ||
206 | fn f() { | ||
207 | let t = 'a'; | ||
208 | let chars = "abcd"; | ||
209 | match t { | ||
210 | '\r' => if chars.clone().next().is_some() { <|> }, | ||
211 | _ => true | ||
212 | } | ||
213 | } | ||
214 | "#, | ||
215 | r#" | ||
216 | fn f() { | ||
217 | let t = 'a'; | ||
218 | let chars = "abcd"; | ||
219 | match t { | ||
220 | '\r' <|>if chars.clone().next().is_some() => { }, | ||
221 | _ => true | ||
222 | } | ||
223 | } | ||
224 | "#, | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn move_arm_cond_to_match_guard_if_multiline_body_works() { | ||
230 | check_assist( | ||
231 | move_arm_cond_to_match_guard, | ||
232 | r#" | ||
233 | fn f() { | ||
234 | let mut t = 'a'; | ||
235 | let chars = "abcd"; | ||
236 | match t { | ||
237 | '\r' => if chars.clone().next().is_some() { | ||
238 | t = 'e';<|> | ||
239 | false | ||
240 | }, | ||
241 | _ => true | ||
242 | } | ||
243 | } | ||
244 | "#, | ||
245 | r#" | ||
246 | fn f() { | ||
247 | let mut t = 'a'; | ||
248 | let chars = "abcd"; | ||
249 | match t { | ||
250 | '\r' <|>if chars.clone().next().is_some() => { | ||
251 | t = 'e'; | ||
252 | false | ||
253 | }, | ||
254 | _ => true | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | ); | ||
259 | } | ||
260 | } | ||
diff --git a/crates/ra_assists/src/move_guard_to_arm_body.rs b/crates/ra_assists/src/move_guard_to_arm_body.rs deleted file mode 100644 index a8ca19f5d..000000000 --- a/crates/ra_assists/src/move_guard_to_arm_body.rs +++ /dev/null | |||
@@ -1,115 +0,0 @@ | |||
1 | use hir::db::HirDatabase; | ||
2 | use ra_syntax::{ | ||
3 | TextUnit, | ||
4 | SyntaxElement, | ||
5 | ast::{MatchArm, AstNode, AstToken}, | ||
6 | ast, | ||
7 | }; | ||
8 | |||
9 | use crate::{AssistCtx, Assist, AssistId}; | ||
10 | |||
11 | pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
12 | let match_arm = ctx.node_at_offset::<MatchArm>()?; | ||
13 | let guard = match_arm.guard()?; | ||
14 | let space_before_guard = guard.syntax().prev_sibling_or_token(); | ||
15 | |||
16 | let guard_conditions = guard.expr()?; | ||
17 | let arm_expr = match_arm.expr()?; | ||
18 | let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); | ||
19 | |||
20 | ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| { | ||
21 | edit.target(guard.syntax().range()); | ||
22 | let offseting_amount = match space_before_guard { | ||
23 | Some(SyntaxElement::Token(tok)) => { | ||
24 | if let Some(_) = ast::Whitespace::cast(tok) { | ||
25 | let ele = space_before_guard.unwrap().range(); | ||
26 | edit.delete(ele); | ||
27 | ele.len() | ||
28 | } else { | ||
29 | TextUnit::from(0) | ||
30 | } | ||
31 | } | ||
32 | _ => TextUnit::from(0), | ||
33 | }; | ||
34 | |||
35 | edit.delete(guard.syntax().range()); | ||
36 | edit.replace_node_and_indent(arm_expr.syntax(), buf); | ||
37 | edit.set_cursor(arm_expr.syntax().range().start() + TextUnit::from(3) - offseting_amount); | ||
38 | }); | ||
39 | ctx.build() | ||
40 | } | ||
41 | |||
42 | #[cfg(test)] | ||
43 | mod tests { | ||
44 | use super::*; | ||
45 | |||
46 | use crate::helpers::{ check_assist, check_assist_target }; | ||
47 | |||
48 | #[test] | ||
49 | fn move_guard_to_arm_body_target() { | ||
50 | check_assist_target( | ||
51 | move_guard_to_arm_body, | ||
52 | r#" | ||
53 | fn f() { | ||
54 | let t = 'a'; | ||
55 | let chars = "abcd"; | ||
56 | match t { | ||
57 | '\r' <|>if chars.clone().next() == Some('\n') => false, | ||
58 | _ => true | ||
59 | } | ||
60 | } | ||
61 | "#, | ||
62 | r#"if chars.clone().next() == Some('\n')"#, | ||
63 | ); | ||
64 | } | ||
65 | |||
66 | #[test] | ||
67 | fn move_guard_to_arm_body_works() { | ||
68 | check_assist( | ||
69 | move_guard_to_arm_body, | ||
70 | r#" | ||
71 | fn f() { | ||
72 | let t = 'a'; | ||
73 | let chars = "abcd"; | ||
74 | match t { | ||
75 | '\r' <|>if chars.clone().next() == Some('\n') => false, | ||
76 | _ => true | ||
77 | } | ||
78 | } | ||
79 | "#, | ||
80 | r#" | ||
81 | fn f() { | ||
82 | let t = 'a'; | ||
83 | let chars = "abcd"; | ||
84 | match t { | ||
85 | '\r' => if chars.clone().next() == Some('\n') { <|>false }, | ||
86 | _ => true | ||
87 | } | ||
88 | } | ||
89 | "#, | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn move_guard_to_arm_body_works_complex_match() { | ||
95 | check_assist( | ||
96 | move_guard_to_arm_body, | ||
97 | r#" | ||
98 | fn f() { | ||
99 | match x { | ||
100 | <|>y @ 4 | y @ 5 if y > 5 => true, | ||
101 | _ => false | ||
102 | } | ||
103 | } | ||
104 | "#, | ||
105 | r#" | ||
106 | fn f() { | ||
107 | match x { | ||
108 | y @ 4 | y @ 5 => if y > 5 { <|>true }, | ||
109 | _ => false | ||
110 | } | ||
111 | } | ||
112 | "#, | ||
113 | ); | ||
114 | } | ||
115 | } | ||
diff --git a/docs/user/features.md b/docs/user/features.md index a714574fb..22470bc56 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -390,14 +390,14 @@ fn foo() { | |||
390 | 390 | ||
391 | - Move guard expression to match arm body | 391 | - Move guard expression to match arm body |
392 | ```rust | 392 | ```rust |
393 | //before: | 393 | // before: |
394 | fn f() { | 394 | fn f() { |
395 | match x { | 395 | match x { |
396 | <|>y @ 4 | y @ 5 if y > 5 => true, | 396 | <|>y @ 4 | y @ 5 if y > 5 => true, |
397 | _ => false | 397 | _ => false |
398 | } | 398 | } |
399 | } | 399 | } |
400 | //after: | 400 | // after: |
401 | fn f() { | 401 | fn f() { |
402 | match x { | 402 | match x { |
403 | y @ 4 | y @ 5 => if y > 5 { <|>true }, | 403 | y @ 4 | y @ 5 => if y > 5 { <|>true }, |
@@ -406,6 +406,35 @@ fn f() { | |||
406 | } | 406 | } |
407 | ``` | 407 | ``` |
408 | 408 | ||
409 | - Move if condition to match arm guard | ||
410 | ```rust | ||
411 | // before: | ||
412 | fn f() { | ||
413 | let mut t = 'a'; | ||
414 | let chars = "abcd"; | ||
415 | match t { | ||
416 | '\r' => if chars.clone().next().is_some() { | ||
417 | t = 'e';<|> | ||
418 | false | ||
419 | }, | ||
420 | _ => true | ||
421 | } | ||
422 | } | ||
423 | |||
424 | // after: | ||
425 | fn f() { | ||
426 | let mut t = 'a'; | ||
427 | let chars = "abcd"; | ||
428 | match t { | ||
429 | '\r' <|>if chars.clone().next().is_some() => { | ||
430 | t = 'e'; | ||
431 | false | ||
432 | }, | ||
433 | _ => true | ||
434 | } | ||
435 | } | ||
436 | ``` | ||
437 | |||
409 | ### Magic Completions | 438 | ### Magic Completions |
410 | 439 | ||
411 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | 440 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ |