11use ide_db:: { famous_defs:: FamousDefs , source_change:: SourceChangeBuilder } ;
22use stdx:: { format_to, to_lower_snake_case} ;
33use syntax:: {
4- ast:: { self , AstNode , HasName , HasVisibility } ,
5- TextRange ,
4+ ast:: { self , edit_in_place :: Indent , make , AstNode , HasName , HasVisibility } ,
5+ ted , TextRange ,
66} ;
77
88use crate :: {
9- utils:: { convert_reference_type, find_impl_block_end , find_struct_impl, generate_impl_text } ,
9+ utils:: { convert_reference_type, find_struct_impl, generate_impl } ,
1010 AssistContext , AssistId , AssistKind , Assists , GroupLabel ,
1111} ;
1212
@@ -214,14 +214,14 @@ fn generate_getter_from_info(
214214 ctx : & AssistContext < ' _ > ,
215215 info : & AssistInfo ,
216216 record_field_info : & RecordFieldInfo ,
217- ) -> String {
218- let mut buf = String :: with_capacity ( 512 ) ;
219-
220- let vis = info. strukt . visibility ( ) . map_or ( String :: new ( ) , |v| format ! ( "{v} " ) ) ;
217+ ) -> ast:: Fn {
221218 let ( ty, body) = if matches ! ( info. assist_type, AssistType :: MutGet ) {
222219 (
223- format ! ( "&mut {}" , record_field_info. field_ty) ,
224- format ! ( "&mut self.{}" , record_field_info. field_name) ,
220+ make:: ty_ref ( record_field_info. field_ty . clone ( ) , true ) ,
221+ make:: expr_ref (
222+ make:: expr_field ( make:: ext:: expr_self ( ) , & record_field_info. field_name . text ( ) ) ,
223+ true ,
224+ ) ,
225225 )
226226 } else {
227227 ( || {
@@ -240,41 +240,52 @@ fn generate_getter_from_info(
240240 } ) ( )
241241 . unwrap_or_else ( || {
242242 (
243- format ! ( "&{}" , record_field_info. field_ty) ,
244- format ! ( "&self.{}" , record_field_info. field_name) ,
243+ make:: ty_ref ( record_field_info. field_ty . clone ( ) , false ) ,
244+ make:: expr_ref (
245+ make:: expr_field ( make:: ext:: expr_self ( ) , & record_field_info. field_name . text ( ) ) ,
246+ false ,
247+ ) ,
245248 )
246249 } )
247250 } ;
248251
249- format_to ! (
250- buf,
251- " {}fn {}(&{}self) -> {} {{
252- {}
253- }}" ,
254- vis,
255- record_field_info. fn_name,
256- matches!( info. assist_type, AssistType :: MutGet ) . then_some( "mut " ) . unwrap_or_default( ) ,
257- ty,
258- body,
259- ) ;
252+ let self_param = if matches ! ( info. assist_type, AssistType :: MutGet ) {
253+ make:: mut_self_param ( )
254+ } else {
255+ make:: self_param ( )
256+ } ;
260257
261- buf
258+ let strukt = & info. strukt ;
259+ let fn_name = make:: name ( & record_field_info. fn_name ) ;
260+ let params = make:: param_list ( Some ( self_param) , [ ] ) ;
261+ let ret_type = Some ( make:: ret_type ( ty) ) ;
262+ let body = make:: block_expr ( [ ] , Some ( body) ) ;
263+
264+ make:: fn_ ( strukt. visibility ( ) , fn_name, None , None , params, body, ret_type, false , false , false )
262265}
263266
264- fn generate_setter_from_info ( info : & AssistInfo , record_field_info : & RecordFieldInfo ) -> String {
265- let mut buf = String :: with_capacity ( 512 ) ;
267+ fn generate_setter_from_info ( info : & AssistInfo , record_field_info : & RecordFieldInfo ) -> ast:: Fn {
266268 let strukt = & info. strukt ;
267- let fn_name = & record_field_info. fn_name ;
269+ let field_name = & record_field_info. fn_name ;
270+ let fn_name = make:: name ( & format ! ( "set_{field_name}" ) ) ;
268271 let field_ty = & record_field_info. field_ty ;
269- let vis = strukt. visibility ( ) . map_or ( String :: new ( ) , |v| format ! ( "{v} " ) ) ;
270- format_to ! (
271- buf,
272- " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{
273- self.{fn_name} = {fn_name};
274- }}"
275- ) ;
276272
277- buf
273+ // Make the param list
274+ // `(&mut self, $field_name: $field_ty)`
275+ let field_param =
276+ make:: param ( make:: ident_pat ( false , false , make:: name ( field_name) ) . into ( ) , field_ty. clone ( ) ) ;
277+ let params = make:: param_list ( Some ( make:: mut_self_param ( ) ) , [ field_param] ) ;
278+
279+ // Make the assignment body
280+ // `self.$field_name = $field_name`
281+ let self_expr = make:: ext:: expr_self ( ) ;
282+ let lhs = make:: expr_field ( self_expr, field_name) ;
283+ let rhs = make:: expr_path ( make:: ext:: ident_path ( field_name) ) ;
284+ let assign_stmt = make:: expr_stmt ( make:: expr_assignment ( lhs, rhs) ) ;
285+ let body = make:: block_expr ( [ assign_stmt. into ( ) ] , None ) ;
286+
287+ // Make the setter fn
288+ make:: fn_ ( strukt. visibility ( ) , fn_name, None , None , params, body, None , false , false , false )
278289}
279290
280291fn extract_and_parse (
@@ -367,74 +378,45 @@ fn build_source_change(
367378) {
368379 let record_fields_count = info_of_record_fields. len ( ) ;
369380
370- let mut buf = String :: with_capacity ( 512 ) ;
381+ let impl_def = if let Some ( impl_def) = & assist_info. impl_def {
382+ // We have an existing impl to add to
383+ builder. make_mut ( impl_def. clone ( ) )
384+ } else {
385+ // Generate a new impl to add the methods to
386+ let impl_def = generate_impl ( & ast:: Adt :: Struct ( assist_info. strukt . clone ( ) ) ) ;
371387
372- // Check if an impl exists
373- if let Some ( impl_def) = & assist_info. impl_def {
374- // Check if impl is empty
375- if let Some ( assoc_item_list) = impl_def. assoc_item_list ( ) {
376- if assoc_item_list. assoc_items ( ) . next ( ) . is_some ( ) {
377- // If not empty then only insert a new line
378- buf. push ( '\n' ) ;
379- }
380- }
381- }
388+ // Insert it after the adt
389+ let strukt = builder. make_mut ( assist_info. strukt . clone ( ) ) ;
390+
391+ ted:: insert_all_raw (
392+ ted:: Position :: after ( strukt. syntax ( ) ) ,
393+ vec ! [ make:: tokens:: blank_line( ) . into( ) , impl_def. syntax( ) . clone( ) . into( ) ] ,
394+ ) ;
395+
396+ impl_def
397+ } ;
398+
399+ let assoc_item_list = impl_def. get_or_create_assoc_item_list ( ) ;
382400
383401 for ( i, record_field_info) in info_of_record_fields. iter ( ) . enumerate ( ) {
384- // this buf inserts a newline at the end of a getter
385- // automatically, if one wants to add one more newline
386- // for separating it from other assoc items, that needs
387- // to be handled separately
388- let mut getter_buf = match assist_info. assist_type {
402+ // Make the new getter or setter fn
403+ let new_fn = match assist_info. assist_type {
389404 AssistType :: Set => generate_setter_from_info ( & assist_info, record_field_info) ,
390405 _ => generate_getter_from_info ( ctx, & assist_info, record_field_info) ,
391- } ;
392-
393- // Insert `$0` only for last getter we generate
394- if i == record_fields_count - 1 && ctx. config . snippet_cap . is_some ( ) {
395- getter_buf = getter_buf. replacen ( "fn " , "fn $0" , 1 ) ;
396406 }
397-
398- // For first element we do not merge with '\n', as
399- // that can be inserted by impl_def check defined
400- // above, for other cases which are:
401- //
402- // - impl exists but it empty, here we would ideally
403- // not want to keep newline between impl <struct> {
404- // and fn <fn-name>() { line
405- //
406- // - next if impl itself does not exist, in this
407- // case we ourselves generate a new impl and that
408- // again ends up with the same reasoning as above
409- // for not keeping newline
410- if i == 0 {
411- buf = buf + & getter_buf;
412- } else {
413- buf = buf + "\n " + & getter_buf;
414- }
415-
416- // We don't insert a new line at the end of
417- // last getter as it will end up in the end
418- // of an impl where we would not like to keep
419- // getter and end of impl ( i.e. `}` ) with an
420- // extra line for no reason
421- if i < record_fields_count - 1 {
422- buf += "\n " ;
407+ . clone_for_update ( ) ;
408+ new_fn. indent ( 1 . into ( ) ) ;
409+
410+ // Insert a tabstop only for last method we generate
411+ if i == record_fields_count - 1 {
412+ if let Some ( cap) = ctx. config . snippet_cap {
413+ if let Some ( name) = new_fn. name ( ) {
414+ builder. add_tabstop_before ( cap, name) ;
415+ }
416+ }
423417 }
424- }
425-
426- let start_offset = assist_info
427- . impl_def
428- . as_ref ( )
429- . and_then ( |impl_def| find_impl_block_end ( impl_def. to_owned ( ) , & mut buf) )
430- . unwrap_or_else ( || {
431- buf = generate_impl_text ( & ast:: Adt :: Struct ( assist_info. strukt . clone ( ) ) , & buf) ;
432- assist_info. strukt . syntax ( ) . text_range ( ) . end ( )
433- } ) ;
434418
435- match ctx. config . snippet_cap {
436- Some ( cap) => builder. insert_snippet ( cap, start_offset, buf) ,
437- None => builder. insert ( start_offset, buf) ,
419+ assoc_item_list. add_item ( new_fn. clone ( ) . into ( ) ) ;
438420 }
439421}
440422
0 commit comments