aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/flip_binexpr.rs141
-rw-r--r--crates/ra_assists/src/flip_eq_operands.rs86
-rw-r--r--crates/ra_assists/src/inline_local_variable.rs298
-rw-r--r--crates/ra_assists/src/lib.rs6
4 files changed, 443 insertions, 88 deletions
diff --git a/crates/ra_assists/src/flip_binexpr.rs b/crates/ra_assists/src/flip_binexpr.rs
new file mode 100644
index 000000000..ec377642e
--- /dev/null
+++ b/crates/ra_assists/src/flip_binexpr.rs
@@ -0,0 +1,141 @@
1use hir::db::HirDatabase;
2use ra_syntax::ast::{AstNode, BinExpr, BinOp};
3
4use crate::{AssistCtx, Assist, AssistId};
5
6/// Flip binary expression assist.
7pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
8 let expr = ctx.node_at_offset::<BinExpr>()?;
9 let lhs = expr.lhs()?.syntax();
10 let rhs = expr.rhs()?.syntax();
11 let op_range = expr.op()?.range();
12 // The assist should be applied only if the cursor is on the operator
13 let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
14 if !cursor_in_range {
15 return None;
16 }
17 let action: FlipAction = expr.op_kind()?.into();
18 // The assist should not be applied for certain operators
19 if let FlipAction::DontFlip = action {
20 return None;
21 }
22
23 ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| {
24 edit.target(op_range);
25 if let FlipAction::FlipAndReplaceOp(new_op) = action {
26 edit.replace(op_range, new_op);
27 }
28 edit.replace(lhs.range(), rhs.text());
29 edit.replace(rhs.range(), lhs.text());
30 });
31
32 ctx.build()
33}
34
35enum FlipAction {
36 // Flip the expression
37 Flip,
38 // Flip the expression and replace the operator with this string
39 FlipAndReplaceOp(&'static str),
40 // Do not flip the expression
41 DontFlip,
42}
43
44impl From<BinOp> for FlipAction {
45 fn from(op_kind: BinOp) -> Self {
46 match op_kind {
47 BinOp::Assignment => FlipAction::DontFlip,
48 BinOp::AddAssign => FlipAction::DontFlip,
49 BinOp::DivAssign => FlipAction::DontFlip,
50 BinOp::MulAssign => FlipAction::DontFlip,
51 BinOp::RemAssign => FlipAction::DontFlip,
52 BinOp::ShrAssign => FlipAction::DontFlip,
53 BinOp::ShlAssign => FlipAction::DontFlip,
54 BinOp::SubAssign => FlipAction::DontFlip,
55 BinOp::BitOrAssign => FlipAction::DontFlip,
56 BinOp::BitAndAssign => FlipAction::DontFlip,
57 BinOp::BitXorAssign => FlipAction::DontFlip,
58 BinOp::GreaterTest => FlipAction::FlipAndReplaceOp("<"),
59 BinOp::GreaterEqualTest => FlipAction::FlipAndReplaceOp("<="),
60 BinOp::LesserTest => FlipAction::FlipAndReplaceOp(">"),
61 BinOp::LesserEqualTest => FlipAction::FlipAndReplaceOp(">="),
62 _ => FlipAction::Flip,
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable };
72
73 #[test]
74 fn flip_binexpr_target_is_the_op() {
75 check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==")
76 }
77
78 #[test]
79 fn flip_binexpr_not_applicable_for_assignment() {
80 check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=<|> 2 }")
81 }
82
83 #[test]
84 fn flip_binexpr_works_for_eq() {
85 check_assist(
86 flip_binexpr,
87 "fn f() { let res = 1 ==<|> 2; }",
88 "fn f() { let res = 2 ==<|> 1; }",
89 )
90 }
91
92 #[test]
93 fn flip_binexpr_works_for_gt() {
94 check_assist(
95 flip_binexpr,
96 "fn f() { let res = 1 ><|> 2; }",
97 "fn f() { let res = 2 <<|> 1; }",
98 )
99 }
100
101 #[test]
102 fn flip_binexpr_works_for_lteq() {
103 check_assist(
104 flip_binexpr,
105 "fn f() { let res = 1 <=<|> 2; }",
106 "fn f() { let res = 2 >=<|> 1; }",
107 )
108 }
109
110 #[test]
111 fn flip_binexpr_works_for_complex_expr() {
112 check_assist(
113 flip_binexpr,
114 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
115 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
116 )
117 }
118
119 #[test]
120 fn flip_binexpr_works_inside_match() {
121 check_assist(
122 flip_binexpr,
123 r#"
124 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
125 match other.downcast_ref::<Self>() {
126 None => false,
127 Some(it) => it ==<|> self,
128 }
129 }
130 "#,
131 r#"
132 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
133 match other.downcast_ref::<Self>() {
134 None => false,
135 Some(it) => self ==<|> it,
136 }
137 }
138 "#,
139 )
140 }
141}
diff --git a/crates/ra_assists/src/flip_eq_operands.rs b/crates/ra_assists/src/flip_eq_operands.rs
deleted file mode 100644
index df0bb689d..000000000
--- a/crates/ra_assists/src/flip_eq_operands.rs
+++ /dev/null
@@ -1,86 +0,0 @@
1use hir::db::HirDatabase;
2use ra_syntax::ast::{AstNode, BinExpr, BinOp};
3
4use crate::{AssistCtx, Assist, AssistId};
5
6pub(crate) fn flip_eq_operands(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
7 let expr = ctx.node_at_offset::<BinExpr>()?;
8 let lhs = expr.lhs()?.syntax();
9 let rhs = expr.rhs()?.syntax();
10 let op_range = expr.op()?.range();
11 let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
12 let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest];
13 let expr_op = expr.op_kind()?;
14 if !cursor_in_range || !allowed_ops.iter().any(|o| *o == expr_op) {
15 return None;
16 }
17 ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| {
18 edit.target(op_range);
19 edit.replace(lhs.range(), rhs.text());
20 edit.replace(rhs.range(), lhs.text());
21 });
22
23 ctx.build()
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29
30 use crate::helpers::{check_assist, check_assist_target};
31
32 #[test]
33 fn flip_eq_operands_for_simple_stmt() {
34 check_assist(
35 flip_eq_operands,
36 "fn f() { let res = 1 ==<|> 2; }",
37 "fn f() { let res = 2 ==<|> 1; }",
38 )
39 }
40
41 #[test]
42 fn flip_neq_operands_for_simple_stmt() {
43 check_assist(
44 flip_eq_operands,
45 "fn f() { let res = 1 !=<|> 2; }",
46 "fn f() { let res = 2 !=<|> 1; }",
47 )
48 }
49
50 #[test]
51 fn flip_eq_operands_for_complex_stmt() {
52 check_assist(
53 flip_eq_operands,
54 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
55 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
56 )
57 }
58
59 #[test]
60 fn flip_eq_operands_in_match_expr() {
61 check_assist(
62 flip_eq_operands,
63 r#"
64 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
65 match other.downcast_ref::<Self>() {
66 None => false,
67 Some(it) => it ==<|> self,
68 }
69 }
70 "#,
71 r#"
72 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
73 match other.downcast_ref::<Self>() {
74 None => false,
75 Some(it) => self ==<|> it,
76 }
77 }
78 "#,
79 )
80 }
81
82 #[test]
83 fn flip_eq_operands_target() {
84 check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "==")
85 }
86}
diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs
new file mode 100644
index 000000000..bd3cdb970
--- /dev/null
+++ b/crates/ra_assists/src/inline_local_variable.rs
@@ -0,0 +1,298 @@
1use hir::db::HirDatabase;
2use hir::source_binder::function_from_child_node;
3use ra_syntax::{ast::{self, AstNode}, TextRange};
4use ra_syntax::ast::{PatKind, ExprKind};
5
6use crate::{Assist, AssistCtx, AssistId};
7use crate::assist_ctx::AssistBuilder;
8
9pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
10 let let_stmt = ctx.node_at_offset::<ast::LetStmt>()?;
11 let bind_pat = match let_stmt.pat()?.kind() {
12 PatKind::BindPat(pat) => pat,
13 _ => return None,
14 };
15 if bind_pat.is_mutable() {
16 return None;
17 }
18 let initializer = let_stmt.initializer()?;
19 let wrap_in_parens = match initializer.kind() {
20 ExprKind::LambdaExpr(_)
21 | ExprKind::IfExpr(_)
22 | ExprKind::LoopExpr(_)
23 | ExprKind::ForExpr(_)
24 | ExprKind::WhileExpr(_)
25 | ExprKind::ContinueExpr(_)
26 | ExprKind::BreakExpr(_)
27 | ExprKind::Label(_)
28 | ExprKind::ReturnExpr(_)
29 | ExprKind::MatchExpr(_)
30 | ExprKind::StructLit(_)
31 | ExprKind::CastExpr(_)
32 | ExprKind::PrefixExpr(_)
33 | ExprKind::RangeExpr(_)
34 | ExprKind::BinExpr(_) => true,
35 ExprKind::CallExpr(_)
36 | ExprKind::IndexExpr(_)
37 | ExprKind::MethodCallExpr(_)
38 | ExprKind::FieldExpr(_)
39 | ExprKind::TryExpr(_)
40 | ExprKind::RefExpr(_)
41 | ExprKind::Literal(_)
42 | ExprKind::TupleExpr(_)
43 | ExprKind::ArrayExpr(_)
44 | ExprKind::ParenExpr(_)
45 | ExprKind::PathExpr(_)
46 | ExprKind::BlockExpr(_) => false,
47 };
48
49 let delete_range = if let Some(whitespace) =
50 let_stmt.syntax().next_sibling().and_then(ast::Whitespace::cast)
51 {
52 TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
53 } else {
54 let_stmt.syntax().range()
55 };
56
57 let init_str = if wrap_in_parens {
58 format!("({})", initializer.syntax().text().to_string())
59 } else {
60 initializer.syntax().text().to_string()
61 };
62 let function = function_from_child_node(ctx.db, ctx.frange.file_id, bind_pat.syntax())?;
63 let scope = function.scopes(ctx.db);
64 let refs = scope.find_all_refs(bind_pat);
65
66 ctx.add_action(
67 AssistId("inline_local_variable"),
68 "inline local variable",
69 move |edit: &mut AssistBuilder| {
70 edit.delete(delete_range);
71 for desc in refs {
72 edit.replace(desc.range, init_str.clone())
73 }
74 edit.set_cursor(delete_range.start())
75 },
76 );
77
78 ctx.build()
79}
80
81#[cfg(test)]
82mod tests {
83 use crate::helpers::{check_assist, check_assist_not_applicable};
84
85 use super::*;
86
87 #[test]
88 fn test_inline_let_bind_literal_expr() {
89 check_assist(
90 inline_local_varialbe,
91 "
92fn bar(a: usize) {}
93fn foo() {
94 let a<|> = 1;
95 a + 1;
96 if a > 10 {
97 }
98
99 while a > 10 {
100
101 }
102 let b = a * 10;
103 bar(a);
104}",
105 "
106fn bar(a: usize) {}
107fn foo() {
108 <|>1 + 1;
109 if 1 > 10 {
110 }
111
112 while 1 > 10 {
113
114 }
115 let b = 1 * 10;
116 bar(1);
117}",
118 );
119 }
120
121 #[test]
122 fn test_inline_let_bind_bin_expr() {
123 check_assist(
124 inline_local_varialbe,
125 "
126fn bar(a: usize) {}
127fn foo() {
128 let a<|> = 1 + 1;
129 a + 1;
130 if a > 10 {
131 }
132
133 while a > 10 {
134
135 }
136 let b = a * 10;
137 bar(a);
138}",
139 "
140fn bar(a: usize) {}
141fn foo() {
142 <|>(1 + 1) + 1;
143 if (1 + 1) > 10 {
144 }
145
146 while (1 + 1) > 10 {
147
148 }
149 let b = (1 + 1) * 10;
150 bar((1 + 1));
151}",
152 );
153 }
154
155 #[test]
156 fn test_inline_let_bind_function_call_expr() {
157 check_assist(
158 inline_local_varialbe,
159 "
160fn bar(a: usize) {}
161fn foo() {
162 let a<|> = bar(1);
163 a + 1;
164 if a > 10 {
165 }
166
167 while a > 10 {
168
169 }
170 let b = a * 10;
171 bar(a);
172}",
173 "
174fn bar(a: usize) {}
175fn foo() {
176 <|>bar(1) + 1;
177 if bar(1) > 10 {
178 }
179
180 while bar(1) > 10 {
181
182 }
183 let b = bar(1) * 10;
184 bar(bar(1));
185}",
186 );
187 }
188
189 #[test]
190 fn test_inline_let_bind_cast_expr() {
191 check_assist(
192 inline_local_varialbe,
193 "
194fn bar(a: usize): usize { a }
195fn foo() {
196 let a<|> = bar(1) as u64;
197 a + 1;
198 if a > 10 {
199 }
200
201 while a > 10 {
202
203 }
204 let b = a * 10;
205 bar(a);
206}",
207 "
208fn bar(a: usize): usize { a }
209fn foo() {
210 <|>(bar(1) as u64) + 1;
211 if (bar(1) as u64) > 10 {
212 }
213
214 while (bar(1) as u64) > 10 {
215
216 }
217 let b = (bar(1) as u64) * 10;
218 bar((bar(1) as u64));
219}",
220 );
221 }
222
223 #[test]
224 fn test_inline_let_bind_block_expr() {
225 check_assist(
226 inline_local_varialbe,
227 "
228fn foo() {
229 let a<|> = { 10 + 1 };
230 a + 1;
231 if a > 10 {
232 }
233
234 while a > 10 {
235
236 }
237 let b = a * 10;
238 bar(a);
239}",
240 "
241fn foo() {
242 <|>{ 10 + 1 } + 1;
243 if { 10 + 1 } > 10 {
244 }
245
246 while { 10 + 1 } > 10 {
247
248 }
249 let b = { 10 + 1 } * 10;
250 bar({ 10 + 1 });
251}",
252 );
253 }
254
255 #[test]
256 fn test_inline_let_bind_paren_expr() {
257 check_assist(
258 inline_local_varialbe,
259 "
260fn foo() {
261 let a<|> = ( 10 + 1 );
262 a + 1;
263 if a > 10 {
264 }
265
266 while a > 10 {
267
268 }
269 let b = a * 10;
270 bar(a);
271}",
272 "
273fn foo() {
274 <|>( 10 + 1 ) + 1;
275 if ( 10 + 1 ) > 10 {
276 }
277
278 while ( 10 + 1 ) > 10 {
279
280 }
281 let b = ( 10 + 1 ) * 10;
282 bar(( 10 + 1 ));
283}",
284 );
285 }
286
287 #[test]
288 fn test_not_inline_mut_variable() {
289 check_assist_not_applicable(
290 inline_local_varialbe,
291 "
292fn foo() {
293 let mut a<|> = 1 + 1;
294 a + 1;
295}",
296 );
297 }
298}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 8900fbc4b..c1514f8e5 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -88,11 +88,12 @@ where
88mod add_derive; 88mod add_derive;
89mod add_impl; 89mod add_impl;
90mod flip_comma; 90mod flip_comma;
91mod flip_eq_operands; 91mod flip_binexpr;
92mod change_visibility; 92mod change_visibility;
93mod fill_match_arms; 93mod fill_match_arms;
94mod fill_struct_fields; 94mod fill_struct_fields;
95mod introduce_variable; 95mod introduce_variable;
96mod inline_local_variable;
96mod replace_if_let_with_match; 97mod replace_if_let_with_match;
97mod split_import; 98mod split_import;
98mod remove_dbg; 99mod remove_dbg;
@@ -107,7 +108,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
107 fill_match_arms::fill_match_arms, 108 fill_match_arms::fill_match_arms,
108 fill_struct_fields::fill_struct_fields, 109 fill_struct_fields::fill_struct_fields,
109 flip_comma::flip_comma, 110 flip_comma::flip_comma,
110 flip_eq_operands::flip_eq_operands, 111 flip_binexpr::flip_binexpr,
111 introduce_variable::introduce_variable, 112 introduce_variable::introduce_variable,
112 replace_if_let_with_match::replace_if_let_with_match, 113 replace_if_let_with_match::replace_if_let_with_match,
113 split_import::split_import, 114 split_import::split_import,
@@ -115,6 +116,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
115 auto_import::auto_import, 116 auto_import::auto_import,
116 add_missing_impl_members::add_missing_impl_members, 117 add_missing_impl_members::add_missing_impl_members,
117 add_missing_impl_members::add_missing_default_members, 118 add_missing_impl_members::add_missing_default_members,
119 inline_local_variable::inline_local_varialbe,
118 ] 120 ]
119} 121}
120 122