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 ( || {
@@ -233,48 +233,61 @@ fn generate_getter_from_info(
233233 . map ( |conversion| {
234234 cov_mark:: hit!( convert_reference_type) ;
235235 (
236- conversion. convert_type ( ctx. db ( ) ) . to_string ( ) ,
237- conversion. getter ( record_field_info. field_name . to_string ( ) ) . to_string ( ) ,
236+ conversion. convert_type ( ctx. db ( ) ) ,
237+ conversion. getter ( record_field_info. field_name . to_string ( ) ) ,
238238 )
239239 } )
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- }}"
272+
273+ // Make the param list
274+ // `(&mut self, $field_name: $field_ty)`
275+ let field_param = make :: param (
276+ make :: ident_pat ( false , false , make :: name ( & field_name ) ) . into ( ) ,
277+ field_ty . clone ( ) ,
275278 ) ;
279+ let params = make:: param_list ( Some ( make:: mut_self_param ( ) ) , [ field_param] ) ;
276280
277- buf
281+ // Make the assignment body
282+ // `self.$field_name = $field_name`
283+ let self_expr = make:: ext:: expr_self ( ) ;
284+ let lhs = make:: expr_field ( self_expr, & field_name) ;
285+ let rhs = make:: expr_path ( make:: ext:: ident_path ( & field_name) ) ;
286+ let assign_stmt = make:: expr_stmt ( make:: expr_assignment ( lhs, rhs) ) ;
287+ let body = make:: block_expr ( [ assign_stmt. into ( ) ] , None ) ;
288+
289+ // Make the setter fn
290+ make:: fn_ ( strukt. visibility ( ) , fn_name, None , None , params, body, None , false , false , false )
278291}
279292
280293fn extract_and_parse (
@@ -367,74 +380,45 @@ fn build_source_change(
367380) {
368381 let record_fields_count = info_of_record_fields. len ( ) ;
369382
370- let mut buf = String :: with_capacity ( 512 ) ;
383+ let impl_def = if let Some ( impl_def) = & assist_info. impl_def {
384+ // We have an existing impl to add to
385+ builder. make_mut ( impl_def. clone ( ) )
386+ } else {
387+ // Generate a new impl to add the methods to
388+ let impl_def = generate_impl ( & ast:: Adt :: Struct ( assist_info. strukt . clone ( ) ) ) ;
371389
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- }
390+ // Insert it after the adt
391+ let strukt = builder. make_mut ( assist_info. strukt . clone ( ) ) ;
392+
393+ ted:: insert_all_raw (
394+ ted:: Position :: after ( strukt. syntax ( ) ) ,
395+ vec ! [ make:: tokens:: blank_line( ) . into( ) , impl_def. syntax( ) . clone( ) . into( ) ] ,
396+ ) ;
397+
398+ impl_def
399+ } ;
400+
401+ let assoc_item_list = impl_def. get_or_create_assoc_item_list ( ) ;
382402
383403 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 {
404+ // Make the new getter or setter fn
405+ let new_fn = match assist_info. assist_type {
389406 AssistType :: Set => generate_setter_from_info ( & assist_info, record_field_info) ,
390407 _ => 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 ) ;
396408 }
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 " ;
409+ . clone_for_update ( ) ;
410+ new_fn. indent ( 1 . into ( ) ) ;
411+
412+ // Insert a tabstop only for last method we generate
413+ if i == record_fields_count - 1 {
414+ if let Some ( cap) = ctx. config . snippet_cap {
415+ if let Some ( name) = new_fn. name ( ) {
416+ builder. add_tabstop_before ( cap, name) ;
417+ }
418+ }
423419 }
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- } ) ;
434420
435- match ctx. config . snippet_cap {
436- Some ( cap) => builder. insert_snippet ( cap, start_offset, buf) ,
437- None => builder. insert ( start_offset, buf) ,
421+ assoc_item_list. add_item ( new_fn. clone ( ) . into ( ) ) ;
438422 }
439423}
440424
0 commit comments