11use ide_db:: assists:: GroupLabel ;
2- use itertools:: Itertools ;
32use stdx:: to_lower_snake_case;
4- use syntax:: ast:: HasVisibility ;
5- use syntax:: ast:: { self , AstNode , HasName } ;
3+ use syntax:: {
4+ AstNode , Edition ,
5+ ast:: { self , HasName , HasVisibility , edit:: AstNodeEdit } ,
6+ syntax_editor:: Position ,
7+ } ;
68
79use crate :: {
810 AssistContext , AssistId , Assists ,
9- utils:: { add_method_to_adt , find_struct_impl , is_selected} ,
11+ utils:: { find_struct_impl , generate_impl_with_item , is_selected} ,
1012} ;
1113
1214// Assist: generate_enum_try_into_method
@@ -116,15 +118,6 @@ fn generate_enum_projection_method(
116118 assist_description : & str ,
117119 props : ProjectionProps ,
118120) -> Option < ( ) > {
119- let ProjectionProps {
120- fn_name_prefix,
121- self_param,
122- return_prefix,
123- return_suffix,
124- happy_case,
125- sad_case,
126- } = props;
127-
128121 let variant = ctx. find_node_at_offset :: < ast:: Variant > ( ) ?;
129122 let parent_enum = ast:: Adt :: Enum ( variant. parent_enum ( ) ) ;
130123 let variants = variant
@@ -135,7 +128,7 @@ fn generate_enum_projection_method(
135128 . collect :: < Vec < _ > > ( ) ;
136129 let methods = variants
137130 . iter ( )
138- . map ( |variant| Method :: new ( variant, fn_name_prefix) )
131+ . map ( |variant| Method :: new ( variant, props . fn_name_prefix ) )
139132 . collect :: < Option < Vec < _ > > > ( ) ?;
140133 let fn_names = methods. iter ( ) . map ( |it| it. fn_name . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
141134 stdx:: never!( variants. is_empty( ) ) ;
@@ -151,30 +144,66 @@ fn generate_enum_projection_method(
151144 target,
152145 |builder| {
153146 let vis = parent_enum. visibility ( ) . map_or ( String :: new ( ) , |v| format ! ( "{v} " ) ) ;
147+ let must_use = if ctx. config . assist_emit_must_use { "#[must_use]\n " } else { "" } ;
154148
155- let must_use = if ctx. config . assist_emit_must_use { "#[must_use]\n " } else { "" } ;
156-
157- let method = methods
149+ let fn_items: Vec < ast:: AssocItem > = methods
158150 . iter ( )
159- . map ( |Method { pattern_suffix, field_type, bound_name, fn_name, variant_name } | {
160- format ! (
161- " \
162- {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
163- if let Self::{variant_name}{pattern_suffix} = self {{
164- {happy_case}({bound_name})
165- }} else {{
166- {sad_case}
167- }}
168- }}"
169- )
170- } )
171- . join ( "\n \n " ) ;
151+ . map ( |method| build_fn_item ( method, & vis, must_use, & props) )
152+ . collect ( ) ;
153+
154+ if let Some ( impl_def) = & impl_def {
155+ let editor = builder. make_editor ( impl_def. syntax ( ) ) ;
156+ impl_def. assoc_item_list ( ) . unwrap ( ) . add_items ( & editor, fn_items) ;
157+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
158+ return ;
159+ }
172160
173- add_method_to_adt ( builder, & parent_enum, impl_def, & method) ;
161+ let editor = builder. make_editor ( parent_enum. syntax ( ) ) ;
162+ let make = editor. make ( ) ;
163+ let indent = parent_enum. indent_level ( ) ;
164+ let assoc_list = make. assoc_item_list ( fn_items) ;
165+ let new_impl = generate_impl_with_item ( make, & parent_enum, Some ( assoc_list) ) ;
166+ editor. insert_all (
167+ Position :: after ( parent_enum. syntax ( ) ) ,
168+ vec ! [
169+ make. whitespace( & format!( "\n \n {indent}" ) ) . into( ) ,
170+ new_impl. syntax( ) . clone( ) . into( ) ,
171+ ] ,
172+ ) ;
173+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
174174 } ,
175175 )
176176}
177177
178+ fn build_fn_item (
179+ method : & Method ,
180+ vis : & str ,
181+ must_use : & str ,
182+ props : & ProjectionProps ,
183+ ) -> ast:: AssocItem {
184+ let Method { pattern_suffix, field_type, bound_name, fn_name, variant_name } = method;
185+ let ProjectionProps { self_param, return_prefix, return_suffix, happy_case, sad_case, .. } =
186+ props;
187+ let fn_text = format ! (
188+ "{must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
189+ if let Self::{variant_name}{pattern_suffix} = self {{
190+ {happy_case}({bound_name})
191+ }} else {{
192+ {sad_case}
193+ }}
194+ }}"
195+ ) ;
196+ let wrapped = format ! ( "impl X {{ {fn_text} }}" ) ;
197+ let parse = syntax:: SourceFile :: parse ( & wrapped, Edition :: CURRENT ) ;
198+ let fn_ = parse
199+ . tree ( )
200+ . syntax ( )
201+ . descendants ( )
202+ . find_map ( ast:: Fn :: cast)
203+ . expect ( "fn text must produce a valid fn node" ) ;
204+ ast:: AssocItem :: Fn ( fn_. indent ( 1 . into ( ) ) )
205+ }
206+
178207struct Method {
179208 pattern_suffix : String ,
180209 field_type : ast:: Type ,
@@ -185,6 +214,7 @@ struct Method {
185214
186215impl Method {
187216 fn new ( variant : & ast:: Variant , fn_name_prefix : & str ) -> Option < Self > {
217+ use itertools:: Itertools as _;
188218 let variant_name = variant. name ( ) ?;
189219 let fn_name = format ! ( "{fn_name_prefix}_{}" , & to_lower_snake_case( & variant_name. text( ) ) ) ;
190220
0 commit comments