1
- use hir:: { HirDisplay , TypeInfo } ;
1
+ use hir:: { HasSource , HirDisplay , InFile , Module , TypeInfo } ;
2
2
use ide_db:: { base_db:: FileId , helpers:: SnippetCap } ;
3
3
use rustc_hash:: { FxHashMap , FxHashSet } ;
4
4
use stdx:: to_lower_snake_case;
@@ -13,7 +13,7 @@ use syntax::{
13
13
14
14
use crate :: {
15
15
utils:: useless_type_special_case,
16
- utils:: { render_snippet, Cursor } ,
16
+ utils:: { find_struct_impl , render_snippet, Cursor } ,
17
17
AssistContext , AssistId , AssistKind , Assists ,
18
18
} ;
19
19
@@ -103,10 +103,22 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
103
103
pub ( crate ) fn generate_method ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
104
104
let fn_name: ast:: NameRef = ctx. find_node_at_offset ( ) ?;
105
105
let call: ast:: MethodCallExpr = ctx. find_node_at_offset ( ) ?;
106
- let module = ctx. sema . scope ( call. syntax ( ) ) . module ( ) ;
107
- let ty = ctx. sema . type_of_expr ( & call. receiver ( ) ?) ?. as_adt ( ) ?;
108
-
109
- let function_builder = FunctionBuilder :: from_method_call ( ctx, & call, & fn_name, module) ?;
106
+ let ty = ctx. sema . type_of_expr ( & call. receiver ( ) ?) ?. original ( ) . strip_references ( ) . as_adt ( ) ?;
107
+
108
+ let ( impl_, file) = match ty {
109
+ hir:: Adt :: Struct ( strukt) => get_impl ( strukt. source ( ctx. sema . db ) ?. syntax ( ) , & fn_name, ctx) ,
110
+ hir:: Adt :: Enum ( en) => get_impl ( en. source ( ctx. sema . db ) ?. syntax ( ) , & fn_name, ctx) ,
111
+ hir:: Adt :: Union ( union) => get_impl ( union. source ( ctx. sema . db ) ?. syntax ( ) , & fn_name, ctx) ,
112
+ } ?;
113
+
114
+ let function_builder = FunctionBuilder :: from_method_call (
115
+ ctx,
116
+ & call,
117
+ & fn_name,
118
+ & impl_,
119
+ file,
120
+ ty. module ( ctx. sema . db ) ,
121
+ ) ?;
110
122
let target = call. syntax ( ) . text_range ( ) ;
111
123
112
124
acc. add (
@@ -116,11 +128,10 @@ pub(crate) fn generate_method(acc: &mut Assists, ctx: &AssistContext) -> Option<
116
128
|builder| {
117
129
let function_template = function_builder. render ( ) ;
118
130
builder. edit_file ( function_template. file ) ;
119
- let new_fn = format ! (
120
- "impl {} {{{}}}" ,
121
- ty. name( ctx. sema. db) ,
122
- function_template. to_string( ctx. config. snippet_cap)
123
- ) ;
131
+ let mut new_fn = function_template. to_string ( ctx. config . snippet_cap ) ;
132
+ if impl_. is_none ( ) {
133
+ new_fn = format ! ( "\n impl {} {{\n {}\n }}" , ty. name( ctx. sema. db) , new_fn, ) ;
134
+ }
124
135
match ctx. config . snippet_cap {
125
136
Some ( cap) => builder. insert_snippet ( cap, function_template. insert_offset , new_fn) ,
126
137
None => builder. insert ( function_template. insert_offset , new_fn) ,
@@ -129,6 +140,18 @@ pub(crate) fn generate_method(acc: &mut Assists, ctx: &AssistContext) -> Option<
129
140
)
130
141
}
131
142
143
+ fn get_impl (
144
+ adt : InFile < & SyntaxNode > ,
145
+ fn_name : & ast:: NameRef ,
146
+ ctx : & AssistContext ,
147
+ ) -> Option < ( Option < ast:: Impl > , FileId ) > {
148
+ let file = adt. file_id . original_file ( ctx. sema . db ) ;
149
+ let adt = adt. value ;
150
+ let adt = ast:: Adt :: cast ( adt. clone ( ) ) ?;
151
+ let r = find_struct_impl ( ctx, & adt, fn_name. text ( ) . as_str ( ) ) ?;
152
+ Some ( ( r, file) )
153
+ }
154
+
132
155
struct FunctionTemplate {
133
156
insert_offset : TextSize ,
134
157
leading_ws : String ,
@@ -235,20 +258,24 @@ impl FunctionBuilder {
235
258
ctx : & AssistContext ,
236
259
call : & ast:: MethodCallExpr ,
237
260
name : & ast:: NameRef ,
238
- target_module : Option < hir:: Module > ,
261
+ impl_ : & Option < ast:: Impl > ,
262
+ file : FileId ,
263
+ target_module : Module ,
239
264
) -> Option < Self > {
240
- let mut file = ctx. frange . file_id ;
241
- let target = match & target_module {
242
- Some ( target_module) => {
243
- let module_source = target_module. definition_source ( ctx. db ( ) ) ;
244
- let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , & module_source) ?;
245
- file = in_file;
246
- target
265
+ // let mut file = ctx.frange.file_id;
266
+ // let target_module = ctx.sema.scope(call.syntax()).module()?;
267
+ let target = match impl_ {
268
+ Some ( impl_) => next_space_for_fn_in_impl ( & impl_) ?,
269
+ None => {
270
+ next_space_for_fn_in_module (
271
+ ctx. sema . db ,
272
+ & target_module. definition_source ( ctx. sema . db ) ,
273
+ ) ?
274
+ . 1
247
275
}
248
- None => next_space_for_fn_after_call_site ( FuncExpr :: Method ( call) ) ?,
249
276
} ;
277
+
250
278
let needs_pub = false ;
251
- let target_module = target_module. or_else ( || ctx. sema . scope ( target. syntax ( ) ) . module ( ) ) ?;
252
279
let fn_name = make:: name ( & name. text ( ) ) ;
253
280
let ( type_params, params) = fn_args ( ctx, target_module, FuncExpr :: Method ( call) ) ?;
254
281
@@ -268,7 +295,11 @@ impl FunctionBuilder {
268
295
// type, but that the current state of their code doesn't allow that return type
269
296
// to be accurately inferred.
270
297
let ( ret_ty, should_render_snippet) = {
271
- match ctx. sema . type_of_expr ( & ast:: Expr :: MethodCallExpr ( call. clone ( ) ) ) {
298
+ match ctx
299
+ . sema
300
+ . type_of_expr ( & ast:: Expr :: MethodCallExpr ( call. clone ( ) ) )
301
+ . map ( TypeInfo :: original)
302
+ {
272
303
Some ( ty) if ty. is_unknown ( ) || ty. is_unit ( ) => ( make:: ty_unit ( ) , true ) ,
273
304
Some ( ty) => {
274
305
let rendered = ty. display_source_code ( ctx. db ( ) , target_module. into ( ) ) ;
@@ -525,6 +556,14 @@ fn next_space_for_fn_in_module(
525
556
Some ( ( file, assist_item) )
526
557
}
527
558
559
+ fn next_space_for_fn_in_impl ( impl_ : & ast:: Impl ) -> Option < GeneratedFunctionTarget > {
560
+ if let Some ( last_item) = impl_. assoc_item_list ( ) . and_then ( |it| it. assoc_items ( ) . last ( ) ) {
561
+ Some ( GeneratedFunctionTarget :: BehindItem ( last_item. syntax ( ) . clone ( ) ) )
562
+ } else {
563
+ Some ( GeneratedFunctionTarget :: InEmptyItemList ( impl_. assoc_item_list ( ) ?. syntax ( ) . clone ( ) ) )
564
+ }
565
+ }
566
+
528
567
#[ cfg( test) ]
529
568
mod tests {
530
569
use crate :: tests:: { check_assist, check_assist_not_applicable} ;
0 commit comments