diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/ast_transform.rs | 24 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_custom_impl.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_explicit_type.rs | 4 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_missing_impl_members.rs | 50 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/unwrap_block.rs | 221 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 4 |
6 files changed, 270 insertions, 35 deletions
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 9ac65ab39..3079a02a2 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{PathResolution, SemanticsScope}; | 4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::SyntaxRewriter, | 7 | algo::SyntaxRewriter, |
@@ -51,7 +51,27 @@ impl<'a> SubstituteTypeParams<'a> { | |||
51 | .into_iter() | 51 | .into_iter() |
52 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky | 52 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky |
53 | .skip(1) | 53 | .skip(1) |
54 | .zip(substs.into_iter()) | 54 | // The actual list of trait type parameters may be longer than the one |
55 | // used in the `impl` block due to trailing default type parametrs. | ||
56 | // For that case we extend the `substs` with an empty iterator so we | ||
57 | // can still hit those trailing values and check if they actually have | ||
58 | // a default type. If they do, go for that type from `hir` to `ast` so | ||
59 | // the resulting change can be applied correctly. | ||
60 | .zip(substs.into_iter().map(Some).chain(std::iter::repeat(None))) | ||
61 | .filter_map(|(k, v)| match v { | ||
62 | Some(v) => Some((k, v)), | ||
63 | None => { | ||
64 | let default = k.default(source_scope.db)?; | ||
65 | Some(( | ||
66 | k, | ||
67 | ast::make::type_ref( | ||
68 | &default | ||
69 | .display_source_code(source_scope.db, source_scope.module()?.into()) | ||
70 | .ok()?, | ||
71 | ), | ||
72 | )) | ||
73 | } | ||
74 | }) | ||
55 | .collect(); | 75 | .collect(); |
56 | return SubstituteTypeParams { | 76 | return SubstituteTypeParams { |
57 | source_scope, | 77 | source_scope, |
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 795a225a4..2baeb8607 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -49,7 +49,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
49 | let start_offset = annotated.syntax().parent()?.text_range().end(); | 49 | let start_offset = annotated.syntax().parent()?.text_range().end(); |
50 | 50 | ||
51 | let label = | 51 | let label = |
52 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); |
53 | 53 | ||
54 | let target = attr.syntax().text_range(); | 54 | let target = attr.syntax().text_range(); |
55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { | 55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 146cc75df..0c7d5e355 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -61,7 +61,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; | 61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
62 | acc.add( | 62 | acc.add( |
63 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", inferred_type), | 64 | format!("Insert explicit type `{}`", inferred_type), |
65 | pat_range, | 65 | pat_range, |
66 | |builder| match ascribed_ty { | 66 | |builder| match ascribed_ty { |
67 | Some(ascribed_ty) => { | 67 | Some(ascribed_ty) => { |
@@ -209,7 +209,7 @@ struct Test<K, T = u8> { | |||
209 | } | 209 | } |
210 | 210 | ||
211 | fn main() { | 211 | fn main() { |
212 | let test<|>: Test<i32, u8> = Test { t: 23, k: 33 }; | 212 | let test<|>: Test<i32> = Test { t: 23, k: 33 }; |
213 | }"#, | 213 | }"#, |
214 | ); | 214 | ); |
215 | } | 215 | } |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index c1ce87914..22e1156d2 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -618,4 +618,54 @@ impl Foo for S { | |||
618 | }"#, | 618 | }"#, |
619 | ) | 619 | ) |
620 | } | 620 | } |
621 | |||
622 | #[test] | ||
623 | fn test_generic_single_default_parameter() { | ||
624 | check_assist( | ||
625 | add_missing_impl_members, | ||
626 | r#" | ||
627 | trait Foo<T = Self> { | ||
628 | fn bar(&self, other: &T); | ||
629 | } | ||
630 | |||
631 | struct S; | ||
632 | impl Foo for S { <|> }"#, | ||
633 | r#" | ||
634 | trait Foo<T = Self> { | ||
635 | fn bar(&self, other: &T); | ||
636 | } | ||
637 | |||
638 | struct S; | ||
639 | impl Foo for S { | ||
640 | <|>fn bar(&self, other: &Self) { | ||
641 | todo!() | ||
642 | } | ||
643 | }"#, | ||
644 | ) | ||
645 | } | ||
646 | |||
647 | #[test] | ||
648 | fn test_generic_default_parameter_is_second() { | ||
649 | check_assist( | ||
650 | add_missing_impl_members, | ||
651 | r#" | ||
652 | trait Foo<T1, T2 = Self> { | ||
653 | fn bar(&self, this: &T1, that: &T2); | ||
654 | } | ||
655 | |||
656 | struct S<T>; | ||
657 | impl Foo<T> for S<T> { <|> }"#, | ||
658 | r#" | ||
659 | trait Foo<T1, T2 = Self> { | ||
660 | fn bar(&self, this: &T1, that: &T2); | ||
661 | } | ||
662 | |||
663 | struct S<T>; | ||
664 | impl Foo<T> for S<T> { | ||
665 | <|>fn bar(&self, this: &T, that: &Self) { | ||
666 | todo!() | ||
667 | } | ||
668 | }"#, | ||
669 | ) | ||
670 | } | ||
621 | } | 671 | } |
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 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | 1 | use crate::{AssistContext, AssistId, Assists}; |
2 | 2 | ||
3 | use ast::LoopBodyOwner; | 3 | use ast::{ElseBranch, Expr, LoopBodyOwner}; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | 5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; |
6 | 6 | ||
@@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | 25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | 26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; |
27 | let parent = block.syntax().parent()?; | 27 | let parent = block.syntax().parent()?; |
28 | let assist_id = AssistId("unwrap_block"); | ||
29 | let assist_label = "Unwrap block"; | ||
30 | |||
28 | let (expr, expr_to_unwrap) = match_ast! { | 31 | let (expr, expr_to_unwrap) = match_ast! { |
29 | match parent { | 32 | match parent { |
30 | ast::IfExpr(if_expr) => { | ||
31 | let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); | ||
32 | let expr_to_unwrap = expr_to_unwrap?; | ||
33 | // Find if we are in a else if block | ||
34 | let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); | ||
35 | |||
36 | match ancestor { | ||
37 | None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), | ||
38 | Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), | ||
39 | } | ||
40 | }, | ||
41 | ast::ForExpr(for_expr) => { | 33 | ast::ForExpr(for_expr) => { |
42 | let block_expr = for_expr.loop_body()?; | 34 | let block_expr = for_expr.loop_body()?; |
43 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 35 | 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<()> | |||
53 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 45 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
54 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | 46 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) |
55 | }, | 47 | }, |
48 | ast::IfExpr(if_expr) => { | ||
49 | let mut resp = None; | ||
50 | |||
51 | let then_branch = if_expr.then_branch()?; | ||
52 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
53 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
54 | // For `else if` blocks | ||
55 | let ancestor_then_branch = ancestor.then_branch()?; | ||
56 | let l_curly_token = then_branch.l_curly_token()?; | ||
57 | |||
58 | let target = then_branch.syntax().text_range(); | ||
59 | return acc.add(assist_id, assist_label, target, |edit| { | ||
60 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
61 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
62 | |||
63 | edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); | ||
64 | edit.delete(range_to_del_rest); | ||
65 | edit.delete(range_to_del_else_if); | ||
66 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
67 | }); | ||
68 | } else { | ||
69 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
70 | } | ||
71 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
72 | match else_branch { | ||
73 | ElseBranch::Block(else_block) => { | ||
74 | let l_curly_token = else_block.l_curly_token()?; | ||
75 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
76 | let target = else_block.syntax().text_range(); | ||
77 | return acc.add(assist_id, assist_label, target, |edit| { | ||
78 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
79 | |||
80 | edit.set_cursor(then_branch.syntax().text_range().end()); | ||
81 | edit.delete(range_to_del); | ||
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | ||
83 | }); | ||
84 | } | ||
85 | }, | ||
86 | ElseBranch::IfExpr(_) => {}, | ||
87 | } | ||
88 | } | ||
89 | |||
90 | resp? | ||
91 | }, | ||
56 | _ => return None, | 92 | _ => return None, |
57 | } | 93 | } |
58 | }; | 94 | }; |
59 | 95 | ||
60 | let target = expr_to_unwrap.syntax().text_range(); | 96 | let target = expr_to_unwrap.syntax().text_range(); |
61 | acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { | 97 | acc.add(assist_id, assist_label, target, |edit| { |
62 | edit.set_cursor(expr.syntax().text_range().start()); | 98 | edit.set_cursor(expr.syntax().text_range().start()); |
63 | 99 | ||
64 | let pat_start: &[_] = &[' ', '{', '\n']; | 100 | edit.replace( |
65 | let expr_to_unwrap = expr_to_unwrap.to_string(); | 101 | expr.syntax().text_range(), |
66 | let expr_string = expr_to_unwrap.trim_start_matches(pat_start); | 102 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), |
67 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 103 | ); |
68 | expr_string_lines.pop(); // Delete last line | ||
69 | |||
70 | let expr_string = expr_string_lines | ||
71 | .into_iter() | ||
72 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
73 | .collect::<Vec<String>>() | ||
74 | .join("\n"); | ||
75 | |||
76 | edit.replace(expr.syntax().text_range(), expr_string); | ||
77 | }) | 104 | }) |
78 | } | 105 | } |
79 | 106 | ||
@@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E | |||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
117 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | ||
118 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | ||
119 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | ||
120 | expr_string_lines.pop(); // Delete last line | ||
121 | |||
122 | expr_string_lines | ||
123 | .into_iter() | ||
124 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
125 | .collect::<Vec<String>>() | ||
126 | .join("\n") | ||
127 | } | ||
128 | |||
90 | #[cfg(test)] | 129 | #[cfg(test)] |
91 | mod tests { | 130 | mod tests { |
92 | use crate::tests::{check_assist, check_assist_not_applicable}; | 131 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -142,7 +181,13 @@ mod tests { | |||
142 | r#" | 181 | r#" |
143 | fn main() { | 182 | fn main() { |
144 | bar(); | 183 | bar(); |
145 | <|>println!("bar"); | 184 | if true { |
185 | foo(); | ||
186 | |||
187 | //comment | ||
188 | bar(); | ||
189 | }<|> | ||
190 | println!("bar"); | ||
146 | } | 191 | } |
147 | "#, | 192 | "#, |
148 | ); | 193 | ); |
@@ -170,7 +215,127 @@ mod tests { | |||
170 | r#" | 215 | r#" |
171 | fn main() { | 216 | fn main() { |
172 | //bar(); | 217 | //bar(); |
173 | <|>println!("bar"); | 218 | if true { |
219 | println!("true"); | ||
220 | |||
221 | //comment | ||
222 | //bar(); | ||
223 | }<|> | ||
224 | println!("bar"); | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn simple_if_else_if_nested() { | ||
232 | check_assist( | ||
233 | unwrap_block, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | //bar(); | ||
237 | if true { | ||
238 | println!("true"); | ||
239 | |||
240 | //comment | ||
241 | //bar(); | ||
242 | } else if false { | ||
243 | println!("bar"); | ||
244 | } else if true {<|> | ||
245 | println!("foo"); | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | //bar(); | ||
252 | if true { | ||
253 | println!("true"); | ||
254 | |||
255 | //comment | ||
256 | //bar(); | ||
257 | } else if false { | ||
258 | println!("bar"); | ||
259 | }<|> | ||
260 | println!("foo"); | ||
261 | } | ||
262 | "#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn simple_if_else_if_nested_else() { | ||
268 | check_assist( | ||
269 | unwrap_block, | ||
270 | r#" | ||
271 | fn main() { | ||
272 | //bar(); | ||
273 | if true { | ||
274 | println!("true"); | ||
275 | |||
276 | //comment | ||
277 | //bar(); | ||
278 | } else if false { | ||
279 | println!("bar"); | ||
280 | } else if true { | ||
281 | println!("foo"); | ||
282 | } else {<|> | ||
283 | println!("else"); | ||
284 | } | ||
285 | } | ||
286 | "#, | ||
287 | r#" | ||
288 | fn main() { | ||
289 | //bar(); | ||
290 | if true { | ||
291 | println!("true"); | ||
292 | |||
293 | //comment | ||
294 | //bar(); | ||
295 | } else if false { | ||
296 | println!("bar"); | ||
297 | } else if true { | ||
298 | println!("foo"); | ||
299 | }<|> | ||
300 | println!("else"); | ||
301 | } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn simple_if_else_if_nested_middle() { | ||
308 | check_assist( | ||
309 | unwrap_block, | ||
310 | r#" | ||
311 | fn main() { | ||
312 | //bar(); | ||
313 | if true { | ||
314 | println!("true"); | ||
315 | |||
316 | //comment | ||
317 | //bar(); | ||
318 | } else if false { | ||
319 | println!("bar"); | ||
320 | } else if true {<|> | ||
321 | println!("foo"); | ||
322 | } else { | ||
323 | println!("else"); | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | r#" | ||
328 | fn main() { | ||
329 | //bar(); | ||
330 | if true { | ||
331 | println!("true"); | ||
332 | |||
333 | //comment | ||
334 | //bar(); | ||
335 | } else if false { | ||
336 | println!("bar"); | ||
337 | }<|> | ||
338 | println!("foo"); | ||
174 | } | 339 | } |
175 | "#, | 340 | "#, |
176 | ); | 341 | ); |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 2f15a3f15..f3fc92ebf 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -103,7 +103,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
103 | } | 103 | } |
104 | 104 | ||
105 | #[derive(Clone, Copy)] | 105 | #[derive(Clone, Copy)] |
106 | pub(crate) enum TryEnum { | 106 | pub enum TryEnum { |
107 | Result, | 107 | Result, |
108 | Option, | 108 | Option, |
109 | } | 109 | } |
@@ -111,7 +111,7 @@ pub(crate) enum TryEnum { | |||
111 | impl TryEnum { | 111 | impl TryEnum { |
112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; | 112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; |
113 | 113 | ||
114 | pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { | 114 | pub fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { |
115 | let enum_ = match ty.as_adt() { | 115 | let enum_ = match ty.as_adt() { |
116 | Some(Adt::Enum(it)) => it, | 116 | Some(Adt::Enum(it)) => it, |
117 | _ => return None, | 117 | _ => return None, |