@@ -2,15 +2,17 @@ use hir::{InFile, MacroFileIdExt, ModuleDef};
22use ide_db:: { helpers:: mod_path_to_ast, imports:: import_assets:: NameToImport , items_locator} ;
33use itertools:: Itertools ;
44use syntax:: {
5- ast:: { self , AstNode , HasName } ,
5+ ast:: { self , make, AstNode , HasName } ,
6+ ted,
67 SyntaxKind :: WHITESPACE ,
8+ T ,
79} ;
810
911use crate :: {
1012 assist_context:: { AssistContext , Assists , SourceChangeBuilder } ,
1113 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 ,
1416 } ,
1517 AssistId , AssistKind ,
1618} ;
@@ -132,35 +134,59 @@ fn add_assist(
132134 label,
133135 target,
134136 |builder| {
135- let insert_pos = adt. syntax ( ) . text_range ( ) . end ( ) ;
137+ let insert_after = ted:: Position :: after ( builder. make_mut ( adt. clone ( ) ) . syntax ( ) ) ;
138+
136139 let impl_def_with_items =
137140 impl_def_from_trait ( & ctx. sema , adt, & annotated_name, trait_, replace_trait_path) ;
138141 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+
140145 match ( ctx. config . snippet_cap , impl_def_with_items) {
141146 ( 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+ ) ;
143167 }
144- ( Some ( cap) , None ) => builder. insert_snippet (
145- cap,
146- insert_pos,
147- generate_trait_impl_text ( adt, & trait_path, " $0" ) ,
148- ) ,
149168 ( 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 ;
152170 if let ast:: AssocItem :: Fn ( ref func) = first_assoc_item {
153171 if let Some ( m) = func. syntax ( ) . descendants ( ) . find_map ( ast:: MacroCall :: cast)
154172 {
155173 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 ;
158177 }
159178 }
160179 }
161180
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+ ) ;
164190 }
165191 } ;
166192 } ,
@@ -190,28 +216,7 @@ fn impl_def_from_trait(
190216 if trait_items. is_empty ( ) {
191217 return None ;
192218 }
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 ( ) ) ) ;
215220
216221 let first_assoc_item =
217222 add_trait_assoc_items_to_impl ( sema, & trait_items, trait_, & impl_def, target_scope) ;
@@ -238,20 +243,37 @@ fn update_attribute(
238243 let has_more_derives = !new_derives. is_empty ( ) ;
239244
240245 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 ( ) ) ;
243266 } 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 )
252272 {
253- builder . delete ( line_break_range ) ;
273+ ted :: remove ( line_break )
254274 }
275+
276+ ted:: remove ( attr. syntax ( ) )
255277 }
256278}
257279
@@ -1168,9 +1190,7 @@ struct Foo {
11681190 bar: String,
11691191}
11701192
1171- impl Debug for Foo {
1172- $0
1173- }
1193+ impl Debug for Foo {$0}
11741194 "# ,
11751195 )
11761196 }
@@ -1191,9 +1211,7 @@ pub struct Foo {
11911211 bar: String,
11921212}
11931213
1194- impl Debug for Foo {
1195- $0
1196- }
1214+ impl Debug for Foo {$0}
11971215 "# ,
11981216 )
11991217 }
@@ -1211,9 +1229,7 @@ struct Foo {}
12111229#[derive(Display, Serialize)]
12121230struct Foo {}
12131231
1214- impl Debug for Foo {
1215- $0
1216- }
1232+ impl Debug for Foo {$0}
12171233 "# ,
12181234 )
12191235 }
0 commit comments