aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/move_guard.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/move_guard.rs')
-rw-r--r--crates/ra_assists/src/move_guard.rs260
1 files changed, 260 insertions, 0 deletions
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 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 TextUnit,
4 SyntaxElement,
5 ast::{MatchArm, AstNode, AstToken, IfExpr},
6 ast,
7};
8
9use crate::{AssistCtx, Assist, AssistId};
10
11pub(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
42pub(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)]
84mod 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}