@@ -2,15 +2,17 @@ use hir::{InFile, MacroFileIdExt, ModuleDef};
2
2
use ide_db:: { helpers:: mod_path_to_ast, imports:: import_assets:: NameToImport , items_locator} ;
3
3
use itertools:: Itertools ;
4
4
use syntax:: {
5
- ast:: { self , AstNode , HasName } ,
5
+ ast:: { self , make, AstNode , HasName } ,
6
+ ted,
6
7
SyntaxKind :: WHITESPACE ,
8
+ T ,
7
9
} ;
8
10
9
11
use crate :: {
10
12
assist_context:: { AssistContext , Assists , SourceChangeBuilder } ,
11
13
utils:: {
12
- add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
13
- generate_trait_impl_text , render_snippet , Cursor , DefaultMethods , IgnoreAssocItems ,
14
+ add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, generate_trait_impl ,
15
+ DefaultMethods , IgnoreAssocItems ,
14
16
} ,
15
17
AssistId , AssistKind ,
16
18
} ;
@@ -132,35 +134,59 @@ fn add_assist(
132
134
label,
133
135
target,
134
136
|builder| {
135
- let insert_pos = adt. syntax ( ) . text_range ( ) . end ( ) ;
137
+ let insert_after = ted:: Position :: after ( builder. make_mut ( adt. clone ( ) ) . syntax ( ) ) ;
138
+
136
139
let impl_def_with_items =
137
140
impl_def_from_trait ( & ctx. sema , adt, & annotated_name, trait_, replace_trait_path) ;
138
141
update_attribute ( builder, old_derives, old_tree, old_trait_path, attr) ;
139
- let trait_path = replace_trait_path. to_string ( ) ;
142
+
143
+ let trait_path = make:: ty_path ( replace_trait_path. clone ( ) ) ;
144
+
140
145
match ( ctx. config . snippet_cap , impl_def_with_items) {
141
146
( None , _) => {
142
- builder. insert ( insert_pos, generate_trait_impl_text ( adt, & trait_path, "" ) )
147
+ let impl_def = generate_trait_impl ( adt, trait_path) ;
148
+
149
+ ted:: insert_all (
150
+ insert_after,
151
+ vec ! [ make:: tokens:: blank_line( ) . into( ) , impl_def. syntax( ) . clone( ) . into( ) ] ,
152
+ ) ;
153
+ }
154
+ ( Some ( cap) , None ) => {
155
+ let impl_def = generate_trait_impl ( adt, trait_path) ;
156
+
157
+ if let Some ( l_curly) =
158
+ impl_def. assoc_item_list ( ) . and_then ( |it| it. l_curly_token ( ) )
159
+ {
160
+ builder. add_tabstop_after_token ( cap, l_curly) ;
161
+ }
162
+
163
+ ted:: insert_all (
164
+ insert_after,
165
+ vec ! [ make:: tokens:: blank_line( ) . into( ) , impl_def. syntax( ) . clone( ) . into( ) ] ,
166
+ ) ;
143
167
}
144
- ( Some ( cap) , None ) => builder. insert_snippet (
145
- cap,
146
- insert_pos,
147
- generate_trait_impl_text ( adt, & trait_path, " $0" ) ,
148
- ) ,
149
168
( Some ( cap) , Some ( ( impl_def, first_assoc_item) ) ) => {
150
- let mut cursor = Cursor :: Before ( first_assoc_item. syntax ( ) ) ;
151
- let placeholder;
169
+ let mut added_snippet = false ;
152
170
if let ast:: AssocItem :: Fn ( ref func) = first_assoc_item {
153
171
if let Some ( m) = func. syntax ( ) . descendants ( ) . find_map ( ast:: MacroCall :: cast)
154
172
{
155
173
if m. syntax ( ) . text ( ) == "todo!()" {
156
- placeholder = m;
157
- cursor = Cursor :: Replace ( placeholder. syntax ( ) ) ;
174
+ // Make the `todo!()` a placeholder
175
+ builder. add_placeholder_snippet ( cap, m) ;
176
+ added_snippet = true ;
158
177
}
159
178
}
160
179
}
161
180
162
- let rendered = render_snippet ( cap, impl_def. syntax ( ) , cursor) ;
163
- builder. insert_snippet ( cap, insert_pos, format ! ( "\n \n {rendered}" ) )
181
+ if !added_snippet {
182
+ // If we haven't already added a snippet, add a tabstop before the generated function
183
+ builder. add_tabstop_before ( cap, first_assoc_item) ;
184
+ }
185
+
186
+ ted:: insert_all (
187
+ insert_after,
188
+ vec ! [ make:: tokens:: blank_line( ) . into( ) , impl_def. syntax( ) . clone( ) . into( ) ] ,
189
+ ) ;
164
190
}
165
191
} ;
166
192
} ,
@@ -190,28 +216,7 @@ fn impl_def_from_trait(
190
216
if trait_items. is_empty ( ) {
191
217
return None ;
192
218
}
193
- let impl_def = {
194
- use syntax:: ast:: Impl ;
195
- let text = generate_trait_impl_text ( adt, trait_path. to_string ( ) . as_str ( ) , "" ) ;
196
- // FIXME: `generate_trait_impl_text` currently generates two newlines
197
- // at the front, but these leading newlines should really instead be
198
- // inserted at the same time the impl is inserted
199
- assert_eq ! ( & text[ ..2 ] , "\n \n " , "`generate_trait_impl_text` output changed" ) ;
200
- let parse = syntax:: SourceFile :: parse ( & text[ 2 ..] ) ;
201
- let node = match parse. tree ( ) . syntax ( ) . descendants ( ) . find_map ( Impl :: cast) {
202
- Some ( it) => it,
203
- None => {
204
- panic ! (
205
- "Failed to make ast node `{}` from text {}" ,
206
- std:: any:: type_name:: <Impl >( ) ,
207
- text
208
- )
209
- }
210
- } ;
211
- let node = node. clone_for_update ( ) ;
212
- assert_eq ! ( node. syntax( ) . text_range( ) . start( ) , 0 . into( ) ) ;
213
- node
214
- } ;
219
+ let impl_def = generate_trait_impl ( adt, make:: ty_path ( trait_path. clone ( ) ) ) ;
215
220
216
221
let first_assoc_item =
217
222
add_trait_assoc_items_to_impl ( sema, & trait_items, trait_, & impl_def, target_scope) ;
@@ -238,20 +243,37 @@ fn update_attribute(
238
243
let has_more_derives = !new_derives. is_empty ( ) ;
239
244
240
245
if has_more_derives {
241
- let new_derives = format ! ( "({})" , new_derives. iter( ) . format( ", " ) ) ;
242
- builder. replace ( old_tree. syntax ( ) . text_range ( ) , new_derives) ;
246
+ let old_tree = builder. make_mut ( old_tree. clone ( ) ) ;
247
+
248
+ // Make the paths into flat lists of tokens in a vec
249
+ let tt = new_derives. iter ( ) . map ( |path| path. syntax ( ) . clone ( ) ) . map ( |node| {
250
+ node. descendants_with_tokens ( )
251
+ . filter_map ( |element| element. into_token ( ) )
252
+ . collect :: < Vec < _ > > ( )
253
+ } ) ;
254
+ // ...which are interspersed with ", "
255
+ let tt = Itertools :: intersperse (
256
+ tt,
257
+ vec ! [ make:: token( T ![ , ] ) . into( ) , make:: tokens:: single_space( ) . into( ) ] ,
258
+ ) ;
259
+ // ...wrap them into the appropriate `NodeOrToken` variant
260
+ let tt = tt. flatten ( ) . map ( |token| syntax:: NodeOrToken :: Token ( token) ) ;
261
+ // ...and make them into a flat list of tokens
262
+ let tt = tt. collect :: < Vec < _ > > ( ) ;
263
+
264
+ let new_tree = make:: token_tree ( T ! [ '(' ] , tt) . clone_for_update ( ) ;
265
+ ted:: replace ( old_tree. syntax ( ) , new_tree. syntax ( ) ) ;
243
266
} else {
244
- let attr_range = attr. syntax ( ) . text_range ( ) ;
245
- builder. delete ( attr_range) ;
246
-
247
- if let Some ( line_break_range) = attr
248
- . syntax ( )
249
- . next_sibling_or_token ( )
250
- . filter ( |t| t. kind ( ) == WHITESPACE )
251
- . map ( |t| t. text_range ( ) )
267
+ // Remove the attr and any trailing whitespace
268
+ let attr = builder. make_mut ( attr. clone ( ) ) ;
269
+
270
+ if let Some ( line_break) =
271
+ attr. syntax ( ) . next_sibling_or_token ( ) . filter ( |t| t. kind ( ) == WHITESPACE )
252
272
{
253
- builder . delete ( line_break_range ) ;
273
+ ted :: remove ( line_break )
254
274
}
275
+
276
+ ted:: remove ( attr. syntax ( ) )
255
277
}
256
278
}
257
279
@@ -1168,9 +1190,7 @@ struct Foo {
1168
1190
bar: String,
1169
1191
}
1170
1192
1171
- impl Debug for Foo {
1172
- $0
1173
- }
1193
+ impl Debug for Foo {$0}
1174
1194
"# ,
1175
1195
)
1176
1196
}
@@ -1191,9 +1211,7 @@ pub struct Foo {
1191
1211
bar: String,
1192
1212
}
1193
1213
1194
- impl Debug for Foo {
1195
- $0
1196
- }
1214
+ impl Debug for Foo {$0}
1197
1215
"# ,
1198
1216
)
1199
1217
}
@@ -1211,9 +1229,7 @@ struct Foo {}
1211
1229
#[derive(Display, Serialize)]
1212
1230
struct Foo {}
1213
1231
1214
- impl Debug for Foo {
1215
- $0
1216
- }
1232
+ impl Debug for Foo {$0}
1217
1233
"# ,
1218
1234
)
1219
1235
}
0 commit comments