@@ -2,7 +2,10 @@ use hir::next_solver::{DbInterner, TypingMode};
22use ide_db:: { RootDatabase , famous_defs:: FamousDefs } ;
33use syntax:: ast:: { self , AstNode , HasName } ;
44
5- use crate :: { AssistContext , AssistId , Assists , utils:: generate_trait_impl_text_intransitive} ;
5+ use crate :: {
6+ AssistContext , AssistId , Assists ,
7+ utils:: { generate_trait_impl_text_intransitive, is_selected} ,
8+ } ;
69
710// Assist: generate_from_impl_for_enum
811//
@@ -26,8 +29,68 @@ pub(crate) fn generate_from_impl_for_enum(
2629 ctx : & AssistContext < ' _ > ,
2730) -> Option < ( ) > {
2831 let variant = ctx. find_node_at_offset :: < ast:: Variant > ( ) ?;
29- let variant_name = variant. name ( ) ?;
30- let enum_ = ast:: Adt :: Enum ( variant. parent_enum ( ) ) ;
32+ let adt = ast:: Adt :: Enum ( variant. parent_enum ( ) ) ;
33+ let variants = selected_variants ( ctx, & variant) ?;
34+
35+ let target = variant. syntax ( ) . text_range ( ) ;
36+ acc. add (
37+ AssistId :: generate ( "generate_from_impl_for_enum" ) ,
38+ "Generate `From` impl for this enum variant(s)" ,
39+ target,
40+ |edit| {
41+ let start_offset = variant. parent_enum ( ) . syntax ( ) . text_range ( ) . end ( ) ;
42+ let from_impl = variants
43+ . into_iter ( )
44+ . map ( |variant_info| {
45+ let from_trait = format ! ( "From<{}>" , variant_info. ty) ;
46+ let impl_code = generate_impl_code ( variant_info) ;
47+ generate_trait_impl_text_intransitive ( & adt, & from_trait, & impl_code)
48+ } )
49+ . collect :: < String > ( ) ;
50+ edit. insert ( start_offset, from_impl) ;
51+ } ,
52+ )
53+ }
54+
55+ fn generate_impl_code ( VariantInfo { name, field_name, ty } : VariantInfo ) -> String {
56+ if let Some ( field) = field_name {
57+ format ! (
58+ r#" fn from({field}: {ty}) -> Self {{
59+ Self::{name} {{ {field} }}
60+ }}"#
61+ )
62+ } else {
63+ format ! (
64+ r#" fn from(v: {ty}) -> Self {{
65+ Self::{name}(v)
66+ }}"#
67+ )
68+ }
69+ }
70+
71+ struct VariantInfo {
72+ name : ast:: Name ,
73+ field_name : Option < ast:: Name > ,
74+ ty : ast:: Type ,
75+ }
76+
77+ fn selected_variants ( ctx : & AssistContext < ' _ > , variant : & ast:: Variant ) -> Option < Vec < VariantInfo > > {
78+ variant
79+ . parent_enum ( )
80+ . variant_list ( ) ?
81+ . variants ( )
82+ . filter ( |it| is_selected ( it, ctx. selection_trimmed ( ) , true ) )
83+ . map ( |variant| {
84+ let ( name, ty) = extract_variant_info ( & ctx. sema , & variant) ?;
85+ Some ( VariantInfo { name : variant. name ( ) ?, field_name : name, ty } )
86+ } )
87+ . collect ( )
88+ }
89+
90+ fn extract_variant_info (
91+ sema : & ' _ hir:: Semantics < ' _ , RootDatabase > ,
92+ variant : & ast:: Variant ,
93+ ) -> Option < ( Option < ast:: Name > , ast:: Type ) > {
3194 let ( field_name, field_type) = match variant. kind ( ) {
3295 ast:: StructKind :: Tuple ( field_list) => {
3396 if field_list. fields ( ) . count ( ) != 1 {
@@ -45,36 +108,11 @@ pub(crate) fn generate_from_impl_for_enum(
45108 ast:: StructKind :: Unit => return None ,
46109 } ;
47110
48- if existing_from_impl ( & ctx . sema , & variant) . is_some ( ) {
111+ if existing_from_impl ( sema, variant) . is_some ( ) {
49112 cov_mark:: hit!( test_add_from_impl_already_exists) ;
50113 return None ;
51114 }
52-
53- let target = variant. syntax ( ) . text_range ( ) ;
54- acc. add (
55- AssistId :: generate ( "generate_from_impl_for_enum" ) ,
56- "Generate `From` impl for this enum variant" ,
57- target,
58- |edit| {
59- let start_offset = variant. parent_enum ( ) . syntax ( ) . text_range ( ) . end ( ) ;
60- let from_trait = format ! ( "From<{field_type}>" ) ;
61- let impl_code = if let Some ( name) = field_name {
62- format ! (
63- r#" fn from({name}: {field_type}) -> Self {{
64- Self::{variant_name} {{ {name} }}
65- }}"#
66- )
67- } else {
68- format ! (
69- r#" fn from(v: {field_type}) -> Self {{
70- Self::{variant_name}(v)
71- }}"#
72- )
73- } ;
74- let from_impl = generate_trait_impl_text_intransitive ( & enum_, & from_trait, & impl_code) ;
75- edit. insert ( start_offset, from_impl) ;
76- } ,
77- )
115+ Some ( ( field_name, field_type) )
78116}
79117
80118fn existing_from_impl (
@@ -123,6 +161,32 @@ impl From<u32> for A {
123161 ) ;
124162 }
125163
164+ #[ test]
165+ fn test_generate_from_impl_for_multiple_enum_variants ( ) {
166+ check_assist (
167+ generate_from_impl_for_enum,
168+ r#"
169+ //- minicore: from
170+ enum A { $0Foo(u32), Bar$0(i32) }
171+ "# ,
172+ r#"
173+ enum A { Foo(u32), Bar(i32) }
174+
175+ impl From<u32> for A {
176+ fn from(v: u32) -> Self {
177+ Self::Foo(v)
178+ }
179+ }
180+
181+ impl From<i32> for A {
182+ fn from(v: i32) -> Self {
183+ Self::Bar(v)
184+ }
185+ }
186+ "# ,
187+ ) ;
188+ }
189+
126190 // FIXME(next-solver): it would be nice to not be *required* to resolve the
127191 // path in order to properly generate assists
128192 #[ test]
0 commit comments