aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs349
-rw-r--r--crates/ra_assists/src/lib.rs2
3 files changed, 370 insertions, 0 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index d6a34b609..6696cc832 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -728,3 +728,22 @@ use std::{collections::HashMap};
728"#####, 728"#####,
729 ) 729 )
730} 730}
731
732#[test]
733fn doctest_unwrap_block() {
734 check(
735 "unwrap_block",
736 r#####"
737fn foo() {
738 if true {<|>
739 println!("foo");
740 }
741}
742"#####,
743 r#####"
744fn foo() {
745 println!("foo");
746}
747"#####,
748 )
749}
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
new file mode 100644
index 000000000..8912ce645
--- /dev/null
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -0,0 +1,349 @@
1use crate::{Assist, AssistCtx, AssistId};
2
3use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
4use ra_fmt::unwrap_trivial_block;
5use ra_syntax::{ast, AstNode, TextRange, T};
6
7// Assist: unwrap_block
8//
9// This assist removes if...else, for, while and loop control statements to just keep the body.
10//
11// ```
12// fn foo() {
13// if true {<|>
14// println!("foo");
15// }
16// }
17// ```
18// ->
19// ```
20// fn foo() {
21// println!("foo");
22// }
23// ```
24pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
25 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
26
27 let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) {
28 // if expression
29 let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
30 let expr_to_unwrap = expr_to_unwrap?;
31 // Find if we are in a else if block
32 let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);
33
34 if let Some(ancestor) = ancestor {
35 Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap))
36 } else {
37 Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap))
38 }
39 } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
40 // for expression
41 let block_expr = for_expr.loop_body()?;
42 extract_expr(ctx.frange.range, block_expr)
43 .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap))
44 } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
45 // while expression
46 let block_expr = while_expr.loop_body()?;
47 extract_expr(ctx.frange.range, block_expr)
48 .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap))
49 } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
50 // loop expression
51 let block_expr = loop_expr.loop_body()?;
52 extract_expr(ctx.frange.range, block_expr)
53 .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap))
54 } else {
55 None
56 };
57
58 let (expr, expr_to_unwrap) = res?;
59 ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
60 edit.set_cursor(expr.syntax().text_range().start());
61 edit.target(expr_to_unwrap.syntax().text_range());
62
63 let pat_start: &[_] = &[' ', '{', '\n'];
64 let expr_to_unwrap = expr_to_unwrap.to_string();
65 let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
66 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
67 expr_string_lines.pop(); // Delete last line
68
69 let expr_string = expr_string_lines
70 .into_iter()
71 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
72 .collect::<Vec<String>>()
73 .join("\n");
74
75 edit.replace(expr.syntax().text_range(), expr_string);
76 })
77}
78
79fn extract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option<Expr> {
80 let block = block_expr.block()?;
81 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
82
83 if cursor_in_range {
84 Some(unwrap_trivial_block(block_expr))
85 } else {
86 None
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::helpers::{check_assist, check_assist_not_applicable};
93
94 use super::*;
95
96 #[test]
97 fn simple_if() {
98 check_assist(
99 unwrap_block,
100 r#"
101 fn main() {
102 bar();
103 if true {<|>
104 foo();
105
106 //comment
107 bar();
108 } else {
109 println!("bar");
110 }
111 }
112 "#,
113 r#"
114 fn main() {
115 bar();
116 <|>foo();
117
118 //comment
119 bar();
120 }
121 "#,
122 );
123 }
124
125 #[test]
126 fn simple_if_else() {
127 check_assist(
128 unwrap_block,
129 r#"
130 fn main() {
131 bar();
132 if true {
133 foo();
134
135 //comment
136 bar();
137 } else {<|>
138 println!("bar");
139 }
140 }
141 "#,
142 r#"
143 fn main() {
144 bar();
145 <|>println!("bar");
146 }
147 "#,
148 );
149 }
150
151 #[test]
152 fn simple_if_else_if() {
153 check_assist(
154 unwrap_block,
155 r#"
156 fn main() {
157 //bar();
158 if true {
159 println!("true");
160
161 //comment
162 //bar();
163 } else if false {<|>
164 println!("bar");
165 } else {
166 println!("foo");
167 }
168 }
169 "#,
170 r#"
171 fn main() {
172 //bar();
173 <|>println!("bar");
174 }
175 "#,
176 );
177 }
178
179 #[test]
180 fn simple_if_bad_cursor_position() {
181 check_assist_not_applicable(
182 unwrap_block,
183 r#"
184 fn main() {
185 bar();<|>
186 if true {
187 foo();
188
189 //comment
190 bar();
191 } else {
192 println!("bar");
193 }
194 }
195 "#,
196 );
197 }
198
199 #[test]
200 fn simple_for() {
201 check_assist(
202 unwrap_block,
203 r#"
204 fn main() {
205 for i in 0..5 {<|>
206 if true {
207 foo();
208
209 //comment
210 bar();
211 } else {
212 println!("bar");
213 }
214 }
215 }
216 "#,
217 r#"
218 fn main() {
219 <|>if true {
220 foo();
221
222 //comment
223 bar();
224 } else {
225 println!("bar");
226 }
227 }
228 "#,
229 );
230 }
231
232 #[test]
233 fn simple_if_in_for() {
234 check_assist(
235 unwrap_block,
236 r#"
237 fn main() {
238 for i in 0..5 {
239 if true {<|>
240 foo();
241
242 //comment
243 bar();
244 } else {
245 println!("bar");
246 }
247 }
248 }
249 "#,
250 r#"
251 fn main() {
252 for i in 0..5 {
253 <|>foo();
254
255 //comment
256 bar();
257 }
258 }
259 "#,
260 );
261 }
262
263 #[test]
264 fn simple_loop() {
265 check_assist(
266 unwrap_block,
267 r#"
268 fn main() {
269 loop {<|>
270 if true {
271 foo();
272
273 //comment
274 bar();
275 } else {
276 println!("bar");
277 }
278 }
279 }
280 "#,
281 r#"
282 fn main() {
283 <|>if true {
284 foo();
285
286 //comment
287 bar();
288 } else {
289 println!("bar");
290 }
291 }
292 "#,
293 );
294 }
295
296 #[test]
297 fn simple_while() {
298 check_assist(
299 unwrap_block,
300 r#"
301 fn main() {
302 while true {<|>
303 if true {
304 foo();
305
306 //comment
307 bar();
308 } else {
309 println!("bar");
310 }
311 }
312 }
313 "#,
314 r#"
315 fn main() {
316 <|>if true {
317 foo();
318
319 //comment
320 bar();
321 } else {
322 println!("bar");
323 }
324 }
325 "#,
326 );
327 }
328
329 #[test]
330 fn simple_if_in_while_bad_cursor_position() {
331 check_assist_not_applicable(
332 unwrap_block,
333 r#"
334 fn main() {
335 while true {
336 if true {
337 foo();<|>
338
339 //comment
340 bar();
341 } else {
342 println!("bar");
343 }
344 }
345 }
346 "#,
347 );
348 }
349}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 64bd87afb..c5df86600 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -143,6 +143,7 @@ mod handlers {
143 mod split_import; 143 mod split_import;
144 mod add_from_impl_for_enum; 144 mod add_from_impl_for_enum;
145 mod reorder_fields; 145 mod reorder_fields;
146 mod unwrap_block;
146 147
147 pub(crate) fn all() -> &'static [AssistHandler] { 148 pub(crate) fn all() -> &'static [AssistHandler] {
148 &[ 149 &[
@@ -181,6 +182,7 @@ mod handlers {
181 replace_unwrap_with_match::replace_unwrap_with_match, 182 replace_unwrap_with_match::replace_unwrap_with_match,
182 split_import::split_import, 183 split_import::split_import,
183 add_from_impl_for_enum::add_from_impl_for_enum, 184 add_from_impl_for_enum::add_from_impl_for_enum,
185 unwrap_block::unwrap_block,
184 // These are manually sorted for better priorities 186 // These are manually sorted for better priorities
185 add_missing_impl_members::add_missing_impl_members, 187 add_missing_impl_members::add_missing_impl_members,
186 add_missing_impl_members::add_missing_default_members, 188 add_missing_impl_members::add_missing_default_members,