diff options
author | Benjamin Coenen <[email protected]> | 2020-05-02 11:20:39 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-05-02 11:20:39 +0100 |
commit | eea21738ab9e0b7438d03f7b2efc18c15cc30cf2 (patch) | |
tree | 374eeda78a349801ccffc2e39368cfb26a53445a | |
parent | df7899e47a83fb5544d09d2db9405762d3ce29b7 (diff) |
Add unwrap block assist #4156
Signed-off-by: Benjamin Coenen <[email protected]>
-rw-r--r-- | crates/ra_assists/src/handlers/unwrap_block.rs | 89 |
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 @@ | |||
1 | use crate::{Assist, AssistCtx, AssistId}; | 1 | use crate::{Assist, AssistCtx, AssistId}; |
2 | 2 | ||
3 | use ast::{BlockExpr, Expr, LoopBodyOwner}; | 3 | use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr}; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, AstNode, TextRange}; | 5 | use 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 | // ``` |
24 | pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(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 | ||
83 | fn excract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option<Expr> { | 79 | fn 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, |