1
+ use std:: fmt:: Display ;
2
+
1
3
use ide_db:: { helpers:: FamousDefs , RootDatabase } ;
2
4
use syntax:: {
3
5
ast:: { self , NameOwner } ,
4
- AstNode ,
6
+ AstNode , SyntaxNode ,
5
7
} ;
6
8
7
9
use crate :: {
8
- assist_context:: { AssistContext , Assists } ,
10
+ assist_context:: { AssistBuilder , AssistContext , Assists } ,
9
11
utils:: generate_trait_impl_text,
10
12
AssistId , AssistKind ,
11
13
} ;
@@ -36,11 +38,15 @@ use crate::{
36
38
// }
37
39
// ```
38
40
pub ( crate ) fn generate_deref ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
41
+ generate_record_deref ( acc, ctx) . or_else ( || generate_tuple_deref ( acc, ctx) )
42
+ }
43
+
44
+ fn generate_record_deref ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
39
45
let strukt = ctx. find_node_at_offset :: < ast:: Struct > ( ) ?;
40
46
let field = ctx. find_node_at_offset :: < ast:: RecordField > ( ) ?;
41
47
42
48
if existing_deref_impl ( & ctx. sema , & strukt) . is_some ( ) {
43
- cov_mark:: hit!( test_add_deref_impl_already_exists ) ;
49
+ cov_mark:: hit!( test_add_record_deref_impl_already_exists ) ;
44
50
return None ;
45
51
}
46
52
@@ -51,26 +57,50 @@ pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<(
51
57
AssistId ( "generate_deref" , AssistKind :: Generate ) ,
52
58
format ! ( "Generate `Deref` impl using `{}`" , field_name) ,
53
59
target,
54
- |edit| {
55
- let start_offset = strukt. syntax ( ) . text_range ( ) . end ( ) ;
56
- let impl_code = format ! (
57
- r#" type Target = {0};
60
+ |edit| generate_edit ( edit, strukt, field_type. syntax ( ) , field_name. syntax ( ) ) ,
61
+ )
62
+ }
63
+
64
+ fn generate_tuple_deref ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
65
+ let strukt = ctx. find_node_at_offset :: < ast:: Struct > ( ) ?;
66
+ let field = ctx. find_node_at_offset :: < ast:: TupleField > ( ) ?;
67
+ let field_list = ctx. find_node_at_offset :: < ast:: TupleFieldList > ( ) ?;
68
+ let field_list_index =
69
+ field_list. syntax ( ) . children ( ) . into_iter ( ) . position ( |s| & s == field. syntax ( ) ) ?;
70
+
71
+ if existing_deref_impl ( & ctx. sema , & strukt) . is_some ( ) {
72
+ cov_mark:: hit!( test_add_field_deref_impl_already_exists) ;
73
+ return None ;
74
+ }
75
+
76
+ let field_type = field. ty ( ) ?;
77
+ let target = field. syntax ( ) . text_range ( ) ;
78
+ acc. add (
79
+ AssistId ( "generate_deref" , AssistKind :: Generate ) ,
80
+ format ! ( "Generate `Deref` impl using `{}`" , field. syntax( ) ) ,
81
+ target,
82
+ |edit| generate_edit ( edit, strukt, field_type. syntax ( ) , field_list_index) ,
83
+ )
84
+ }
85
+
86
+ fn generate_edit (
87
+ edit : & mut AssistBuilder ,
88
+ strukt : ast:: Struct ,
89
+ field_type_syntax : & SyntaxNode ,
90
+ field_name : impl Display ,
91
+ ) {
92
+ let start_offset = strukt. syntax ( ) . text_range ( ) . end ( ) ;
93
+ let impl_code = format ! (
94
+ r#" type Target = {0};
58
95
59
96
fn deref(&self) -> &Self::Target {{
60
97
&self.{1}
61
98
}}"# ,
62
- field_type. syntax( ) ,
63
- field_name. syntax( )
64
- ) ;
65
- let strukt_adt = ast:: Adt :: Struct ( strukt) ;
66
- // Q for reviewer: Is there a better way to specify the trait_text, e.g.
67
- // - can I have it auto `use std::ops::Deref`, and then just use `Deref` as the trait text?
68
- // Or is there a helper that might detect if `std::ops::Deref` has been used, and pick `Deref`,
69
- // otherwise, pick `std::ops::Deref` for the trait_text.
70
- let deref_impl = generate_trait_impl_text ( & strukt_adt, "std::ops::Deref" , & impl_code) ;
71
- edit. insert ( start_offset, deref_impl) ;
72
- } ,
73
- )
99
+ field_type_syntax, field_name
100
+ ) ;
101
+ let strukt_adt = ast:: Adt :: Struct ( strukt) ;
102
+ let deref_impl = generate_trait_impl_text ( & strukt_adt, "std::ops::Deref" , & impl_code) ;
103
+ edit. insert ( start_offset, deref_impl) ;
74
104
}
75
105
76
106
fn existing_deref_impl (
@@ -97,7 +127,7 @@ mod tests {
97
127
use super :: * ;
98
128
99
129
#[ test]
100
- fn test_generate_deref ( ) {
130
+ fn test_generate_record_deref ( ) {
101
131
check_assist (
102
132
generate_deref,
103
133
r#"struct A { }
@@ -115,6 +145,43 @@ impl std::ops::Deref for B {
115
145
) ;
116
146
}
117
147
148
+ #[ test]
149
+ fn test_generate_field_deref_idx_0 ( ) {
150
+ check_assist (
151
+ generate_deref,
152
+ r#"struct A { }
153
+ struct B($0A);"# ,
154
+ r#"struct A { }
155
+ struct B(A);
156
+
157
+ impl std::ops::Deref for B {
158
+ type Target = A;
159
+
160
+ fn deref(&self) -> &Self::Target {
161
+ &self.0
162
+ }
163
+ }"# ,
164
+ ) ;
165
+ }
166
+ #[ test]
167
+ fn test_generate_field_deref_idx_1 ( ) {
168
+ check_assist (
169
+ generate_deref,
170
+ r#"struct A { }
171
+ struct B(u8, $0A);"# ,
172
+ r#"struct A { }
173
+ struct B(u8, A);
174
+
175
+ impl std::ops::Deref for B {
176
+ type Target = A;
177
+
178
+ fn deref(&self) -> &Self::Target {
179
+ &self.1
180
+ }
181
+ }"# ,
182
+ ) ;
183
+ }
184
+
118
185
fn check_not_applicable ( ra_fixture : & str ) {
119
186
let fixture = format ! (
120
187
"//- /main.rs crate:main deps:core,std\n {}\n {}" ,
@@ -125,8 +192,8 @@ impl std::ops::Deref for B {
125
192
}
126
193
127
194
#[ test]
128
- fn test_generate_deref_not_applicable_if_already_impl ( ) {
129
- cov_mark:: check!( test_add_deref_impl_already_exists ) ;
195
+ fn test_generate_record_deref_not_applicable_if_already_impl ( ) {
196
+ cov_mark:: check!( test_add_record_deref_impl_already_exists ) ;
130
197
check_not_applicable (
131
198
r#"struct A { }
132
199
struct B { $0a: A }
@@ -137,6 +204,23 @@ impl std::ops::Deref for B {
137
204
fn deref(&self) -> &Self::Target {
138
205
&self.a
139
206
}
207
+ }"# ,
208
+ )
209
+ }
210
+
211
+ #[ test]
212
+ fn test_generate_field_deref_not_applicable_if_already_impl ( ) {
213
+ cov_mark:: check!( test_add_field_deref_impl_already_exists) ;
214
+ check_not_applicable (
215
+ r#"struct A { }
216
+ struct B($0A)
217
+
218
+ impl std::ops::Deref for B {
219
+ type Target = A;
220
+
221
+ fn deref(&self) -> &Self::Target {
222
+ &self.0
223
+ }
140
224
}"# ,
141
225
)
142
226
}
0 commit comments