diff options
-rw-r--r-- | crates/assists/src/handlers/replace_impl_trait_with_generic.rs | 110 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 26 |
2 files changed, 124 insertions, 12 deletions
diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 8af2d16dd..5b0d5d971 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -13,9 +13,6 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
13 | let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; | 13 | let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; |
14 | let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; | 14 | let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; |
15 | 15 | ||
16 | let generic_param_list = | ||
17 | type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None)); | ||
18 | |||
19 | let impl_trait_ty = type_impl_trait | 16 | let impl_trait_ty = type_impl_trait |
20 | .syntax() | 17 | .syntax() |
21 | .descendants() | 18 | .descendants() |
@@ -31,11 +28,16 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
31 | target, | 28 | target, |
32 | |edit| { | 29 | |edit| { |
33 | let generic_letter = impl_trait_ty[..1].to_string(); | 30 | let generic_letter = impl_trait_ty[..1].to_string(); |
34 | edit.replace_ast::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)); | ||
35 | 31 | ||
36 | let new_params = generic_param_list | 32 | let generic_param_list = type_fn |
37 | .append_param(make::generic_param(generic_letter, Some(impl_trait_ty))); | 33 | .generic_param_list() |
38 | let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params); | 34 | .unwrap_or_else(|| make::generic_param_list(None)) |
35 | .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); | ||
36 | |||
37 | let new_type_fn = type_fn | ||
38 | .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) | ||
39 | .with_generic_params(generic_param_list); | ||
40 | |||
39 | edit.replace_ast(type_fn.clone(), new_type_fn); | 41 | edit.replace_ast(type_fn.clone(), new_type_fn); |
40 | }, | 42 | }, |
41 | ) | 43 | ) |
@@ -48,7 +50,7 @@ mod tests { | |||
48 | use crate::tests::check_assist; | 50 | use crate::tests::check_assist; |
49 | 51 | ||
50 | #[test] | 52 | #[test] |
51 | fn replace_with_generic_params() { | 53 | fn replace_impl_trait_with_generic_params() { |
52 | check_assist( | 54 | check_assist( |
53 | replace_impl_trait_with_generic, | 55 | replace_impl_trait_with_generic, |
54 | r#" | 56 | r#" |
@@ -59,4 +61,96 @@ mod tests { | |||
59 | "#, | 61 | "#, |
60 | ); | 62 | ); |
61 | } | 63 | } |
64 | |||
65 | #[test] | ||
66 | fn replace_impl_trait_without_generic_params() { | ||
67 | check_assist( | ||
68 | replace_impl_trait_with_generic, | ||
69 | r#" | ||
70 | fn foo(bar: <|>impl Bar) {} | ||
71 | "#, | ||
72 | r#" | ||
73 | fn foo<B: Bar>(bar: B) {} | ||
74 | "#, | ||
75 | ); | ||
76 | } | ||
77 | |||
78 | #[test] | ||
79 | fn replace_two_impl_trait_with_generic_params() { | ||
80 | check_assist( | ||
81 | replace_impl_trait_with_generic, | ||
82 | r#" | ||
83 | fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {} | ||
84 | "#, | ||
85 | r#" | ||
86 | fn foo<G, B: Bar>(foo: impl Foo, bar: B) {} | ||
87 | "#, | ||
88 | ); | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn replace_impl_trait_with_empty_generic_params() { | ||
93 | check_assist( | ||
94 | replace_impl_trait_with_generic, | ||
95 | r#" | ||
96 | fn foo<>(bar: <|>impl Bar) {} | ||
97 | "#, | ||
98 | r#" | ||
99 | fn foo<B: Bar>(bar: B) {} | ||
100 | "#, | ||
101 | ); | ||
102 | } | ||
103 | |||
104 | #[test] | ||
105 | fn replace_impl_trait_with_empty_multiline_generic_params() { | ||
106 | // FIXME: It would be more correct to place the generic parameter | ||
107 | // on the next line after the left angle. | ||
108 | check_assist( | ||
109 | replace_impl_trait_with_generic, | ||
110 | r#" | ||
111 | fn foo< | ||
112 | >(bar: <|>impl Bar) {} | ||
113 | "#, | ||
114 | r#" | ||
115 | fn foo<B: Bar, | ||
116 | >(bar: B) {} | ||
117 | "#, | ||
118 | ); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | #[ignore = "This case is very rare but there is no simple solutions to fix it."] | ||
123 | fn replace_impl_trait_with_exist_generic_letter() { | ||
124 | check_assist( | ||
125 | replace_impl_trait_with_generic, | ||
126 | r#" | ||
127 | fn foo<B>(bar: <|>impl Bar) {} | ||
128 | "#, | ||
129 | r#" | ||
130 | fn foo<B, C: Bar>(bar: C) {} | ||
131 | "#, | ||
132 | ); | ||
133 | } | ||
134 | |||
135 | #[test] | ||
136 | fn replace_impl_trait_with_multiline_generic_params() { | ||
137 | check_assist( | ||
138 | replace_impl_trait_with_generic, | ||
139 | r#" | ||
140 | fn foo< | ||
141 | G: Foo, | ||
142 | F, | ||
143 | H, | ||
144 | >(bar: <|>impl Bar) {} | ||
145 | "#, | ||
146 | r#" | ||
147 | fn foo< | ||
148 | G: Foo, | ||
149 | F, | ||
150 | H, | ||
151 | B: Bar, | ||
152 | >(bar: B) {} | ||
153 | "#, | ||
154 | ); | ||
155 | } | ||
62 | } | 156 | } |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 1ccb4de6a..68987dbf6 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | ast::{ | 13 | ast::{ |
14 | self, | 14 | self, |
15 | make::{self, tokens}, | 15 | make::{self, tokens}, |
16 | AstNode, TypeBoundsOwner, | 16 | AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, |
17 | }, | 17 | }, |
18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, | 18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, |
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
@@ -46,6 +46,19 @@ impl ast::Fn { | |||
46 | to_insert.push(body.syntax().clone().into()); | 46 | to_insert.push(body.syntax().clone().into()); |
47 | self.replace_children(single_node(old_body_or_semi), to_insert) | 47 | self.replace_children(single_node(old_body_or_semi), to_insert) |
48 | } | 48 | } |
49 | |||
50 | #[must_use] | ||
51 | pub fn with_generic_params(&self, generic_args: ast::GenericParamList) -> ast::Fn { | ||
52 | if let Some(old) = self.generic_param_list() { | ||
53 | return self.replace_descendant(old, generic_args); | ||
54 | } | ||
55 | |||
56 | let anchor = self.name().expect("The function must have a name").syntax().clone(); | ||
57 | |||
58 | let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new(); | ||
59 | to_insert.push(generic_args.syntax().clone().into()); | ||
60 | self.insert_children(InsertPosition::After(anchor.into()), to_insert) | ||
61 | } | ||
49 | } | 62 | } |
50 | 63 | ||
51 | fn make_multiline<N>(node: N) -> N | 64 | fn make_multiline<N>(node: N) -> N |
@@ -461,14 +474,17 @@ impl ast::MatchArmList { | |||
461 | 474 | ||
462 | impl ast::GenericParamList { | 475 | impl ast::GenericParamList { |
463 | #[must_use] | 476 | #[must_use] |
464 | pub fn append_params(&self, params: impl IntoIterator<Item = ast::GenericParam>) -> Self { | 477 | pub fn append_params( |
478 | &self, | ||
479 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
480 | ) -> ast::GenericParamList { | ||
465 | let mut res = self.clone(); | 481 | let mut res = self.clone(); |
466 | params.into_iter().for_each(|it| res = res.append_param(it)); | 482 | params.into_iter().for_each(|it| res = res.append_param(it)); |
467 | res | 483 | res |
468 | } | 484 | } |
469 | 485 | ||
470 | #[must_use] | 486 | #[must_use] |
471 | pub fn append_param(&self, item: ast::GenericParam) -> Self { | 487 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { |
472 | let is_multiline = self.syntax().text().contains_char('\n'); | 488 | let is_multiline = self.syntax().text().contains_char('\n'); |
473 | let ws; | 489 | let ws; |
474 | let space = if is_multiline { | 490 | let space = if is_multiline { |
@@ -482,7 +498,9 @@ impl ast::GenericParamList { | |||
482 | }; | 498 | }; |
483 | 499 | ||
484 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | 500 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); |
485 | to_insert.push(space.into()); | 501 | if self.generic_params().next().is_some() { |
502 | to_insert.push(space.into()); | ||
503 | } | ||
486 | to_insert.push(item.syntax().clone().into()); | 504 | to_insert.push(item.syntax().clone().into()); |
487 | to_insert.push(make::token(T![,]).into()); | 505 | to_insert.push(make::token(T![,]).into()); |
488 | 506 | ||