From 76733f0cd456005295e60da8c45d74c8c48f177c Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 29 Apr 2020 13:52:55 +0200 Subject: Add unwrap block assist #4156 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 435 +++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 crates/ra_assists/src/handlers/unwrap_block.rs (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') 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..b98601f1c --- /dev/null +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -0,0 +1,435 @@ +use crate::{Assist, AssistCtx, AssistId}; + +use ast::LoopBodyOwner; +use ra_fmt::unwrap_trivial_block; +use ra_syntax::{ast, AstNode}; + +// Assist: unwrap_block +// +// Removes the `mut` keyword. +// +// ``` +// fn foo() { +// if true {<|> +// println!("foo"); +// } +// } +// ``` +// -> +// ``` +// fn foo() { +// <|>println!("foo"); +// } +// ``` +pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { + let res = if let Some(if_expr) = ctx.find_node_at_offset::() { + // if expression + let mut expr_to_unwrap: Option = None; + for block_expr in if_expr.blocks() { + if let Some(block) = block_expr.block() { + let cursor_in_range = + block.l_curly_token()?.text_range().contains_range(ctx.frange.range); + + if cursor_in_range { + let exprto = unwrap_trivial_block(block_expr); + expr_to_unwrap = Some(exprto); + break; + } + } + } + let expr_to_unwrap = expr_to_unwrap?; + // Find if we are in a else if block + let ancestor = ctx + .sema + .ancestors_with_macros(if_expr.syntax().clone()) + .skip(1) + .find_map(ast::IfExpr::cast); + + if let Some(ancestor) = ancestor { + Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap)) + } else { + Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap)) + } + } else if let Some(for_expr) = ctx.find_node_at_offset::() { + // for expression + let block_expr = for_expr.loop_body()?; + let block = block_expr.block()?; + let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); + + if cursor_in_range { + let expr_to_unwrap = unwrap_trivial_block(block_expr); + + Some((ast::Expr::ForExpr(for_expr), expr_to_unwrap)) + } else { + None + } + } else if let Some(while_expr) = ctx.find_node_at_offset::() { + // while expression + let block_expr = while_expr.loop_body()?; + let block = block_expr.block()?; + let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); + + if cursor_in_range { + let expr_to_unwrap = unwrap_trivial_block(block_expr); + + Some((ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) + } else { + None + } + } else if let Some(loop_expr) = ctx.find_node_at_offset::() { + // loop expression + let block_expr = loop_expr.loop_body()?; + let block = block_expr.block()?; + let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); + + if cursor_in_range { + let expr_to_unwrap = unwrap_trivial_block(block_expr); + + Some((ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) + } else { + None + } + } else { + None + }; + + let (expr, expr_to_unwrap) = res?; + ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { + edit.set_cursor(expr.syntax().text_range().start()); + edit.target(expr_to_unwrap.syntax().text_range()); + + let pat_start: &[_] = &[' ', '{', '\n']; + let expr_to_unwrap = expr_to_unwrap.to_string(); + let expr_string = expr_to_unwrap.trim_start_matches(pat_start); + let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); + expr_string_lines.pop(); // Delete last line + + let expr_string = expr_string_lines + .into_iter() + .map(|line| line.replacen(" ", "", 1)) // Delete indentation + .collect::>() + .join("\n"); + + edit.replace(expr.syntax().text_range(), expr_string); + }) +} + +#[cfg(test)] +mod tests { + use crate::helpers::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn simple_if() { + check_assist( + unwrap_block, + r#" + fn main() { + bar(); + if true {<|> + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + "#, + r#" + fn main() { + bar(); + <|>foo(); + + //comment + bar(); + } + "#, + ); + } + + #[test] + fn simple_if_else() { + check_assist( + unwrap_block, + r#" + fn main() { + bar(); + if true { + foo(); + + //comment + bar(); + } else {<|> + println!("bar"); + } + } + "#, + r#" + fn main() { + bar(); + <|>println!("bar"); + } + "#, + ); + } + + #[test] + fn simple_if_else_if() { + check_assist( + unwrap_block, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false {<|> + println!("bar"); + } else { + println!("foo"); + } + } + "#, + r#" + fn main() { + //bar(); + <|>println!("bar"); + } + "#, + ); + } + + #[test] + fn simple_if_bad_cursor_position() { + check_assist_not_applicable( + unwrap_block, + r#" + fn main() { + bar();<|> + if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + "#, + ); + } + + #[test] + fn issue_example_with_if() { + check_assist( + unwrap_block, + r#" + fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { + if let Some(ty) = &ctx.expected_type {<|> + if let Some(Adt::Enum(enum_data)) = ty.as_adt() { + let variants = enum_data.variants(ctx.db); + + let module = if let Some(module) = ctx.scope().module() { + // Compute path from the completion site if available. + module + } else { + // Otherwise fall back to the enum's definition site. + enum_data.module(ctx.db) + }; + + for variant in variants { + if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { + // Variants with trivial paths are already added by the existing completion logic, + // so we should avoid adding these twice + if path.segments.len() > 1 { + acc.add_enum_variant(ctx, variant, Some(path.to_string())); + } + } + } + } + } + } + "#, + r#" + fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { + <|>if let Some(Adt::Enum(enum_data)) = ty.as_adt() { + let variants = enum_data.variants(ctx.db); + + let module = if let Some(module) = ctx.scope().module() { + // Compute path from the completion site if available. + module + } else { + // Otherwise fall back to the enum's definition site. + enum_data.module(ctx.db) + }; + + for variant in variants { + if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { + // Variants with trivial paths are already added by the existing completion logic, + // so we should avoid adding these twice + if path.segments.len() > 1 { + acc.add_enum_variant(ctx, variant, Some(path.to_string())); + } + } + } + } + } + "#, + ); + } + + #[test] + fn simple_for() { + check_assist( + unwrap_block, + r#" + fn main() { + for i in 0..5 {<|> + if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + } + "#, + r#" + fn main() { + <|>if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + "#, + ); + } + + #[test] + fn simple_if_in_for() { + check_assist( + unwrap_block, + r#" + fn main() { + for i in 0..5 { + if true {<|> + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + } + "#, + r#" + fn main() { + for i in 0..5 { + <|>foo(); + + //comment + bar(); + } + } + "#, + ); + } + + #[test] + fn simple_loop() { + check_assist( + unwrap_block, + r#" + fn main() { + loop {<|> + if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + } + "#, + r#" + fn main() { + <|>if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + "#, + ); + } + + #[test] + fn simple_while() { + check_assist( + unwrap_block, + r#" + fn main() { + while true {<|> + if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + } + "#, + r#" + fn main() { + <|>if true { + foo(); + + //comment + bar(); + } else { + println!("bar"); + } + } + "#, + ); + } + + #[test] + fn simple_if_in_while_bad_cursor_position() { + check_assist_not_applicable( + unwrap_block, + r#" + fn main() { + while true { + if true { + foo();<|> + + //comment + bar(); + } else { + println!("bar"); + } + } + } + "#, + ); + } +} -- cgit v1.2.3 From bbe22640b8d52354c3de3e126c9fcda5b1b174fd Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 29 Apr 2020 14:53:47 +0200 Subject: Add unwrap block assist #4156 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index b98601f1c..35d87bc9e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -6,7 +6,7 @@ use ra_syntax::{ast, AstNode}; // Assist: unwrap_block // -// Removes the `mut` keyword. +// This assist removes if...else, for, while and loop control statements to just keep the body. // // ``` // fn foo() { @@ -18,7 +18,7 @@ use ra_syntax::{ast, AstNode}; // -> // ``` // fn foo() { -// <|>println!("foo"); +// println!("foo"); // } // ``` pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { -- cgit v1.2.3 From df7899e47a83fb5544d09d2db9405762d3ce29b7 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 1 May 2020 16:41:29 +0200 Subject: Add unwrap block assist #4156 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 69 +++++++++----------------- 1 file changed, 23 insertions(+), 46 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 35d87bc9e..71d6d462b 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,8 +1,8 @@ use crate::{Assist, AssistCtx, AssistId}; -use ast::LoopBodyOwner; +use ast::{BlockExpr, Expr, LoopBodyOwner}; use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, AstNode}; +use ra_syntax::{ast, AstNode, TextRange}; // Assist: unwrap_block // @@ -26,24 +26,14 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { // if expression let mut expr_to_unwrap: Option = None; for block_expr in if_expr.blocks() { - if let Some(block) = block_expr.block() { - let cursor_in_range = - block.l_curly_token()?.text_range().contains_range(ctx.frange.range); - - if cursor_in_range { - let exprto = unwrap_trivial_block(block_expr); - expr_to_unwrap = Some(exprto); - break; - } + if let Some(expr) = excract_expr(ctx.frange.range, block_expr) { + expr_to_unwrap = Some(expr); + break; } } let expr_to_unwrap = expr_to_unwrap?; // Find if we are in a else if block - let ancestor = ctx - .sema - .ancestors_with_macros(if_expr.syntax().clone()) - .skip(1) - .find_map(ast::IfExpr::cast); + let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast); if let Some(ancestor) = ancestor { Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap)) @@ -53,42 +43,18 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { } else if let Some(for_expr) = ctx.find_node_at_offset::() { // for expression let block_expr = for_expr.loop_body()?; - let block = block_expr.block()?; - let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); - - if cursor_in_range { - let expr_to_unwrap = unwrap_trivial_block(block_expr); - - Some((ast::Expr::ForExpr(for_expr), expr_to_unwrap)) - } else { - None - } + excract_expr(ctx.frange.range, block_expr) + .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap)) } else if let Some(while_expr) = ctx.find_node_at_offset::() { // while expression let block_expr = while_expr.loop_body()?; - let block = block_expr.block()?; - let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); - - if cursor_in_range { - let expr_to_unwrap = unwrap_trivial_block(block_expr); - - Some((ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) - } else { - None - } + excract_expr(ctx.frange.range, block_expr) + .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) } else if let Some(loop_expr) = ctx.find_node_at_offset::() { // loop expression let block_expr = loop_expr.loop_body()?; - let block = block_expr.block()?; - let cursor_in_range = block.l_curly_token()?.text_range().contains_range(ctx.frange.range); - - if cursor_in_range { - let expr_to_unwrap = unwrap_trivial_block(block_expr); - - Some((ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) - } else { - None - } + excract_expr(ctx.frange.range, block_expr) + .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) } else { None }; @@ -114,6 +80,17 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }) } +fn excract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option { + let block = block_expr.block()?; + let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); + + if cursor_in_range { + Some(unwrap_trivial_block(block_expr)) + } else { + None + } +} + #[cfg(test)] mod tests { use crate::helpers::{check_assist, check_assist_not_applicable}; -- cgit v1.2.3 From eea21738ab9e0b7438d03f7b2efc18c15cc30cf2 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sat, 2 May 2020 12:20:39 +0200 Subject: Add unwrap block assist #4156 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 89 ++++---------------------- 1 file changed, 13 insertions(+), 76 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') 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 @@ use crate::{Assist, AssistCtx, AssistId}; -use ast::{BlockExpr, Expr, LoopBodyOwner}; +use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr}; use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, AstNode, TextRange}; +use ra_syntax::{ast, AstNode, TextRange, T}; // Assist: unwrap_block // @@ -22,15 +22,11 @@ use ra_syntax::{ast, AstNode, TextRange}; // } // ``` pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { - let res = if let Some(if_expr) = ctx.find_node_at_offset::() { + let l_curly_token = ctx.find_token_at_offset(T!['{'])?; + + let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) { // if expression - let mut expr_to_unwrap: Option = None; - for block_expr in if_expr.blocks() { - if let Some(expr) = excract_expr(ctx.frange.range, block_expr) { - expr_to_unwrap = Some(expr); - break; - } - } + let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); let expr_to_unwrap = expr_to_unwrap?; // Find if we are in a else if block 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 { } else { Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap)) } - } else if let Some(for_expr) = ctx.find_node_at_offset::() { + } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) { // for expression let block_expr = for_expr.loop_body()?; - excract_expr(ctx.frange.range, block_expr) + extract_expr(ctx.frange.range, block_expr) .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap)) - } else if let Some(while_expr) = ctx.find_node_at_offset::() { + } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) { // while expression let block_expr = while_expr.loop_body()?; - excract_expr(ctx.frange.range, block_expr) + extract_expr(ctx.frange.range, block_expr) .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) - } else if let Some(loop_expr) = ctx.find_node_at_offset::() { + } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) { // loop expression let block_expr = loop_expr.loop_body()?; - excract_expr(ctx.frange.range, block_expr) + extract_expr(ctx.frange.range, block_expr) .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) } else { None @@ -80,7 +76,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }) } -fn excract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option { +fn extract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option { let block = block_expr.block()?; let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); @@ -200,65 +196,6 @@ mod tests { ); } - #[test] - fn issue_example_with_if() { - check_assist( - unwrap_block, - r#" - fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { - if let Some(ty) = &ctx.expected_type {<|> - if let Some(Adt::Enum(enum_data)) = ty.as_adt() { - let variants = enum_data.variants(ctx.db); - - let module = if let Some(module) = ctx.scope().module() { - // Compute path from the completion site if available. - module - } else { - // Otherwise fall back to the enum's definition site. - enum_data.module(ctx.db) - }; - - for variant in variants { - if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { - // Variants with trivial paths are already added by the existing completion logic, - // so we should avoid adding these twice - if path.segments.len() > 1 { - acc.add_enum_variant(ctx, variant, Some(path.to_string())); - } - } - } - } - } - } - "#, - r#" - fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { - <|>if let Some(Adt::Enum(enum_data)) = ty.as_adt() { - let variants = enum_data.variants(ctx.db); - - let module = if let Some(module) = ctx.scope().module() { - // Compute path from the completion site if available. - module - } else { - // Otherwise fall back to the enum's definition site. - enum_data.module(ctx.db) - }; - - for variant in variants { - if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { - // Variants with trivial paths are already added by the existing completion logic, - // so we should avoid adding these twice - if path.segments.len() > 1 { - acc.add_enum_variant(ctx, variant, Some(path.to_string())); - } - } - } - } - } - "#, - ); - } - #[test] fn simple_for() { check_assist( -- cgit v1.2.3 From 6d5f3922f7cf6d6c02521ad947abd63ab4764fca Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sat, 2 May 2020 12:31:11 +0200 Subject: Add unwrap block assist #4156 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 8912ce645..58649c47e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -76,12 +76,11 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }) } -fn extract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option { - let block = block_expr.block()?; +fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option { let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); if cursor_in_range { - Some(unwrap_trivial_block(block_expr)) + Some(unwrap_trivial_block(block)) } else { None } -- cgit v1.2.3 From 802617ccc25aeea82eb417b9329812c795311a73 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 4 May 2020 14:16:10 +0200 Subject: Simplify --- crates/ra_assists/src/handlers/unwrap_block.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 58649c47e..b407eb870 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -24,38 +24,38 @@ use ra_syntax::{ast, AstNode, TextRange, T}; pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { let l_curly_token = ctx.find_token_at_offset(T!['{'])?; - let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) { + let (expr, expr_to_unwrap) = if let Some(if_expr) = + l_curly_token.ancestors().find_map(IfExpr::cast) + { // if expression let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); let expr_to_unwrap = expr_to_unwrap?; // Find if we are in a else if block let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast); - if let Some(ancestor) = ancestor { - Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap)) - } else { - Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap)) + match ancestor { + None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), + Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), } } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) { // for expression let block_expr = for_expr.loop_body()?; - extract_expr(ctx.frange.range, block_expr) - .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap)) + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::ForExpr(for_expr), expr_to_unwrap) } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) { // while expression let block_expr = while_expr.loop_body()?; - extract_expr(ctx.frange.range, block_expr) - .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) { // loop expression let block_expr = loop_expr.loop_body()?; - extract_expr(ctx.frange.range, block_expr) - .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) } else { - None + return None; }; - let (expr, expr_to_unwrap) = res?; ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { edit.set_cursor(expr.syntax().text_range().start()); edit.target(expr_to_unwrap.syntax().text_range()); -- cgit v1.2.3 From d7450222a97cb9abefc4fd843ae9d6f4d0d0f93f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 4 May 2020 14:55:24 +0200 Subject: Simplify --- crates/ra_assists/src/handlers/unwrap_block.rs | 67 +++++++++++++------------- 1 file changed, 34 insertions(+), 33 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index b407eb870..859c70ad8 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,8 +1,8 @@ use crate::{Assist, AssistCtx, AssistId}; -use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr}; +use ast::LoopBodyOwner; use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, AstNode, TextRange, T}; +use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; // Assist: unwrap_block // @@ -23,37 +23,38 @@ use ra_syntax::{ast, AstNode, TextRange, T}; // ``` pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { let l_curly_token = ctx.find_token_at_offset(T!['{'])?; - - let (expr, expr_to_unwrap) = if let Some(if_expr) = - l_curly_token.ancestors().find_map(IfExpr::cast) - { - // if expression - let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); - let expr_to_unwrap = expr_to_unwrap?; - // Find if we are in a else if block - let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast); - - match ancestor { - None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), - Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), + let block = ast::BlockExpr::cast(l_curly_token.parent())?; + let parent = block.syntax().parent()?; + let (expr, expr_to_unwrap) = match_ast! { + match parent { + ast::IfExpr(if_expr) => { + let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); + let expr_to_unwrap = expr_to_unwrap?; + // Find if we are in a else if block + let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); + + match ancestor { + None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), + Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), + } + }, + ast::ForExpr(for_expr) => { + let block_expr = for_expr.loop_body()?; + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::ForExpr(for_expr), expr_to_unwrap) + }, + ast::WhileExpr(while_expr) => { + let block_expr = while_expr.loop_body()?; + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) + }, + ast::LoopExpr(loop_expr) => { + let block_expr = loop_expr.loop_body()?; + let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; + (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) + }, + _ => return None, } - } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) { - // for expression - let block_expr = for_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::ForExpr(for_expr), expr_to_unwrap) - } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) { - // while expression - let block_expr = while_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) - } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) { - // loop expression - let block_expr = loop_expr.loop_body()?; - let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; - (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) - } else { - return None; }; ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { @@ -76,7 +77,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }) } -fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option { +fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option { let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); if cursor_in_range { -- cgit v1.2.3 From 25e6bbde01d4a9cd08fa79db5b8b7da6bbf1a623 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 6 May 2020 10:16:55 +0200 Subject: Merge assits::test_helpers and tests --- crates/ra_assists/src/handlers/unwrap_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 859c70ad8..89992117d 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -89,7 +89,7 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option Date: Wed, 6 May 2020 12:51:28 +0200 Subject: Move target to AssistLabel Target is used for assists sorting, so we need it before we compute the action. --- crates/ra_assists/src/handlers/unwrap_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 89992117d..6df927abb 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -57,9 +57,9 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { } }; - ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { + let target = expr_to_unwrap.syntax().text_range(); + ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| { edit.set_cursor(expr.syntax().text_range().start()); - edit.target(expr_to_unwrap.syntax().text_range()); let pat_start: &[_] = &[' ', '{', '\n']; let expr_to_unwrap = expr_to_unwrap.to_string(); -- cgit v1.2.3 From 4867968d22899395e6551f22641b3617e995140c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 6 May 2020 18:45:35 +0200 Subject: Refactor assists API to be more convenient for adding new assists It now duplicates completion API in its shape. --- crates/ra_assists/src/handlers/unwrap_block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 6df927abb..eba0631a4 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,4 +1,4 @@ -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; use ast::LoopBodyOwner; use ra_fmt::unwrap_trivial_block; @@ -21,7 +21,7 @@ use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let l_curly_token = ctx.find_token_at_offset(T!['{'])?; let block = ast::BlockExpr::cast(l_curly_token.parent())?; let parent = block.syntax().parent()?; @@ -58,7 +58,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }; let target = expr_to_unwrap.syntax().text_range(); - ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| { + acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { edit.set_cursor(expr.syntax().text_range().start()); let pat_start: &[_] = &[' ', '{', '\n']; -- cgit v1.2.3 From 98a7bb24358c0f1e9353195d6933f5973c8edaba Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 10 May 2020 15:31:51 +0200 Subject: do not remove then block when you unwrap else block #4361 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_assists/src/handlers/unwrap_block.rs | 221 +++++++++++++++++++++---- 1 file changed, 193 insertions(+), 28 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index eba0631a4..e52ec557e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,6 +1,6 @@ use crate::{AssistContext, AssistId, Assists}; -use ast::LoopBodyOwner; +use ast::{ElseBranch, Expr, LoopBodyOwner}; use ra_fmt::unwrap_trivial_block; use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; @@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let l_curly_token = ctx.find_token_at_offset(T!['{'])?; let block = ast::BlockExpr::cast(l_curly_token.parent())?; let parent = block.syntax().parent()?; + let assist_id = AssistId("unwrap_block"); + let assist_label = "Unwrap block"; + let (expr, expr_to_unwrap) = match_ast! { match parent { - ast::IfExpr(if_expr) => { - let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); - let expr_to_unwrap = expr_to_unwrap?; - // Find if we are in a else if block - let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); - - match ancestor { - None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), - Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), - } - }, ast::ForExpr(for_expr) => { let block_expr = for_expr.loop_body()?; let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; @@ -53,27 +45,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) }, + ast::IfExpr(if_expr) => { + let mut resp = None; + + let then_branch = if_expr.then_branch()?; + if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { + if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { + // For `else if` blocks + let ancestor_then_branch = ancestor.then_branch()?; + let l_curly_token = then_branch.l_curly_token()?; + + let target = then_branch.syntax().text_range(); + return acc.add(assist_id, assist_label, target, |edit| { + let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); + let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); + + edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); + edit.delete(range_to_del_rest); + edit.delete(range_to_del_else_if); + edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); + }); + } else { + resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); + } + } else if let Some(else_branch) = if_expr.else_branch() { + match else_branch { + ElseBranch::Block(else_block) => { + let l_curly_token = else_block.l_curly_token()?; + if l_curly_token.text_range().contains_range(ctx.frange.range) { + let target = else_block.syntax().text_range(); + return acc.add(assist_id, assist_label, target, |edit| { + let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); + + edit.set_cursor(then_branch.syntax().text_range().end()); + edit.delete(range_to_del); + edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); + }); + } + }, + ElseBranch::IfExpr(_) => {}, + } + } + + resp? + }, _ => return None, } }; let target = expr_to_unwrap.syntax().text_range(); - acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { + acc.add(assist_id, assist_label, target, |edit| { edit.set_cursor(expr.syntax().text_range().start()); - let pat_start: &[_] = &[' ', '{', '\n']; - let expr_to_unwrap = expr_to_unwrap.to_string(); - let expr_string = expr_to_unwrap.trim_start_matches(pat_start); - let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); - expr_string_lines.pop(); // Delete last line - - let expr_string = expr_string_lines - .into_iter() - .map(|line| line.replacen(" ", "", 1)) // Delete indentation - .collect::>() - .join("\n"); - - edit.replace(expr.syntax().text_range(), expr_string); + edit.replace( + expr.syntax().text_range(), + update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), + ); }) } @@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option String { + let expr_string = expr_str.trim_start_matches(trim_start_pat); + let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); + expr_string_lines.pop(); // Delete last line + + expr_string_lines + .into_iter() + .map(|line| line.replacen(" ", "", 1)) // Delete indentation + .collect::>() + .join("\n") +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -142,7 +181,13 @@ mod tests { r#" fn main() { bar(); - <|>println!("bar"); + if true { + foo(); + + //comment + bar(); + }<|> + println!("bar"); } "#, ); @@ -170,7 +215,127 @@ mod tests { r#" fn main() { //bar(); - <|>println!("bar"); + if true { + println!("true"); + + //comment + //bar(); + }<|> + println!("bar"); + } + "#, + ); + } + + #[test] + fn simple_if_else_if_nested() { + check_assist( + unwrap_block, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + } else if true {<|> + println!("foo"); + } + } + "#, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + }<|> + println!("foo"); + } + "#, + ); + } + + #[test] + fn simple_if_else_if_nested_else() { + check_assist( + unwrap_block, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + } else if true { + println!("foo"); + } else {<|> + println!("else"); + } + } + "#, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + } else if true { + println!("foo"); + }<|> + println!("else"); + } + "#, + ); + } + + #[test] + fn simple_if_else_if_nested_middle() { + check_assist( + unwrap_block, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + } else if true {<|> + println!("foo"); + } else { + println!("else"); + } + } + "#, + r#" + fn main() { + //bar(); + if true { + println!("true"); + + //comment + //bar(); + } else if false { + println!("bar"); + }<|> + println!("foo"); } "#, ); -- cgit v1.2.3 From 74da16f6f95aec839842d1f920fec9e40b6f80a4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 13:41:58 +0200 Subject: Cleanup imports --- crates/ra_assists/src/handlers/unwrap_block.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index e52ec557e..b76182d79 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,8 +1,10 @@ -use crate::{AssistContext, AssistId, Assists}; - -use ast::{ElseBranch, Expr, LoopBodyOwner}; use ra_fmt::unwrap_trivial_block; -use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; +use ra_syntax::{ + ast::{self, ElseBranch, Expr, LoopBodyOwner}, + match_ast, AstNode, TextRange, T, +}; + +use crate::{AssistContext, AssistId, Assists}; // Assist: unwrap_block // -- cgit v1.2.3 From 70930d3bb2ba1d4a7a7d4a489da714096294acca Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 May 2020 00:03:42 +0200 Subject: Remove set_cursor --- crates/ra_assists/src/handlers/unwrap_block.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs') diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index b76182d79..8440c7d0f 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -62,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); - edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); edit.delete(range_to_del_rest); edit.delete(range_to_del_else_if); edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); @@ -79,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> return acc.add(assist_id, assist_label, target, |edit| { let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); - edit.set_cursor(then_branch.syntax().text_range().end()); edit.delete(range_to_del); edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); }); @@ -97,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let target = expr_to_unwrap.syntax().text_range(); acc.add(assist_id, assist_label, target, |edit| { - edit.set_cursor(expr.syntax().text_range().start()); - edit.replace( expr.syntax().text_range(), update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), @@ -154,7 +150,7 @@ mod tests { r#" fn main() { bar(); - <|>foo(); + foo(); //comment bar(); @@ -188,7 +184,7 @@ mod tests { //comment bar(); - }<|> + } println!("bar"); } "#, @@ -222,7 +218,7 @@ mod tests { //comment //bar(); - }<|> + } println!("bar"); } "#, @@ -258,7 +254,7 @@ mod tests { //bar(); } else if false { println!("bar"); - }<|> + } println!("foo"); } "#, @@ -298,7 +294,7 @@ mod tests { println!("bar"); } else if true { println!("foo"); - }<|> + } println!("else"); } "#, @@ -336,7 +332,7 @@ mod tests { //bar(); } else if false { println!("bar"); - }<|> + } println!("foo"); } "#, @@ -383,7 +379,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment @@ -417,7 +413,7 @@ mod tests { r#" fn main() { for i in 0..5 { - <|>foo(); + foo(); //comment bar(); @@ -447,7 +443,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment @@ -480,7 +476,7 @@ mod tests { "#, r#" fn main() { - <|>if true { + if true { foo(); //comment -- cgit v1.2.3