@@ -6,9 +6,9 @@ use syntax::{
66 ast:: {
77 self ,
88 edit:: { AstNodeEdit , IndentLevel } ,
9- make, ArgListOwner , AstNode , ModuleItemOwner ,
9+ make, ArgListOwner , AstNode , CallExpr , ModuleItemOwner ,
1010 } ,
11- SyntaxKind , SyntaxNode , TextSize ,
11+ SyntaxKind , SyntaxNode , TextRange , TextSize ,
1212} ;
1313
1414use crate :: {
@@ -85,31 +85,19 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
8585 None => None ,
8686 } ;
8787
88- let function_builder = FunctionBuilder :: from_call ( ctx, & call, & path, target_module) ?;
88+ let ( target, file, insert_offset) = get_fn_target ( ctx, & target_module, call. clone ( ) ) ?;
89+ let function_builder = FunctionBuilder :: from_call ( ctx, & call, & path, target_module, target) ?;
8990 let target = call. syntax ( ) . text_range ( ) ;
90-
91- acc. add (
92- AssistId ( "generate_function" , AssistKind :: Generate ) ,
93- format ! ( "Generate `{}` function" , function_builder. fn_name) ,
94- target,
95- |builder| {
96- let function_template = function_builder. render ( ) ;
97- builder. edit_file ( function_template. file ) ;
98- let new_fn = function_template. to_string ( ctx. config . snippet_cap ) ;
99- match ctx. config . snippet_cap {
100- Some ( cap) => builder. insert_snippet ( cap, function_template. insert_offset , new_fn) ,
101- None => builder. insert ( function_template. insert_offset , new_fn) ,
102- }
103- } ,
104- )
91+ let label = format ! ( "Generate {} function" , function_builder. fn_name. clone( ) ) ;
92+ add_func_to_accumulator ( acc, ctx, target, function_builder, insert_offset, file, None , label)
10593}
10694
10795fn gen_method ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
10896 let call: ast:: MethodCallExpr = ctx. find_node_at_offset ( ) ?;
10997 let fn_name = call. name_ref ( ) ?;
11098 let adt = ctx. sema . type_of_expr ( & call. receiver ( ) ?) ?. original ( ) . strip_references ( ) . as_adt ( ) ?;
11199
112- let current_module = ctx . sema . scope ( call. syntax ( ) ) . module ( ) ?;
100+ let current_module = current_module ( call. syntax ( ) , ctx ) ?;
113101 let target_module = adt. module ( ctx. sema . db ) ;
114102
115103 if current_module. krate ( ) != target_module. krate ( ) {
@@ -122,44 +110,58 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
122110 ctx. sema . find_node_at_offset_with_macros ( file. syntax ( ) , range. range . start ( ) ) ?;
123111 let impl_ = find_struct_impl ( ctx, & adt_source, fn_name. text ( ) . as_str ( ) ) ?;
124112
125- let function_builder = FunctionBuilder :: from_method_call (
113+ let ( target, insert_offset) = get_method_target ( ctx, & target_module, & impl_) ?;
114+ let function_builder =
115+ FunctionBuilder :: from_method_call ( ctx, & call, & fn_name, target_module, target) ?;
116+ let text_range = call. syntax ( ) . text_range ( ) ;
117+ let adt_name = if impl_. is_none ( ) { Some ( adt. name ( ctx. sema . db ) ) } else { None } ;
118+ let label = format ! ( "Generate {} method" , function_builder. fn_name. clone( ) ) ;
119+ add_func_to_accumulator (
120+ acc,
126121 ctx,
127- & call ,
128- & fn_name ,
129- & impl_ ,
122+ text_range ,
123+ function_builder ,
124+ insert_offset ,
130125 range. file_id ,
131- target_module,
132- current_module,
133- ) ?;
134- let target = call. syntax ( ) . text_range ( ) ;
135-
136- acc. add (
137- AssistId ( "generate_function" , AssistKind :: Generate ) ,
138- format ! ( "Generate `{}` method" , function_builder. fn_name) ,
139- target,
140- |builder| {
141- let function_template = function_builder. render ( ) ;
142- builder. edit_file ( function_template. file ) ;
143- let mut new_fn = function_template. to_string ( ctx. config . snippet_cap ) ;
144- if impl_. is_none ( ) {
145- new_fn = format ! ( "\n impl {} {{\n {}\n }}" , adt. name( ctx. sema. db) , new_fn, ) ;
146- }
147- match ctx. config . snippet_cap {
148- Some ( cap) => builder. insert_snippet ( cap, function_template. insert_offset , new_fn) ,
149- None => builder. insert ( function_template. insert_offset , new_fn) ,
150- }
151- } ,
126+ adt_name,
127+ label,
152128 )
153129}
154130
155- struct FunctionTemplate {
131+ fn add_func_to_accumulator (
132+ acc : & mut Assists ,
133+ ctx : & AssistContext ,
134+ text_range : TextRange ,
135+ function_builder : FunctionBuilder ,
156136 insert_offset : TextSize ,
137+ file : FileId ,
138+ adt_name : Option < hir:: Name > ,
139+ label : String ,
140+ ) -> Option < ( ) > {
141+ acc. add ( AssistId ( "generate_function" , AssistKind :: Generate ) , label, text_range, |builder| {
142+ let function_template = function_builder. render ( ) ;
143+ let mut func = function_template. to_string ( ctx. config . snippet_cap ) ;
144+ if let Some ( name) = adt_name {
145+ func = format ! ( "\n impl {} {{\n {}\n }}" , name, func) ;
146+ }
147+ builder. edit_file ( file) ;
148+ match ctx. config . snippet_cap {
149+ Some ( cap) => builder. insert_snippet ( cap, insert_offset, func) ,
150+ None => builder. insert ( insert_offset, func) ,
151+ }
152+ } )
153+ }
154+
155+ fn current_module ( current_node : & SyntaxNode , ctx : & AssistContext ) -> Option < Module > {
156+ ctx. sema . scope ( current_node) . module ( )
157+ }
158+
159+ struct FunctionTemplate {
157160 leading_ws : String ,
158161 fn_def : ast:: Fn ,
159162 ret_type : Option < ast:: RetType > ,
160163 should_focus_return_type : bool ,
161164 trailing_ws : String ,
162- file : FileId ,
163165 tail_expr : ast:: Expr ,
164166}
165167
@@ -193,7 +195,6 @@ struct FunctionBuilder {
193195 params : ast:: ParamList ,
194196 ret_type : Option < ast:: RetType > ,
195197 should_focus_return_type : bool ,
196- file : FileId ,
197198 needs_pub : bool ,
198199 is_async : bool ,
199200}
@@ -206,19 +207,10 @@ impl FunctionBuilder {
206207 call : & ast:: CallExpr ,
207208 path : & ast:: Path ,
208209 target_module : Option < hir:: Module > ,
210+ target : GeneratedFunctionTarget ,
209211 ) -> Option < Self > {
210- let mut file = ctx. frange . file_id ;
211- let target = match & target_module {
212- Some ( target_module) => {
213- let module_source = target_module. definition_source ( ctx. db ( ) ) ;
214- let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , & module_source) ?;
215- file = in_file;
216- target
217- }
218- None => next_space_for_fn_after_call_site ( FuncExpr :: Func ( call. clone ( ) ) ) ?,
219- } ;
220212 let needs_pub = target_module. is_some ( ) ;
221- let target_module = target_module. or_else ( || ctx . sema . scope ( target. syntax ( ) ) . module ( ) ) ?;
213+ let target_module = target_module. or_else ( || current_module ( target. syntax ( ) , ctx ) ) ?;
222214 let fn_name = fn_name ( path) ?;
223215 let ( type_params, params) = fn_args ( ctx, target_module, FuncExpr :: Func ( call. clone ( ) ) ) ?;
224216
@@ -235,7 +227,6 @@ impl FunctionBuilder {
235227 params,
236228 ret_type,
237229 should_focus_return_type,
238- file,
239230 needs_pub,
240231 is_async,
241232 } )
@@ -245,25 +236,11 @@ impl FunctionBuilder {
245236 ctx : & AssistContext ,
246237 call : & ast:: MethodCallExpr ,
247238 name : & ast:: NameRef ,
248- impl_ : & Option < ast:: Impl > ,
249- file : FileId ,
250239 target_module : Module ,
251- current_module : Module ,
240+ target : GeneratedFunctionTarget ,
252241 ) -> Option < Self > {
253- // let mut file = ctx.frange.file_id;
254- // let target_module = ctx.sema.scope(call.syntax()).module()?;
255- let target = match impl_ {
256- Some ( impl_) => next_space_for_fn_in_impl ( & impl_) ?,
257- None => {
258- next_space_for_fn_in_module (
259- ctx. sema . db ,
260- & target_module. definition_source ( ctx. sema . db ) ,
261- ) ?
262- . 1
263- }
264- } ;
265- let needs_pub = !module_is_descendant ( & current_module, & target_module, ctx) ;
266-
242+ let needs_pub =
243+ !module_is_descendant ( & current_module ( call. syntax ( ) , ctx) ?, & target_module, ctx) ;
267244 let fn_name = make:: name ( & name. text ( ) ) ;
268245 let ( type_params, params) = fn_args ( ctx, target_module, FuncExpr :: Method ( call. clone ( ) ) ) ?;
269246
@@ -280,7 +257,6 @@ impl FunctionBuilder {
280257 params,
281258 ret_type,
282259 should_focus_return_type,
283- file,
284260 needs_pub,
285261 is_async,
286262 } )
@@ -302,33 +278,29 @@ impl FunctionBuilder {
302278 let leading_ws;
303279 let trailing_ws;
304280
305- let insert_offset = match self . target {
281+ match self . target {
306282 GeneratedFunctionTarget :: BehindItem ( it) => {
307283 let indent = IndentLevel :: from_node ( & it) ;
308284 leading_ws = format ! ( "\n \n {}" , indent) ;
309285 fn_def = fn_def. indent ( indent) ;
310286 trailing_ws = String :: new ( ) ;
311- it. text_range ( ) . end ( )
312287 }
313288 GeneratedFunctionTarget :: InEmptyItemList ( it) => {
314289 let indent = IndentLevel :: from_node ( & it) ;
315290 leading_ws = format ! ( "\n {}" , indent + 1 ) ;
316291 fn_def = fn_def. indent ( indent + 1 ) ;
317292 trailing_ws = format ! ( "\n {}" , indent) ;
318- it. text_range ( ) . start ( ) + TextSize :: of ( '{' )
319293 }
320294 } ;
321295
322296 FunctionTemplate {
323- insert_offset,
324297 leading_ws,
325298 ret_type : fn_def. ret_type ( ) ,
326299 // PANIC: we guarantee we always create a function body with a tail expr
327300 tail_expr : fn_def. body ( ) . unwrap ( ) . tail_expr ( ) . unwrap ( ) ,
328301 should_focus_return_type : self . should_focus_return_type ,
329302 fn_def,
330303 trailing_ws,
331- file : self . file ,
332304 }
333305 }
334306}
@@ -365,6 +337,47 @@ fn make_return_type(
365337 ( ret_type, should_focus_return_type)
366338}
367339
340+ fn get_fn_target (
341+ ctx : & AssistContext ,
342+ target_module : & Option < Module > ,
343+ call : CallExpr ,
344+ ) -> Option < ( GeneratedFunctionTarget , FileId , TextSize ) > {
345+ let mut file = ctx. frange . file_id ;
346+ let target = match target_module {
347+ Some ( target_module) => {
348+ let module_source = target_module. definition_source ( ctx. db ( ) ) ;
349+ let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , & module_source) ?;
350+ file = in_file;
351+ target
352+ }
353+ None => next_space_for_fn_after_call_site ( FuncExpr :: Func ( call. clone ( ) ) ) ?,
354+ } ;
355+ Some ( ( target. clone ( ) , file, get_insert_offset ( & target) ) )
356+ }
357+
358+ fn get_method_target (
359+ ctx : & AssistContext ,
360+ target_module : & Module ,
361+ impl_ : & Option < ast:: Impl > ,
362+ ) -> Option < ( GeneratedFunctionTarget , TextSize ) > {
363+ let target = match impl_ {
364+ Some ( impl_) => next_space_for_fn_in_impl ( & impl_) ?,
365+ None => {
366+ next_space_for_fn_in_module ( ctx. sema . db , & target_module. definition_source ( ctx. sema . db ) ) ?
367+ . 1
368+ }
369+ } ;
370+ Some ( ( target. clone ( ) , get_insert_offset ( & target) ) )
371+ }
372+
373+ fn get_insert_offset ( target : & GeneratedFunctionTarget ) -> TextSize {
374+ match & target {
375+ GeneratedFunctionTarget :: BehindItem ( it) => it. text_range ( ) . end ( ) ,
376+ GeneratedFunctionTarget :: InEmptyItemList ( it) => it. text_range ( ) . start ( ) + TextSize :: of ( '{' ) ,
377+ }
378+ }
379+
380+ #[ derive( Clone ) ]
368381enum GeneratedFunctionTarget {
369382 BehindItem ( SyntaxNode ) ,
370383 InEmptyItemList ( SyntaxNode ) ,
0 commit comments