@@ -6,9 +6,9 @@ use syntax::{
6
6
ast:: {
7
7
self ,
8
8
edit:: { AstNodeEdit , IndentLevel } ,
9
- make, ArgListOwner , AstNode , ModuleItemOwner ,
9
+ make, ArgListOwner , AstNode , CallExpr , ModuleItemOwner ,
10
10
} ,
11
- SyntaxKind , SyntaxNode , TextSize ,
11
+ SyntaxKind , SyntaxNode , TextRange , TextSize ,
12
12
} ;
13
13
14
14
use crate :: {
@@ -85,31 +85,19 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
85
85
None => None ,
86
86
} ;
87
87
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) ?;
89
90
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)
105
93
}
106
94
107
95
fn gen_method ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
108
96
let call: ast:: MethodCallExpr = ctx. find_node_at_offset ( ) ?;
109
97
let fn_name = call. name_ref ( ) ?;
110
98
let adt = ctx. sema . type_of_expr ( & call. receiver ( ) ?) ?. original ( ) . strip_references ( ) . as_adt ( ) ?;
111
99
112
- let current_module = ctx . sema . scope ( call. syntax ( ) ) . module ( ) ?;
100
+ let current_module = current_module ( call. syntax ( ) , ctx ) ?;
113
101
let target_module = adt. module ( ctx. sema . db ) ;
114
102
115
103
if current_module. krate ( ) != target_module. krate ( ) {
@@ -122,44 +110,58 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
122
110
ctx. sema . find_node_at_offset_with_macros ( file. syntax ( ) , range. range . start ( ) ) ?;
123
111
let impl_ = find_struct_impl ( ctx, & adt_source, fn_name. text ( ) . as_str ( ) ) ?;
124
112
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,
126
121
ctx,
127
- & call ,
128
- & fn_name ,
129
- & impl_ ,
122
+ text_range ,
123
+ function_builder ,
124
+ insert_offset ,
130
125
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,
152
128
)
153
129
}
154
130
155
- struct FunctionTemplate {
131
+ fn add_func_to_accumulator (
132
+ acc : & mut Assists ,
133
+ ctx : & AssistContext ,
134
+ text_range : TextRange ,
135
+ function_builder : FunctionBuilder ,
156
136
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 {
157
160
leading_ws : String ,
158
161
fn_def : ast:: Fn ,
159
162
ret_type : Option < ast:: RetType > ,
160
163
should_focus_return_type : bool ,
161
164
trailing_ws : String ,
162
- file : FileId ,
163
165
tail_expr : ast:: Expr ,
164
166
}
165
167
@@ -193,7 +195,6 @@ struct FunctionBuilder {
193
195
params : ast:: ParamList ,
194
196
ret_type : Option < ast:: RetType > ,
195
197
should_focus_return_type : bool ,
196
- file : FileId ,
197
198
needs_pub : bool ,
198
199
is_async : bool ,
199
200
}
@@ -206,19 +207,10 @@ impl FunctionBuilder {
206
207
call : & ast:: CallExpr ,
207
208
path : & ast:: Path ,
208
209
target_module : Option < hir:: Module > ,
210
+ target : GeneratedFunctionTarget ,
209
211
) -> 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
- } ;
220
212
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 ) ) ?;
222
214
let fn_name = fn_name ( path) ?;
223
215
let ( type_params, params) = fn_args ( ctx, target_module, FuncExpr :: Func ( call. clone ( ) ) ) ?;
224
216
@@ -235,7 +227,6 @@ impl FunctionBuilder {
235
227
params,
236
228
ret_type,
237
229
should_focus_return_type,
238
- file,
239
230
needs_pub,
240
231
is_async,
241
232
} )
@@ -245,25 +236,11 @@ impl FunctionBuilder {
245
236
ctx : & AssistContext ,
246
237
call : & ast:: MethodCallExpr ,
247
238
name : & ast:: NameRef ,
248
- impl_ : & Option < ast:: Impl > ,
249
- file : FileId ,
250
239
target_module : Module ,
251
- current_module : Module ,
240
+ target : GeneratedFunctionTarget ,
252
241
) -> 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) ;
267
244
let fn_name = make:: name ( & name. text ( ) ) ;
268
245
let ( type_params, params) = fn_args ( ctx, target_module, FuncExpr :: Method ( call. clone ( ) ) ) ?;
269
246
@@ -280,7 +257,6 @@ impl FunctionBuilder {
280
257
params,
281
258
ret_type,
282
259
should_focus_return_type,
283
- file,
284
260
needs_pub,
285
261
is_async,
286
262
} )
@@ -302,33 +278,29 @@ impl FunctionBuilder {
302
278
let leading_ws;
303
279
let trailing_ws;
304
280
305
- let insert_offset = match self . target {
281
+ match self . target {
306
282
GeneratedFunctionTarget :: BehindItem ( it) => {
307
283
let indent = IndentLevel :: from_node ( & it) ;
308
284
leading_ws = format ! ( "\n \n {}" , indent) ;
309
285
fn_def = fn_def. indent ( indent) ;
310
286
trailing_ws = String :: new ( ) ;
311
- it. text_range ( ) . end ( )
312
287
}
313
288
GeneratedFunctionTarget :: InEmptyItemList ( it) => {
314
289
let indent = IndentLevel :: from_node ( & it) ;
315
290
leading_ws = format ! ( "\n {}" , indent + 1 ) ;
316
291
fn_def = fn_def. indent ( indent + 1 ) ;
317
292
trailing_ws = format ! ( "\n {}" , indent) ;
318
- it. text_range ( ) . start ( ) + TextSize :: of ( '{' )
319
293
}
320
294
} ;
321
295
322
296
FunctionTemplate {
323
- insert_offset,
324
297
leading_ws,
325
298
ret_type : fn_def. ret_type ( ) ,
326
299
// PANIC: we guarantee we always create a function body with a tail expr
327
300
tail_expr : fn_def. body ( ) . unwrap ( ) . tail_expr ( ) . unwrap ( ) ,
328
301
should_focus_return_type : self . should_focus_return_type ,
329
302
fn_def,
330
303
trailing_ws,
331
- file : self . file ,
332
304
}
333
305
}
334
306
}
@@ -365,6 +337,47 @@ fn make_return_type(
365
337
( ret_type, should_focus_return_type)
366
338
}
367
339
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 ) ]
368
381
enum GeneratedFunctionTarget {
369
382
BehindItem ( SyntaxNode ) ,
370
383
InEmptyItemList ( SyntaxNode ) ,
0 commit comments