1
1
use crate :: assist_context:: { AssistContext , Assists } ;
2
- use ide_db:: { assists:: AssistId , SnippetCap } ;
2
+ use ide_db:: assists:: AssistId ;
3
3
use syntax:: {
4
- ast:: { self , HasGenericParams , HasVisibility } ,
5
- AstNode ,
4
+ ast:: { self , edit :: IndentLevel , make , HasGenericParams , HasVisibility } ,
5
+ ted , AstNode , SyntaxKind ,
6
6
} ;
7
7
8
8
// NOTES :
@@ -68,6 +68,16 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
68
68
// Get AST Node
69
69
let impl_ast = ctx. find_node_at_offset :: < ast:: Impl > ( ) ?;
70
70
71
+ // Check if cursor is to the left of assoc item list's L_CURLY.
72
+ // if no L_CURLY then return.
73
+ let l_curly = impl_ast. assoc_item_list ( ) ?. l_curly_token ( ) ?;
74
+
75
+ let cursor_offset = ctx. offset ( ) ;
76
+ let l_curly_offset = l_curly. text_range ( ) ;
77
+ if cursor_offset >= l_curly_offset. start ( ) {
78
+ return None ;
79
+ }
80
+
71
81
// If impl is not inherent then we don't really need to go any further.
72
82
if impl_ast. for_token ( ) . is_some ( ) {
73
83
return None ;
@@ -80,9 +90,11 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
80
90
return None ;
81
91
}
82
92
93
+ let impl_name = impl_ast. self_ty ( ) ?;
94
+
83
95
acc. add (
84
96
AssistId ( "generate_trait_from_impl" , ide_db:: assists:: AssistKind :: Generate ) ,
85
- "Generate trait from impl" . to_owned ( ) ,
97
+ "Generate trait from impl" ,
86
98
impl_ast. syntax ( ) . text_range ( ) ,
87
99
|builder| {
88
100
let trait_items = assoc_items. clone_for_update ( ) ;
@@ -93,45 +105,43 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
93
105
remove_items_visibility ( & item) ;
94
106
} ) ;
95
107
96
- syntax :: ted:: replace ( assoc_items. clone_for_update ( ) . syntax ( ) , impl_items. syntax ( ) ) ;
108
+ ted:: replace ( assoc_items. clone_for_update ( ) . syntax ( ) , impl_items. syntax ( ) ) ;
97
109
98
110
impl_items. assoc_items ( ) . for_each ( |item| {
99
111
remove_items_visibility ( & item) ;
100
112
} ) ;
101
113
102
- let trait_ast = ast :: make:: trait_ (
114
+ let trait_ast = make:: trait_ (
103
115
false ,
104
- "NewTrait" . to_string ( ) ,
105
- HasGenericParams :: generic_param_list ( & impl_ast ) ,
106
- HasGenericParams :: where_clause ( & impl_ast ) ,
116
+ "NewTrait" ,
117
+ impl_ast . generic_param_list ( ) ,
118
+ impl_ast . where_clause ( ) ,
107
119
trait_items,
108
120
) ;
109
121
110
122
// Change `impl Foo` to `impl NewTrait for Foo`
111
- // First find the PATH_TYPE which is what Foo is.
112
- let impl_name = impl_ast. self_ty ( ) . unwrap ( ) ;
113
- let trait_name = if let Some ( genpars) = impl_ast. generic_param_list ( ) {
114
- format ! ( "NewTrait{}" , genpars. to_generic_args( ) )
123
+ let arg_list = if let Some ( genpars) = impl_ast. generic_param_list ( ) {
124
+ genpars. to_generic_args ( ) . to_string ( )
115
125
} else {
116
- format ! ( "NewTrait" )
126
+ "" . to_string ( )
117
127
} ;
118
128
119
129
// // Then replace
120
130
builder. replace (
121
- impl_name. clone ( ) . syntax ( ) . text_range ( ) ,
122
- format ! ( "{} for {}" , trait_name , impl_name. to_string( ) ) ,
131
+ impl_name. syntax ( ) . text_range ( ) ,
132
+ format ! ( "NewTrait {} for {}" , arg_list , impl_name. to_string( ) ) ,
123
133
) ;
124
134
125
- builder. replace (
126
- impl_ast. assoc_item_list ( ) . unwrap ( ) . syntax ( ) . text_range ( ) ,
127
- impl_items. to_string ( ) ,
128
- ) ;
135
+ builder. replace ( assoc_items. syntax ( ) . text_range ( ) , impl_items. to_string ( ) ) ;
129
136
130
137
// Insert trait before TraitImpl
131
- builder. insert_snippet (
132
- SnippetCap :: new ( true ) . unwrap ( ) ,
138
+ builder. insert (
133
139
impl_ast. syntax ( ) . text_range ( ) . start ( ) ,
134
- format ! ( "{}\n \n " , trait_ast. to_string( ) ) ,
140
+ format ! (
141
+ "{}\n \n {}" ,
142
+ trait_ast. to_string( ) ,
143
+ IndentLevel :: from_node( impl_ast. syntax( ) )
144
+ ) ,
135
145
) ;
136
146
} ,
137
147
) ;
@@ -144,17 +154,17 @@ fn remove_items_visibility(item: &ast::AssocItem) {
144
154
match item {
145
155
ast:: AssocItem :: Const ( c) => {
146
156
if let Some ( vis) = c. visibility ( ) {
147
- syntax :: ted:: remove ( vis. syntax ( ) ) ;
157
+ ted:: remove ( vis. syntax ( ) ) ;
148
158
}
149
159
}
150
160
ast:: AssocItem :: Fn ( f) => {
151
161
if let Some ( vis) = f. visibility ( ) {
152
- syntax :: ted:: remove ( vis. syntax ( ) ) ;
162
+ ted:: remove ( vis. syntax ( ) ) ;
153
163
}
154
164
}
155
165
ast:: AssocItem :: TypeAlias ( t) => {
156
166
if let Some ( vis) = t. visibility ( ) {
157
- syntax :: ted:: remove ( vis. syntax ( ) ) ;
167
+ ted:: remove ( vis. syntax ( ) ) ;
158
168
}
159
169
}
160
170
_ => ( ) ,
@@ -168,12 +178,12 @@ fn strip_body(item: &ast::AssocItem) {
168
178
// In constrast to function bodies, we want to see no ws before a semicolon.
169
179
// So let's remove them if we see any.
170
180
if let Some ( prev) = body. syntax ( ) . prev_sibling_or_token ( ) {
171
- if prev. kind ( ) == syntax :: SyntaxKind :: WHITESPACE {
172
- syntax :: ted:: remove ( prev) ;
181
+ if prev. kind ( ) == SyntaxKind :: WHITESPACE {
182
+ ted:: remove ( prev) ;
173
183
}
174
184
}
175
185
176
- syntax :: ted:: replace ( body. syntax ( ) , ast:: make:: tokens:: semicolon ( ) ) ;
186
+ ted:: replace ( body. syntax ( ) , ast:: make:: tokens:: semicolon ( ) ) ;
177
187
}
178
188
}
179
189
_ => ( ) ,
@@ -185,6 +195,21 @@ mod tests {
185
195
use super :: * ;
186
196
use crate :: tests:: { check_assist, check_assist_not_applicable} ;
187
197
198
+ #[ test]
199
+ fn test_trigger_when_cursor_on_header ( ) {
200
+ check_assist_not_applicable (
201
+ generate_trait_from_impl,
202
+ r#"
203
+ struct Foo(f64);
204
+
205
+ impl Foo { $0
206
+ fn add(&mut self, x: f64) {
207
+ self.0 += x;
208
+ }
209
+ }"# ,
210
+ ) ;
211
+ }
212
+
188
213
#[ test]
189
214
fn test_assoc_item_fn ( ) {
190
215
check_assist (
@@ -299,7 +324,7 @@ impl<const N: usize> NewTrait<N> for Foo<N> {
299
324
}
300
325
301
326
#[ test]
302
- fn test_e0449_avoided ( ) {
327
+ fn test_trait_items_should_not_have_vis ( ) {
303
328
check_assist (
304
329
generate_trait_from_impl,
305
330
r#"
@@ -334,4 +359,27 @@ impl Emp$0tyImpl{}
334
359
"# ,
335
360
)
336
361
}
362
+
363
+ #[ test]
364
+ fn test_not_top_level_impl ( ) {
365
+ check_assist (
366
+ generate_trait_from_impl,
367
+ r#"
368
+ mod a {
369
+ impl S$0 {
370
+ fn foo() {}
371
+ }
372
+ }"# ,
373
+ r#"
374
+ mod a {
375
+ trait NewTrait {
376
+ fn foo();
377
+ }
378
+
379
+ impl NewTrait for S {
380
+ fn foo() {}
381
+ }
382
+ }"# ,
383
+ )
384
+ }
337
385
}
0 commit comments