|
| 1 | +use syntax::{ast, AstNode, SyntaxKind, TextRange}; |
| 2 | + |
| 3 | +use crate::{AssistContext, AssistId, AssistKind, Assists}; |
| 4 | + |
| 5 | +// Assist: remove_parentheses |
| 6 | +// |
| 7 | +// Removes useless parentheses. |
| 8 | +// |
| 9 | +// ``` |
| 10 | +// fn main() { |
| 11 | +// _ = $0(2) + 2; |
| 12 | +// } |
| 13 | +// ``` |
| 14 | +// -> |
| 15 | +// ``` |
| 16 | +// fn main() { |
| 17 | +// _ = 2 + 2; |
| 18 | +// } |
| 19 | +// ``` |
| 20 | +pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { |
| 21 | + let parens = ctx.find_node_at_offset::<ast::ParenExpr>()?; |
| 22 | + let l_paren = parens.l_paren_token()?; |
| 23 | + let r_paren = parens.r_paren_token()?; |
| 24 | + |
| 25 | + let cursor_in_range = l_paren.text_range().contains_range(ctx.selection_trimmed()) |
| 26 | + || r_paren.text_range().contains_range(ctx.selection_trimmed()); |
| 27 | + if !cursor_in_range { |
| 28 | + return None; |
| 29 | + } |
| 30 | + |
| 31 | + // FIXME: check that precedence is right |
| 32 | + |
| 33 | + let delete_from_l = l_paren.text_range().start(); |
| 34 | + let delete_to_l = match l_paren.next_token() { |
| 35 | + Some(it) if it.kind() == SyntaxKind::WHITESPACE => it.text_range().end(), |
| 36 | + _ => l_paren.text_range().end(), |
| 37 | + }; |
| 38 | + |
| 39 | + let delete_from_r = match r_paren.prev_token() { |
| 40 | + Some(it) if it.kind() == SyntaxKind::WHITESPACE => it.text_range().start(), |
| 41 | + _ => r_paren.text_range().start(), |
| 42 | + }; |
| 43 | + let delete_to_r = r_paren.text_range().end(); |
| 44 | + |
| 45 | + let target = parens.syntax().text_range(); |
| 46 | + acc.add( |
| 47 | + AssistId("remove_parentheses", AssistKind::Refactor), |
| 48 | + "Remove parentheses", |
| 49 | + target, |
| 50 | + |builder| { |
| 51 | + builder.delete(TextRange::new(delete_from_l, delete_to_l)); |
| 52 | + builder.delete(TextRange::new(delete_from_r, delete_to_r)); |
| 53 | + }, |
| 54 | + ) |
| 55 | +} |
| 56 | + |
| 57 | +#[cfg(test)] |
| 58 | +mod tests { |
| 59 | + use crate::tests::{check_assist, check_assist_not_applicable}; |
| 60 | + |
| 61 | + use super::*; |
| 62 | + |
| 63 | + #[test] |
| 64 | + fn remove_parens_simple() { |
| 65 | + check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#); |
| 66 | + check_assist(remove_parentheses, r#"fn f() { ($02) + 2; }"#, r#"fn f() { 2 + 2; }"#); |
| 67 | + check_assist(remove_parentheses, r#"fn f() { (2)$0 + 2; }"#, r#"fn f() { 2 + 2; }"#); |
| 68 | + check_assist(remove_parentheses, r#"fn f() { (2$0) + 2; }"#, r#"fn f() { 2 + 2; }"#); |
| 69 | + } |
| 70 | + |
| 71 | + // We should not permit assist here and yet |
| 72 | + #[test] |
| 73 | + fn remove_parens_wrong() { |
| 74 | + check_assist( |
| 75 | + remove_parentheses, |
| 76 | + r#"fn f() { $0(2 + 2) * 8; }"#, |
| 77 | + r#"fn f() { 2 + 2 * 8; }"#, |
| 78 | + ); |
| 79 | + } |
| 80 | + |
| 81 | + #[test] |
| 82 | + fn remove_parens_doesnt_apply_with_cursor_not_on_paren() { |
| 83 | + check_assist_not_applicable(remove_parentheses, r#"fn f() { (2 +$0 2) }"#); |
| 84 | + check_assist_not_applicable(remove_parentheses, r#"fn f() {$0 (2 + 2) }"#); |
| 85 | + } |
| 86 | +} |
0 commit comments