aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs348
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs7
-rw-r--r--crates/rust-analyzer/src/main_loop.rs3
-rw-r--r--docs/user/assists.md18
-rw-r--r--editors/code/package.json5
8 files changed, 390 insertions, 14 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..58649c47e
--- /dev/null
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -0,0 +1,348 @@
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: BlockExpr) -> Option<Expr> {
80 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
81
82 if cursor_in_range {
83 Some(unwrap_trivial_block(block))
84 } else {
85 None
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use crate::helpers::{check_assist, check_assist_not_applicable};
92
93 use super::*;
94
95 #[test]
96 fn simple_if() {
97 check_assist(
98 unwrap_block,
99 r#"
100 fn main() {
101 bar();
102 if true {<|>
103 foo();
104
105 //comment
106 bar();
107 } else {
108 println!("bar");
109 }
110 }
111 "#,
112 r#"
113 fn main() {
114 bar();
115 <|>foo();
116
117 //comment
118 bar();
119 }
120 "#,
121 );
122 }
123
124 #[test]
125 fn simple_if_else() {
126 check_assist(
127 unwrap_block,
128 r#"
129 fn main() {
130 bar();
131 if true {
132 foo();
133
134 //comment
135 bar();
136 } else {<|>
137 println!("bar");
138 }
139 }
140 "#,
141 r#"
142 fn main() {
143 bar();
144 <|>println!("bar");
145 }
146 "#,
147 );
148 }
149
150 #[test]
151 fn simple_if_else_if() {
152 check_assist(
153 unwrap_block,
154 r#"
155 fn main() {
156 //bar();
157 if true {
158 println!("true");
159
160 //comment
161 //bar();
162 } else if false {<|>
163 println!("bar");
164 } else {
165 println!("foo");
166 }
167 }
168 "#,
169 r#"
170 fn main() {
171 //bar();
172 <|>println!("bar");
173 }
174 "#,
175 );
176 }
177
178 #[test]
179 fn simple_if_bad_cursor_position() {
180 check_assist_not_applicable(
181 unwrap_block,
182 r#"
183 fn main() {
184 bar();<|>
185 if true {
186 foo();
187
188 //comment
189 bar();
190 } else {
191 println!("bar");
192 }
193 }
194 "#,
195 );
196 }
197
198 #[test]
199 fn simple_for() {
200 check_assist(
201 unwrap_block,
202 r#"
203 fn main() {
204 for i in 0..5 {<|>
205 if true {
206 foo();
207
208 //comment
209 bar();
210 } else {
211 println!("bar");
212 }
213 }
214 }
215 "#,
216 r#"
217 fn main() {
218 <|>if true {
219 foo();
220
221 //comment
222 bar();
223 } else {
224 println!("bar");
225 }
226 }
227 "#,
228 );
229 }
230
231 #[test]
232 fn simple_if_in_for() {
233 check_assist(
234 unwrap_block,
235 r#"
236 fn main() {
237 for i in 0..5 {
238 if true {<|>
239 foo();
240
241 //comment
242 bar();
243 } else {
244 println!("bar");
245 }
246 }
247 }
248 "#,
249 r#"
250 fn main() {
251 for i in 0..5 {
252 <|>foo();
253
254 //comment
255 bar();
256 }
257 }
258 "#,
259 );
260 }
261
262 #[test]
263 fn simple_loop() {
264 check_assist(
265 unwrap_block,
266 r#"
267 fn main() {
268 loop {<|>
269 if true {
270 foo();
271
272 //comment
273 bar();
274 } else {
275 println!("bar");
276 }
277 }
278 }
279 "#,
280 r#"
281 fn main() {
282 <|>if true {
283 foo();
284
285 //comment
286 bar();
287 } else {
288 println!("bar");
289 }
290 }
291 "#,
292 );
293 }
294
295 #[test]
296 fn simple_while() {
297 check_assist(
298 unwrap_block,
299 r#"
300 fn main() {
301 while true {<|>
302 if true {
303 foo();
304
305 //comment
306 bar();
307 } else {
308 println!("bar");
309 }
310 }
311 }
312 "#,
313 r#"
314 fn main() {
315 <|>if true {
316 foo();
317
318 //comment
319 bar();
320 } else {
321 println!("bar");
322 }
323 }
324 "#,
325 );
326 }
327
328 #[test]
329 fn simple_if_in_while_bad_cursor_position() {
330 check_assist_not_applicable(
331 unwrap_block,
332 r#"
333 fn main() {
334 while true {
335 if true {
336 foo();<|>
337
338 //comment
339 bar();
340 } else {
341 println!("bar");
342 }
343 }
344 }
345 "#,
346 );
347 }
348}
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,
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs
index 7ee36e60c..7771d6759 100644
--- a/crates/ra_syntax/src/ast/expr_extensions.rs
+++ b/crates/ra_syntax/src/ast/expr_extensions.rs
@@ -43,7 +43,7 @@ impl ast::IfExpr {
43 Some(res) 43 Some(res)
44 } 44 }
45 45
46 fn blocks(&self) -> AstChildren<ast::BlockExpr> { 46 pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
47 support::children(self.syntax()) 47 support::children(self.syntax())
48 } 48 }
49} 49}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 177da94cc..15b7c6912 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -49,7 +49,6 @@ pub enum FilesWatcher {
49 49
50#[derive(Debug, Clone)] 50#[derive(Debug, Clone)]
51pub struct NotificationsConfig { 51pub struct NotificationsConfig {
52 pub workspace_loaded: bool,
53 pub cargo_toml_not_found: bool, 52 pub cargo_toml_not_found: bool,
54} 53}
55 54
@@ -83,10 +82,7 @@ impl Default for Config {
83 lru_capacity: None, 82 lru_capacity: None,
84 proc_macro_srv: None, 83 proc_macro_srv: None,
85 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() }, 84 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
86 notifications: NotificationsConfig { 85 notifications: NotificationsConfig { cargo_toml_not_found: true },
87 workspace_loaded: true,
88 cargo_toml_not_found: true,
89 },
90 86
91 cargo: CargoConfig::default(), 87 cargo: CargoConfig::default(),
92 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, 88 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
@@ -129,7 +125,6 @@ impl Config {
129 Some("client") => FilesWatcher::Client, 125 Some("client") => FilesWatcher::Client,
130 Some("notify") | _ => FilesWatcher::Notify 126 Some("notify") | _ => FilesWatcher::Notify
131 }; 127 };
132 set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
133 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found); 128 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
134 129
135 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features); 130 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 0a0e616c9..3bc2e0a46 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -415,8 +415,7 @@ fn loop_turn(
415 }); 415 });
416 } 416 }
417 417
418 let show_progress = 418 let show_progress = !loop_state.workspace_loaded;
419 !loop_state.workspace_loaded && world_state.config.notifications.workspace_loaded;
420 419
421 if !loop_state.workspace_loaded 420 if !loop_state.workspace_loaded
422 && loop_state.roots_scanned == loop_state.roots_total 421 && loop_state.roots_scanned == loop_state.roots_total
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 5a83c4a98..ee515949e 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -697,3 +697,21 @@ use std::┃collections::HashMap;
697// AFTER 697// AFTER
698use std::{collections::HashMap}; 698use std::{collections::HashMap};
699``` 699```
700
701## `unwrap_block`
702
703This assist removes if...else, for, while and loop control statements to just keep the body.
704
705```rust
706// BEFORE
707fn foo() {
708 if true {┃
709 println!("foo");
710 }
711}
712
713// AFTER
714fn foo() {
715 println!("foo");
716}
717```
diff --git a/editors/code/package.json b/editors/code/package.json
index d30673791..7ef727b9d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -205,11 +205,6 @@
205 "default": [], 205 "default": [],
206 "description": "Paths to exclude from analysis." 206 "description": "Paths to exclude from analysis."
207 }, 207 },
208 "rust-analyzer.notifications.workspaceLoaded": {
209 "type": "boolean",
210 "default": true,
211 "markdownDescription": "Whether to show `workspace loaded` message."
212 },
213 "rust-analyzer.notifications.cargoTomlNotFound": { 208 "rust-analyzer.notifications.cargoTomlNotFound": {
214 "type": "boolean", 209 "type": "boolean",
215 "default": true, 210 "default": true,