aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-05-02 11:20:39 +0100
committerBenjamin Coenen <[email protected]>2020-05-02 11:20:39 +0100
commiteea21738ab9e0b7438d03f7b2efc18c15cc30cf2 (patch)
tree374eeda78a349801ccffc2e39368cfb26a53445a /crates/ra_assists/src
parentdf7899e47a83fb5544d09d2db9405762d3ce29b7 (diff)
Add unwrap block assist #4156
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs89
1 files changed, 13 insertions, 76 deletions
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 71d6d462b..8912ce645 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,8 +1,8 @@
1use crate::{Assist, AssistCtx, AssistId}; 1use crate::{Assist, AssistCtx, AssistId};
2 2
3use ast::{BlockExpr, Expr, LoopBodyOwner}; 3use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
4use ra_fmt::unwrap_trivial_block; 4use ra_fmt::unwrap_trivial_block;
5use ra_syntax::{ast, AstNode, TextRange}; 5use ra_syntax::{ast, AstNode, TextRange, T};
6 6
7// Assist: unwrap_block 7// Assist: unwrap_block
8// 8//
@@ -22,15 +22,11 @@ use ra_syntax::{ast, AstNode, TextRange};
22// } 22// }
23// ``` 23// ```
24pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> { 24pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
25 let res = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { 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) {
26 // if expression 28 // if expression
27 let mut expr_to_unwrap: Option<ast::Expr> = None; 29 let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
28 for block_expr in if_expr.blocks() {
29 if let Some(expr) = excract_expr(ctx.frange.range, block_expr) {
30 expr_to_unwrap = Some(expr);
31 break;
32 }
33 }
34 let expr_to_unwrap = expr_to_unwrap?; 30 let expr_to_unwrap = expr_to_unwrap?;
35 // Find if we are in a else if block 31 // Find if we are in a else if block
36 let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast); 32 let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);
@@ -40,20 +36,20 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
40 } else { 36 } else {
41 Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap)) 37 Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap))
42 } 38 }
43 } else if let Some(for_expr) = ctx.find_node_at_offset::<ast::ForExpr>() { 39 } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
44 // for expression 40 // for expression
45 let block_expr = for_expr.loop_body()?; 41 let block_expr = for_expr.loop_body()?;
46 excract_expr(ctx.frange.range, block_expr) 42 extract_expr(ctx.frange.range, block_expr)
47 .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap)) 43 .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap))
48 } else if let Some(while_expr) = ctx.find_node_at_offset::<ast::WhileExpr>() { 44 } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
49 // while expression 45 // while expression
50 let block_expr = while_expr.loop_body()?; 46 let block_expr = while_expr.loop_body()?;
51 excract_expr(ctx.frange.range, block_expr) 47 extract_expr(ctx.frange.range, block_expr)
52 .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) 48 .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap))
53 } else if let Some(loop_expr) = ctx.find_node_at_offset::<ast::LoopExpr>() { 49 } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
54 // loop expression 50 // loop expression
55 let block_expr = loop_expr.loop_body()?; 51 let block_expr = loop_expr.loop_body()?;
56 excract_expr(ctx.frange.range, block_expr) 52 extract_expr(ctx.frange.range, block_expr)
57 .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) 53 .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap))
58 } else { 54 } else {
59 None 55 None
@@ -80,7 +76,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
80 }) 76 })
81} 77}
82 78
83fn excract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option<Expr> { 79fn extract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option<Expr> {
84 let block = block_expr.block()?; 80 let block = block_expr.block()?;
85 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); 81 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
86 82
@@ -201,65 +197,6 @@ mod tests {
201 } 197 }
202 198
203 #[test] 199 #[test]
204 fn issue_example_with_if() {
205 check_assist(
206 unwrap_block,
207 r#"
208 fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
209 if let Some(ty) = &ctx.expected_type {<|>
210 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
211 let variants = enum_data.variants(ctx.db);
212
213 let module = if let Some(module) = ctx.scope().module() {
214 // Compute path from the completion site if available.
215 module
216 } else {
217 // Otherwise fall back to the enum's definition site.
218 enum_data.module(ctx.db)
219 };
220
221 for variant in variants {
222 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
223 // Variants with trivial paths are already added by the existing completion logic,
224 // so we should avoid adding these twice
225 if path.segments.len() > 1 {
226 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
227 }
228 }
229 }
230 }
231 }
232 }
233 "#,
234 r#"
235 fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
236 <|>if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
237 let variants = enum_data.variants(ctx.db);
238
239 let module = if let Some(module) = ctx.scope().module() {
240 // Compute path from the completion site if available.
241 module
242 } else {
243 // Otherwise fall back to the enum's definition site.
244 enum_data.module(ctx.db)
245 };
246
247 for variant in variants {
248 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
249 // Variants with trivial paths are already added by the existing completion logic,
250 // so we should avoid adding these twice
251 if path.segments.len() > 1 {
252 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
253 }
254 }
255 }
256 }
257 }
258 "#,
259 );
260 }
261
262 #[test]
263 fn simple_for() { 200 fn simple_for() {
264 check_assist( 201 check_assist(
265 unwrap_block, 202 unwrap_block,