diff options
Diffstat (limited to 'crates/assists')
-rw-r--r-- | crates/assists/src/handlers/generate_from_impl_for_enum.rs | 131 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_getter.rs | 8 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_getter_mut.rs | 8 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_impl.rs | 70 | ||||
-rw-r--r-- | crates/assists/src/handlers/generate_setter.rs | 8 | ||||
-rw-r--r-- | crates/assists/src/handlers/replace_derive_with_manual_impl.rs | 26 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 52 |
8 files changed, 200 insertions, 105 deletions
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs index d9af6ab11..d9388a737 100644 --- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -3,7 +3,7 @@ use ide_db::RootDatabase; | |||
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | 4 | use test_utils::mark; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; |
7 | 7 | ||
8 | // Assist: generate_from_impl_for_enum | 8 | // Assist: generate_from_impl_for_enum |
9 | // | 9 | // |
@@ -18,25 +18,29 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
18 | // | 18 | // |
19 | // impl From<u32> for A { | 19 | // impl From<u32> for A { |
20 | // fn from(v: u32) -> Self { | 20 | // fn from(v: u32) -> Self { |
21 | // A::One(v) | 21 | // Self::One(v) |
22 | // } | 22 | // } |
23 | // } | 23 | // } |
24 | // ``` | 24 | // ``` |
25 | pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 25 | pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | 26 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
27 | let variant_name = variant.name()?; | 27 | let variant_name = variant.name()?; |
28 | let enum_name = variant.parent_enum().name()?; | 28 | let enum_ = ast::Adt::Enum(variant.parent_enum()); |
29 | let field_list = match variant.kind() { | 29 | let (field_name, field_type) = match variant.kind() { |
30 | ast::StructKind::Tuple(field_list) => field_list, | 30 | ast::StructKind::Tuple(field_list) => { |
31 | _ => return None, | 31 | if field_list.fields().count() != 1 { |
32 | }; | 32 | return None; |
33 | if field_list.fields().count() != 1 { | 33 | } |
34 | return None; | 34 | (None, field_list.fields().next()?.ty()?) |
35 | } | 35 | } |
36 | let field_type = field_list.fields().next()?.ty()?; | 36 | ast::StructKind::Record(field_list) => { |
37 | let path = match field_type { | 37 | if field_list.fields().count() != 1 { |
38 | ast::Type::PathType(it) => it, | 38 | return None; |
39 | _ => return None, | 39 | } |
40 | let field = field_list.fields().next()?; | ||
41 | (Some(field.name()?), field.ty()?) | ||
42 | } | ||
43 | ast::StructKind::Unit => return None, | ||
40 | }; | 44 | }; |
41 | 45 | ||
42 | if existing_from_impl(&ctx.sema, &variant).is_some() { | 46 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
@@ -51,19 +55,27 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext | |||
51 | target, | 55 | target, |
52 | |edit| { | 56 | |edit| { |
53 | let start_offset = variant.parent_enum().syntax().text_range().end(); | 57 | let start_offset = variant.parent_enum().syntax().text_range().end(); |
54 | let buf = format!( | 58 | let from_trait = format!("From<{}>", field_type.syntax()); |
55 | r#" | 59 | let impl_code = if let Some(name) = field_name { |
56 | 60 | format!( | |
57 | impl From<{0}> for {1} {{ | 61 | r#" fn from({0}: {1}) -> Self {{ |
58 | fn from(v: {0}) -> Self {{ | 62 | Self::{2} {{ {0} }} |
59 | {1}::{2}(v) | 63 | }}"#, |
60 | }} | 64 | name.text(), |
61 | }}"#, | 65 | field_type.syntax(), |
62 | path.syntax(), | 66 | variant_name, |
63 | enum_name, | 67 | ) |
64 | variant_name | 68 | } else { |
65 | ); | 69 | format!( |
66 | edit.insert(start_offset, buf); | 70 | r#" fn from(v: {}) -> Self {{ |
71 | Self::{}(v) | ||
72 | }}"#, | ||
73 | field_type.syntax(), | ||
74 | variant_name, | ||
75 | ) | ||
76 | }; | ||
77 | let from_impl = generate_trait_impl_text(&enum_, &from_trait, &impl_code); | ||
78 | edit.insert(start_offset, from_impl); | ||
67 | }, | 79 | }, |
68 | ) | 80 | ) |
69 | } | 81 | } |
@@ -106,7 +118,7 @@ mod tests { | |||
106 | 118 | ||
107 | impl From<u32> for A { | 119 | impl From<u32> for A { |
108 | fn from(v: u32) -> Self { | 120 | fn from(v: u32) -> Self { |
109 | A::One(v) | 121 | Self::One(v) |
110 | } | 122 | } |
111 | }"#, | 123 | }"#, |
112 | ); | 124 | ); |
@@ -121,7 +133,7 @@ impl From<u32> for A { | |||
121 | 133 | ||
122 | impl From<foo::bar::baz::Boo> for A { | 134 | impl From<foo::bar::baz::Boo> for A { |
123 | fn from(v: foo::bar::baz::Boo) -> Self { | 135 | fn from(v: foo::bar::baz::Boo) -> Self { |
124 | A::One(v) | 136 | Self::One(v) |
125 | } | 137 | } |
126 | }"#, | 138 | }"#, |
127 | ); | 139 | ); |
@@ -145,7 +157,17 @@ impl From<foo::bar::baz::Boo> for A { | |||
145 | 157 | ||
146 | #[test] | 158 | #[test] |
147 | fn test_add_from_impl_struct_variant() { | 159 | fn test_add_from_impl_struct_variant() { |
148 | check_not_applicable("enum A { $0One { x: u32 } }"); | 160 | check_assist( |
161 | generate_from_impl_for_enum, | ||
162 | "enum A { $0One { x: u32 } }", | ||
163 | r#"enum A { One { x: u32 } } | ||
164 | |||
165 | impl From<u32> for A { | ||
166 | fn from(x: u32) -> Self { | ||
167 | Self::One { x } | ||
168 | } | ||
169 | }"#, | ||
170 | ); | ||
149 | } | 171 | } |
150 | 172 | ||
151 | #[test] | 173 | #[test] |
@@ -157,7 +179,7 @@ enum A { $0One(u32), } | |||
157 | 179 | ||
158 | impl From<u32> for A { | 180 | impl From<u32> for A { |
159 | fn from(v: u32) -> Self { | 181 | fn from(v: u32) -> Self { |
160 | A::One(v) | 182 | Self::One(v) |
161 | } | 183 | } |
162 | } | 184 | } |
163 | "#, | 185 | "#, |
@@ -183,7 +205,7 @@ pub trait From<T> { | |||
183 | 205 | ||
184 | impl From<u32> for A { | 206 | impl From<u32> for A { |
185 | fn from(v: u32) -> Self { | 207 | fn from(v: u32) -> Self { |
186 | A::One(v) | 208 | Self::One(v) |
187 | } | 209 | } |
188 | } | 210 | } |
189 | 211 | ||
@@ -198,4 +220,49 @@ pub trait From<T> { | |||
198 | }"#, | 220 | }"#, |
199 | ); | 221 | ); |
200 | } | 222 | } |
223 | |||
224 | #[test] | ||
225 | fn test_add_from_impl_static_str() { | ||
226 | check_assist( | ||
227 | generate_from_impl_for_enum, | ||
228 | "enum A { $0One(&'static str) }", | ||
229 | r#"enum A { One(&'static str) } | ||
230 | |||
231 | impl From<&'static str> for A { | ||
232 | fn from(v: &'static str) -> Self { | ||
233 | Self::One(v) | ||
234 | } | ||
235 | }"#, | ||
236 | ); | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn test_add_from_impl_generic_enum() { | ||
241 | check_assist( | ||
242 | generate_from_impl_for_enum, | ||
243 | "enum Generic<T, U: Clone> { $0One(T), Two(U) }", | ||
244 | r#"enum Generic<T, U: Clone> { One(T), Two(U) } | ||
245 | |||
246 | impl<T, U: Clone> From<T> for Generic<T, U> { | ||
247 | fn from(v: T) -> Self { | ||
248 | Self::One(v) | ||
249 | } | ||
250 | }"#, | ||
251 | ); | ||
252 | } | ||
253 | |||
254 | #[test] | ||
255 | fn test_add_from_impl_with_lifetime() { | ||
256 | check_assist( | ||
257 | generate_from_impl_for_enum, | ||
258 | "enum Generic<'a> { $0One(&'a i32) }", | ||
259 | r#"enum Generic<'a> { One(&'a i32) } | ||
260 | |||
261 | impl<'a> From<&'a i32> for Generic<'a> { | ||
262 | fn from(v: &'a i32) -> Self { | ||
263 | Self::One(v) | ||
264 | } | ||
265 | }"#, | ||
266 | ); | ||
267 | } | ||
201 | } | 268 | } |
diff --git a/crates/assists/src/handlers/generate_getter.rs b/crates/assists/src/handlers/generate_getter.rs index fbcf8b069..df7d1bb95 100644 --- a/crates/assists/src/handlers/generate_getter.rs +++ b/crates/assists/src/handlers/generate_getter.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | 1 | use stdx::{format_to, to_lower_snake_case}; |
2 | use syntax::ast::VisibilityOwner; | 2 | use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | 3 | ||
5 | use crate::{ | 4 | use crate::{ |
6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, | 5 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 6 | AssistContext, AssistId, AssistKind, Assists, GroupLabel, |
8 | }; | 7 | }; |
9 | 8 | ||
10 | // Assist: generate_getter | 9 | // Assist: generate_getter |
@@ -42,7 +41,8 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
42 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; | 41 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; |
43 | 42 | ||
44 | let target = field.syntax().text_range(); | 43 | let target = field.syntax().text_range(); |
45 | acc.add( | 44 | acc.add_group( |
45 | &GroupLabel("Generate getter/setter".to_owned()), | ||
46 | AssistId("generate_getter", AssistKind::Generate), | 46 | AssistId("generate_getter", AssistKind::Generate), |
47 | "Generate a getter method", | 47 | "Generate a getter method", |
48 | target, | 48 | target, |
diff --git a/crates/assists/src/handlers/generate_getter_mut.rs b/crates/assists/src/handlers/generate_getter_mut.rs index bf0d99881..821c2eed5 100644 --- a/crates/assists/src/handlers/generate_getter_mut.rs +++ b/crates/assists/src/handlers/generate_getter_mut.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | 1 | use stdx::{format_to, to_lower_snake_case}; |
2 | use syntax::ast::VisibilityOwner; | 2 | use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | 3 | ||
5 | use crate::{ | 4 | use crate::{ |
6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, | 5 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 6 | AssistContext, AssistId, AssistKind, Assists, GroupLabel, |
8 | }; | 7 | }; |
9 | 8 | ||
10 | // Assist: generate_getter_mut | 9 | // Assist: generate_getter_mut |
@@ -46,7 +45,8 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
46 | )?; | 45 | )?; |
47 | 46 | ||
48 | let target = field.syntax().text_range(); | 47 | let target = field.syntax().text_range(); |
49 | acc.add( | 48 | acc.add_group( |
49 | &GroupLabel("Generate getter/setter".to_owned()), | ||
50 | AssistId("generate_getter_mut", AssistKind::Generate), | 50 | AssistId("generate_getter_mut", AssistKind::Generate), |
51 | "Generate a mut getter method", | 51 | "Generate a mut getter method", |
52 | target, | 52 | target, |
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs index 61d1bd25c..16a600e6f 100644 --- a/crates/assists/src/handlers/generate_impl.rs +++ b/crates/assists/src/handlers/generate_impl.rs | |||
@@ -1,11 +1,6 @@ | |||
1 | use itertools::Itertools; | 1 | use syntax::ast::{self, AstNode, NameOwner}; |
2 | use stdx::format_to; | ||
3 | use syntax::{ | ||
4 | ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner}, | ||
5 | SmolStr, | ||
6 | }; | ||
7 | 2 | ||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{utils::generate_impl_text, AssistContext, AssistId, AssistKind, Assists}; |
9 | 4 | ||
10 | // Assist: generate_impl | 5 | // Assist: generate_impl |
11 | // | 6 | // |
@@ -36,44 +31,15 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
36 | format!("Generate impl for `{}`", name), | 31 | format!("Generate impl for `{}`", name), |
37 | target, | 32 | target, |
38 | |edit| { | 33 | |edit| { |
39 | let type_params = nominal.generic_param_list(); | ||
40 | let start_offset = nominal.syntax().text_range().end(); | 34 | let start_offset = nominal.syntax().text_range().end(); |
41 | let mut buf = String::new(); | ||
42 | buf.push_str("\n\n"); | ||
43 | nominal | ||
44 | .attrs() | ||
45 | .filter(|attr| { | ||
46 | attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false) | ||
47 | }) | ||
48 | .for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str())); | ||
49 | |||
50 | buf.push_str("impl"); | ||
51 | if let Some(type_params) = &type_params { | ||
52 | format_to!(buf, "{}", type_params.syntax()); | ||
53 | } | ||
54 | buf.push_str(" "); | ||
55 | buf.push_str(name.text()); | ||
56 | if let Some(type_params) = type_params { | ||
57 | let lifetime_params = type_params | ||
58 | .lifetime_params() | ||
59 | .filter_map(|it| it.lifetime()) | ||
60 | .map(|it| SmolStr::from(it.text())); | ||
61 | let type_params = type_params | ||
62 | .type_params() | ||
63 | .filter_map(|it| it.name()) | ||
64 | .map(|it| SmolStr::from(it.text())); | ||
65 | |||
66 | let generic_params = lifetime_params.chain(type_params).format(", "); | ||
67 | format_to!(buf, "<{}>", generic_params) | ||
68 | } | ||
69 | match ctx.config.snippet_cap { | 35 | match ctx.config.snippet_cap { |
70 | Some(cap) => { | 36 | Some(cap) => { |
71 | buf.push_str(" {\n $0\n}"); | 37 | let snippet = generate_impl_text(&nominal, " $0"); |
72 | edit.insert_snippet(cap, start_offset, buf); | 38 | edit.insert_snippet(cap, start_offset, snippet); |
73 | } | 39 | } |
74 | None => { | 40 | None => { |
75 | buf.push_str(" {\n}"); | 41 | let snippet = generate_impl_text(&nominal, ""); |
76 | edit.insert(start_offset, buf); | 42 | edit.insert(start_offset, snippet); |
77 | } | 43 | } |
78 | } | 44 | } |
79 | }, | 45 | }, |
@@ -132,6 +98,30 @@ mod tests { | |||
132 | $0 | 98 | $0 |
133 | }"#, | 99 | }"#, |
134 | ); | 100 | ); |
101 | |||
102 | check_assist( | ||
103 | generate_impl, | ||
104 | r#" | ||
105 | struct Defaulted<T = i32> {}$0"#, | ||
106 | r#" | ||
107 | struct Defaulted<T = i32> {} | ||
108 | |||
109 | impl<T> Defaulted<T> { | ||
110 | $0 | ||
111 | }"#, | ||
112 | ); | ||
113 | |||
114 | check_assist( | ||
115 | generate_impl, | ||
116 | r#" | ||
117 | struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {}$0"#, | ||
118 | r#" | ||
119 | struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {} | ||
120 | |||
121 | impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b> Defaulted<'a, 'b, T> { | ||
122 | $0 | ||
123 | }"#, | ||
124 | ); | ||
135 | } | 125 | } |
136 | 126 | ||
137 | #[test] | 127 | #[test] |
diff --git a/crates/assists/src/handlers/generate_setter.rs b/crates/assists/src/handlers/generate_setter.rs index b655f9b9c..288cf745d 100644 --- a/crates/assists/src/handlers/generate_setter.rs +++ b/crates/assists/src/handlers/generate_setter.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | 1 | use stdx::{format_to, to_lower_snake_case}; |
2 | use syntax::ast::VisibilityOwner; | 2 | use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | 3 | ||
5 | use crate::{ | 4 | use crate::{ |
6 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, | 5 | utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, |
7 | AssistContext, AssistId, AssistKind, Assists, | 6 | AssistContext, AssistId, AssistKind, Assists, GroupLabel, |
8 | }; | 7 | }; |
9 | 8 | ||
10 | // Assist: generate_setter | 9 | // Assist: generate_setter |
@@ -46,7 +45,8 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
46 | )?; | 45 | )?; |
47 | 46 | ||
48 | let target = field.syntax().text_range(); | 47 | let target = field.syntax().text_range(); |
49 | acc.add( | 48 | acc.add_group( |
49 | &GroupLabel("Generate getter/setter".to_owned()), | ||
50 | AssistId("generate_setter", AssistKind::Generate), | 50 | AssistId("generate_setter", AssistKind::Generate), |
51 | "Generate a setter method", | 51 | "Generate a setter method", |
52 | target, | 52 | target, |
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 6aa9d2f2c..c69bc5cac 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -2,8 +2,7 @@ use ide_db::helpers::mod_path_to_ast; | |||
2 | use ide_db::imports_locator; | 2 | use ide_db::imports_locator; |
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | use syntax::{ | 4 | use syntax::{ |
5 | ast::{self, make, AstNode}, | 5 | ast::{self, make, AstNode, NameOwner}, |
6 | Direction, | ||
7 | SyntaxKind::{IDENT, WHITESPACE}, | 6 | SyntaxKind::{IDENT, WHITESPACE}, |
8 | TextSize, | 7 | TextSize, |
9 | }; | 8 | }; |
@@ -11,7 +10,8 @@ use syntax::{ | |||
11 | use crate::{ | 10 | use crate::{ |
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | 11 | assist_context::{AssistBuilder, AssistContext, Assists}, |
13 | utils::{ | 12 | utils::{ |
14 | add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods, | 13 | add_trait_assoc_items_to_impl, filter_assoc_items, generate_trait_impl_text, |
14 | render_snippet, Cursor, DefaultMethods, | ||
15 | }, | 15 | }, |
16 | AssistId, AssistKind, | 16 | AssistId, AssistKind, |
17 | }; | 17 | }; |
@@ -57,8 +57,9 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
57 | let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?; | 57 | let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?; |
58 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | 58 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); |
59 | 59 | ||
60 | let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | 60 | let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; |
61 | let insert_pos = annotated_name.syntax().parent()?.text_range().end(); | 61 | let annotated_name = adt.name()?; |
62 | let insert_pos = adt.syntax().text_range().end(); | ||
62 | 63 | ||
63 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 64 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
64 | let current_crate = current_module.krate(); | 65 | let current_crate = current_module.krate(); |
@@ -82,10 +83,10 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
82 | 83 | ||
83 | let mut no_traits_found = true; | 84 | let mut no_traits_found = true; |
84 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 85 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
85 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?; | 86 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &adt, &annotated_name, insert_pos)?; |
86 | } | 87 | } |
87 | if no_traits_found { | 88 | if no_traits_found { |
88 | add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?; | 89 | add_assist(acc, ctx, &attr, &trait_path, None, &adt, &annotated_name, insert_pos)?; |
89 | } | 90 | } |
90 | Some(()) | 91 | Some(()) |
91 | } | 92 | } |
@@ -96,6 +97,7 @@ fn add_assist( | |||
96 | attr: &ast::Attr, | 97 | attr: &ast::Attr, |
97 | trait_path: &ast::Path, | 98 | trait_path: &ast::Path, |
98 | trait_: Option<hir::Trait>, | 99 | trait_: Option<hir::Trait>, |
100 | adt: &ast::Adt, | ||
99 | annotated_name: &ast::Name, | 101 | annotated_name: &ast::Name, |
100 | insert_pos: TextSize, | 102 | insert_pos: TextSize, |
101 | ) -> Option<()> { | 103 | ) -> Option<()> { |
@@ -112,15 +114,15 @@ fn add_assist( | |||
112 | let impl_def_with_items = | 114 | let impl_def_with_items = |
113 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); | 115 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); |
114 | update_attribute(builder, &input, &trait_name, &attr); | 116 | update_attribute(builder, &input, &trait_name, &attr); |
117 | let trait_path = format!("{}", trait_path); | ||
115 | match (ctx.config.snippet_cap, impl_def_with_items) { | 118 | match (ctx.config.snippet_cap, impl_def_with_items) { |
116 | (None, _) => builder.insert( | 119 | (None, _) => { |
117 | insert_pos, | 120 | builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, "")) |
118 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | 121 | } |
119 | ), | ||
120 | (Some(cap), None) => builder.insert_snippet( | 122 | (Some(cap), None) => builder.insert_snippet( |
121 | cap, | 123 | cap, |
122 | insert_pos, | 124 | insert_pos, |
123 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | 125 | generate_trait_impl_text(adt, &trait_path, " $0"), |
124 | ), | 126 | ), |
125 | (Some(cap), Some((impl_def, first_assoc_item))) => { | 127 | (Some(cap), Some((impl_def, first_assoc_item))) => { |
126 | let mut cursor = Cursor::Before(first_assoc_item.syntax()); | 128 | let mut cursor = Cursor::Before(first_assoc_item.syntax()); |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 6f2b22bc2..0516deaff 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -499,7 +499,7 @@ enum A { One(u32) } | |||
499 | 499 | ||
500 | impl From<u32> for A { | 500 | impl From<u32> for A { |
501 | fn from(v: u32) -> Self { | 501 | fn from(v: u32) -> Self { |
502 | A::One(v) | 502 | Self::One(v) |
503 | } | 503 | } |
504 | } | 504 | } |
505 | "#####, | 505 | "#####, |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 5dd32aef1..8418e6e12 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use ast::TypeBoundsOwner; | ||
5 | use hir::{Adt, HasSource}; | 6 | use hir::{Adt, HasSource}; |
6 | use ide_db::{helpers::SnippetCap, RootDatabase}; | 7 | use ide_db::{helpers::SnippetCap, RootDatabase}; |
7 | use itertools::Itertools; | 8 | use itertools::Itertools; |
@@ -367,21 +368,56 @@ pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Opti | |||
367 | // Generates the surrounding `impl Type { <code> }` including type and lifetime | 368 | // Generates the surrounding `impl Type { <code> }` including type and lifetime |
368 | // parameters | 369 | // parameters |
369 | pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String { | 370 | pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String { |
370 | let type_params = adt.generic_param_list(); | 371 | generate_impl_text_inner(adt, None, code) |
372 | } | ||
373 | |||
374 | // Generates the surrounding `impl <trait> for Type { <code> }` including type | ||
375 | // and lifetime parameters | ||
376 | pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String { | ||
377 | generate_impl_text_inner(adt, Some(trait_text), code) | ||
378 | } | ||
379 | |||
380 | fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String { | ||
381 | let generic_params = adt.generic_param_list(); | ||
371 | let mut buf = String::with_capacity(code.len()); | 382 | let mut buf = String::with_capacity(code.len()); |
372 | buf.push_str("\n\nimpl"); | 383 | buf.push_str("\n\n"); |
373 | if let Some(type_params) = &type_params { | 384 | adt.attrs() |
374 | format_to!(buf, "{}", type_params.syntax()); | 385 | .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)) |
386 | .for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str())); | ||
387 | buf.push_str("impl"); | ||
388 | if let Some(generic_params) = &generic_params { | ||
389 | let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax())); | ||
390 | let type_params = generic_params.type_params().map(|type_param| { | ||
391 | let mut buf = String::new(); | ||
392 | if let Some(it) = type_param.name() { | ||
393 | format_to!(buf, "{}", it.syntax()); | ||
394 | } | ||
395 | if let Some(it) = type_param.colon_token() { | ||
396 | format_to!(buf, "{} ", it); | ||
397 | } | ||
398 | if let Some(it) = type_param.type_bound_list() { | ||
399 | format_to!(buf, "{}", it.syntax()); | ||
400 | } | ||
401 | buf | ||
402 | }); | ||
403 | let generics = lifetimes.chain(type_params).format(", "); | ||
404 | format_to!(buf, "<{}>", generics); | ||
375 | } | 405 | } |
376 | buf.push(' '); | 406 | buf.push(' '); |
407 | if let Some(trait_text) = trait_text { | ||
408 | buf.push_str(trait_text); | ||
409 | buf.push_str(" for "); | ||
410 | } | ||
377 | buf.push_str(adt.name().unwrap().text()); | 411 | buf.push_str(adt.name().unwrap().text()); |
378 | if let Some(type_params) = type_params { | 412 | if let Some(generic_params) = generic_params { |
379 | let lifetime_params = type_params | 413 | let lifetime_params = generic_params |
380 | .lifetime_params() | 414 | .lifetime_params() |
381 | .filter_map(|it| it.lifetime()) | 415 | .filter_map(|it| it.lifetime()) |
382 | .map(|it| SmolStr::from(it.text())); | 416 | .map(|it| SmolStr::from(it.text())); |
383 | let type_params = | 417 | let type_params = generic_params |
384 | type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text())); | 418 | .type_params() |
419 | .filter_map(|it| it.name()) | ||
420 | .map(|it| SmolStr::from(it.text())); | ||
385 | format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) | 421 | format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) |
386 | } | 422 | } |
387 | 423 | ||