diff options
-rw-r--r-- | crates/ra_assists/src/assists/apply_demorgan.rs | 14 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/early_return.rs | 11 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/invert_if.rs | 33 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 35 |
4 files changed, 64 insertions, 29 deletions
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs index dac6280ad..ba08a8223 100644 --- a/crates/ra_assists/src/assists/apply_demorgan.rs +++ b/crates/ra_assists/src/assists/apply_demorgan.rs | |||
@@ -31,12 +31,14 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> { | |||
31 | if !cursor_in_range { | 31 | if !cursor_in_range { |
32 | return None; | 32 | return None; |
33 | } | 33 | } |
34 | |||
34 | let lhs = expr.lhs()?; | 35 | let lhs = expr.lhs()?; |
35 | let lhs_range = lhs.syntax().text_range(); | 36 | let lhs_range = lhs.syntax().text_range(); |
37 | let not_lhs = invert_boolean_expression(lhs); | ||
38 | |||
36 | let rhs = expr.rhs()?; | 39 | let rhs = expr.rhs()?; |
37 | let rhs_range = rhs.syntax().text_range(); | 40 | let rhs_range = rhs.syntax().text_range(); |
38 | let not_lhs = invert_boolean_expression(&lhs)?; | 41 | let not_rhs = invert_boolean_expression(rhs); |
39 | let not_rhs = invert_boolean_expression(&rhs)?; | ||
40 | 42 | ||
41 | ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| { | 43 | ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| { |
42 | edit.target(op_range); | 44 | edit.target(op_range); |
@@ -77,12 +79,12 @@ mod tests { | |||
77 | } | 79 | } |
78 | 80 | ||
79 | #[test] | 81 | #[test] |
80 | fn demorgan_doesnt_apply_with_cursor_not_on_op() { | 82 | fn demorgan_general_case() { |
81 | check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") | 83 | check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") |
82 | } | 84 | } |
83 | 85 | ||
84 | #[test] | 86 | #[test] |
85 | fn demorgan_doesnt_apply_when_operands_arent_negated_already() { | 87 | fn demorgan_doesnt_apply_with_cursor_not_on_op() { |
86 | check_assist_not_applicable(apply_demorgan, "fn f() { x ||<|> x }") | 88 | check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") |
87 | } | 89 | } |
88 | } | 90 | } |
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index 3169be2b9..8f30dc586 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs | |||
@@ -10,6 +10,7 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | assist_ctx::{Assist, AssistCtx}, | 12 | assist_ctx::{Assist, AssistCtx}, |
13 | assists::invert_if::invert_boolean_expression, | ||
13 | AssistId, | 14 | AssistId, |
14 | }; | 15 | }; |
15 | 16 | ||
@@ -99,9 +100,13 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> { | |||
99 | let new_block = match if_let_pat { | 100 | let new_block = match if_let_pat { |
100 | None => { | 101 | None => { |
101 | // If. | 102 | // If. |
102 | let early_expression = &(early_expression.syntax().to_string() + ";"); | 103 | let new_expr = { |
103 | let new_expr = if_indent_level | 104 | let then_branch = |
104 | .increase_indent(make::if_expression(cond_expr, early_expression)); | 105 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
106 | let cond = invert_boolean_expression(cond_expr); | ||
107 | let e = make::expr_if(cond, then_branch); | ||
108 | if_indent_level.increase_indent(e) | ||
109 | }; | ||
105 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 110 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
106 | } | 111 | } |
107 | Some((path, bound_ident)) => { | 112 | Some((path, bound_ident)) => { |
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs index 694c3642c..983392f21 100644 --- a/crates/ra_assists/src/assists/invert_if.rs +++ b/crates/ra_assists/src/assists/invert_if.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ra_syntax::ast::{self, AstNode}; | 1 | use ra_syntax::ast::{self, make, AstNode}; |
2 | use ra_syntax::T; | 2 | use ra_syntax::T; |
3 | 3 | ||
4 | use crate::{Assist, AssistCtx, AssistId}; | 4 | use crate::{Assist, AssistCtx, AssistId}; |
@@ -35,8 +35,8 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> { | |||
35 | let then_node = expr.then_branch()?.syntax().clone(); | 35 | let then_node = expr.then_branch()?.syntax().clone(); |
36 | 36 | ||
37 | if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { | 37 | if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { |
38 | let flip_cond = invert_boolean_expression(&cond)?; | ||
39 | let cond_range = cond.syntax().text_range(); | 38 | let cond_range = cond.syntax().text_range(); |
39 | let flip_cond = invert_boolean_expression(cond); | ||
40 | let else_node = else_block.syntax(); | 40 | let else_node = else_block.syntax(); |
41 | let else_range = else_node.text_range(); | 41 | let else_range = else_node.text_range(); |
42 | let then_range = then_node.text_range(); | 42 | let then_range = then_node.text_range(); |
@@ -51,16 +51,23 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> { | |||
51 | None | 51 | None |
52 | } | 52 | } |
53 | 53 | ||
54 | pub(crate) fn invert_boolean_expression(expr: &ast::Expr) -> Option<ast::Expr> { | 54 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { |
55 | if let Some(expr) = invert_special_case(&expr) { | ||
56 | return expr; | ||
57 | } | ||
58 | make::expr_prefix(T![!], expr) | ||
59 | } | ||
60 | |||
61 | pub(crate) fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | ||
55 | match expr { | 62 | match expr { |
56 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { | 63 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { |
57 | ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), | 64 | ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), |
65 | ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), | ||
58 | _ => None, | 66 | _ => None, |
59 | }, | 67 | }, |
60 | ast::Expr::PrefixExpr(pe) => match pe.op_kind()? { | 68 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), |
61 | ast::PrefixOp::Not => pe.expr(), | 69 | // FIXME: |
62 | _ => None, | 70 | // ast::Expr::Literal(true | false ) |
63 | }, | ||
64 | _ => None, | 71 | _ => None, |
65 | } | 72 | } |
66 | } | 73 | } |
@@ -90,12 +97,16 @@ mod tests { | |||
90 | } | 97 | } |
91 | 98 | ||
92 | #[test] | 99 | #[test] |
93 | fn invert_if_doesnt_apply_with_cursor_not_on_if() { | 100 | fn invert_if_general_case() { |
94 | check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }") | 101 | check_assist( |
102 | invert_if, | ||
103 | "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", | ||
104 | "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }", | ||
105 | ) | ||
95 | } | 106 | } |
96 | 107 | ||
97 | #[test] | 108 | #[test] |
98 | fn invert_if_doesnt_apply_without_negated() { | 109 | fn invert_if_doesnt_apply_with_cursor_not_on_if() { |
99 | check_assist_not_applicable(invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }") | 110 | check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }") |
100 | } | 111 | } |
101 | } | 112 | } |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 02966a3ff..862eb1172 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -33,6 +33,21 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordF | |||
33 | } | 33 | } |
34 | } | 34 | } |
35 | 35 | ||
36 | pub fn block_expr( | ||
37 | stmts: impl IntoIterator<Item = ast::Stmt>, | ||
38 | tail_expr: Option<ast::Expr>, | ||
39 | ) -> ast::BlockExpr { | ||
40 | let mut text = "{\n".to_string(); | ||
41 | for stmt in stmts.into_iter() { | ||
42 | text += &format!(" {}\n", stmt.syntax()); | ||
43 | } | ||
44 | if let Some(tail_expr) = tail_expr { | ||
45 | text += &format!(" {}\n", tail_expr.syntax()) | ||
46 | } | ||
47 | text += "}"; | ||
48 | ast_from_text(&format!("fn f() {}", text)) | ||
49 | } | ||
50 | |||
36 | pub fn block_from_expr(e: ast::Expr) -> ast::Block { | 51 | pub fn block_from_expr(e: ast::Expr) -> ast::Block { |
37 | return from_text(&format!("{{ {} }}", e.syntax())); | 52 | return from_text(&format!("{{ {} }}", e.syntax())); |
38 | 53 | ||
@@ -62,6 +77,13 @@ pub fn expr_return() -> ast::Expr { | |||
62 | pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { | 77 | pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { |
63 | expr_from_text(&format!("match {} {}", expr.syntax(), match_arm_list.syntax())) | 78 | expr_from_text(&format!("match {} {}", expr.syntax(), match_arm_list.syntax())) |
64 | } | 79 | } |
80 | pub fn expr_if(condition: ast::Expr, then_branch: ast::BlockExpr) -> ast::Expr { | ||
81 | expr_from_text(&format!("if {} {}", condition.syntax(), then_branch.syntax())) | ||
82 | } | ||
83 | pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { | ||
84 | let token = token(op); | ||
85 | expr_from_text(&format!("{}{}", token, expr.syntax())) | ||
86 | } | ||
65 | fn expr_from_text(text: &str) -> ast::Expr { | 87 | fn expr_from_text(text: &str) -> ast::Expr { |
66 | ast_from_text(&format!("const C: () = {};", text)) | 88 | ast_from_text(&format!("const C: () = {};", text)) |
67 | } | 89 | } |
@@ -158,14 +180,6 @@ pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::Whe | |||
158 | } | 180 | } |
159 | } | 181 | } |
160 | 182 | ||
161 | pub fn if_expression(condition: ast::Expr, statement: &str) -> ast::IfExpr { | ||
162 | ast_from_text(&format!( | ||
163 | "fn f() {{ if !{} {{\n {}\n}}\n}}", | ||
164 | condition.syntax().text(), | ||
165 | statement | ||
166 | )) | ||
167 | } | ||
168 | |||
169 | pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt { | 183 | pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt { |
170 | let text = match initializer { | 184 | let text = match initializer { |
171 | Some(it) => format!("let {} = {};", pattern.syntax(), it.syntax()), | 185 | Some(it) => format!("let {} = {};", pattern.syntax(), it.syntax()), |
@@ -173,6 +187,9 @@ pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetSt | |||
173 | }; | 187 | }; |
174 | ast_from_text(&format!("fn f() {{ {} }}", text)) | 188 | ast_from_text(&format!("fn f() {{ {} }}", text)) |
175 | } | 189 | } |
190 | pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | ||
191 | ast_from_text(&format!("fn f() {{ {}; }}", expr.syntax())) | ||
192 | } | ||
176 | 193 | ||
177 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | 194 | pub fn token(kind: SyntaxKind) -> SyntaxToken { |
178 | tokens::SOURCE_FILE | 195 | tokens::SOURCE_FILE |
@@ -203,7 +220,7 @@ pub mod tokens { | |||
203 | use once_cell::sync::Lazy; | 220 | use once_cell::sync::Lazy; |
204 | 221 | ||
205 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = | 222 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = |
206 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2)\n;")); | 223 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;")); |
207 | 224 | ||
208 | pub fn comma() -> SyntaxToken { | 225 | pub fn comma() -> SyntaxToken { |
209 | SOURCE_FILE | 226 | SOURCE_FILE |