@@ -4,13 +4,15 @@ use syntax::{
44 ast:: { self , make, AstNode } ,
55 Direction , SmolStr ,
66 SyntaxKind :: { IDENT , WHITESPACE } ,
7- TextRange , TextSize ,
7+ TextSize ,
88} ;
99
1010use crate :: {
11- assist_config:: SnippetCap ,
1211 assist_context:: { AssistBuilder , AssistContext , Assists } ,
13- utils:: mod_path_to_ast,
12+ utils:: {
13+ add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor ,
14+ DefaultMethods ,
15+ } ,
1416 AssistId , AssistKind ,
1517} ;
1618
@@ -47,11 +49,10 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
4749 ctx. token_at_offset ( ) . find ( |t| t. kind ( ) == IDENT && * t. text ( ) != attr_name) ?;
4850 let trait_path = make:: path_unqualified ( make:: path_segment ( make:: name_ref ( trait_token. text ( ) ) ) ) ;
4951
50- let annotated = attr. syntax ( ) . siblings ( Direction :: Next ) . find_map ( ast:: Name :: cast) ?;
51- let annotated_name = annotated. syntax ( ) . text ( ) . to_string ( ) ;
52- let insert_pos = annotated. syntax ( ) . parent ( ) ?. text_range ( ) . end ( ) ;
52+ let annotated_name = attr. syntax ( ) . siblings ( Direction :: Next ) . find_map ( ast:: Name :: cast) ?;
53+ let insert_pos = annotated_name. syntax ( ) . parent ( ) ?. text_range ( ) . end ( ) ;
5354
54- let current_module = ctx. sema . scope ( annotated . syntax ( ) ) . module ( ) ?;
55+ let current_module = ctx. sema . scope ( annotated_name . syntax ( ) ) . module ( ) ?;
5556 let current_crate = current_module. krate ( ) ;
5657
5758 let found_traits = imports_locator:: find_imports ( & ctx. sema , current_crate, trait_token. text ( ) )
@@ -69,21 +70,22 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
6970 } ) ;
7071
7172 let mut no_traits_found = true ;
72- for ( trait_path, _trait ) in found_traits. inspect ( |_| no_traits_found = false ) {
73- add_assist ( acc, ctx. config . snippet_cap , & attr, & trait_path, & annotated_name, insert_pos) ?;
73+ for ( trait_path, trait_ ) in found_traits. inspect ( |_| no_traits_found = false ) {
74+ add_assist ( acc, ctx, & attr, & trait_path, Some ( trait_ ) , & annotated_name, insert_pos) ?;
7475 }
7576 if no_traits_found {
76- add_assist ( acc, ctx. config . snippet_cap , & attr, & trait_path, & annotated_name, insert_pos) ?;
77+ add_assist ( acc, ctx, & attr, & trait_path, None , & annotated_name, insert_pos) ?;
7778 }
7879 Some ( ( ) )
7980}
8081
8182fn add_assist (
8283 acc : & mut Assists ,
83- snippet_cap : Option < SnippetCap > ,
84+ ctx : & AssistContext ,
8485 attr : & ast:: Attr ,
8586 trait_path : & ast:: Path ,
86- annotated_name : & str ,
87+ trait_ : Option < hir:: Trait > ,
88+ annotated_name : & ast:: Name ,
8789 insert_pos : TextSize ,
8890) -> Option < ( ) > {
8991 let target = attr. syntax ( ) . text_range ( ) ;
@@ -92,25 +94,62 @@ fn add_assist(
9294 let trait_name = trait_path. segment ( ) . and_then ( |seg| seg. name_ref ( ) ) ?;
9395
9496 acc. add ( AssistId ( "add_custom_impl" , AssistKind :: Refactor ) , label, target, |builder| {
97+ let impl_def_with_items =
98+ impl_def_from_trait ( & ctx. sema , annotated_name, trait_, trait_path) ;
9599 update_attribute ( builder, & input, & trait_name, & attr) ;
96- match snippet_cap {
97- Some ( cap) => {
100+ match ( ctx. config . snippet_cap , impl_def_with_items) {
101+ ( None , _) => builder. insert (
102+ insert_pos,
103+ format ! ( "\n \n impl {} for {} {{\n \n }}" , trait_path, annotated_name) ,
104+ ) ,
105+ ( Some ( cap) , None ) => builder. insert_snippet (
106+ cap,
107+ insert_pos,
108+ format ! ( "\n \n impl {} for {} {{\n $0\n }}" , trait_path, annotated_name) ,
109+ ) ,
110+ ( Some ( cap) , Some ( ( impl_def, first_assoc_item) ) ) => {
111+ let mut cursor = Cursor :: Before ( first_assoc_item. syntax ( ) ) ;
112+ let placeholder;
113+ if let ast:: AssocItem :: Fn ( ref func) = first_assoc_item {
114+ if let Some ( m) = func. syntax ( ) . descendants ( ) . find_map ( ast:: MacroCall :: cast) {
115+ if m. syntax ( ) . text ( ) == "todo!()" {
116+ placeholder = m;
117+ cursor = Cursor :: Replace ( placeholder. syntax ( ) ) ;
118+ }
119+ }
120+ }
121+
98122 builder. insert_snippet (
99123 cap,
100124 insert_pos,
101- format ! ( "\n \n impl {} for {} {{\n $0\n }}" , trait_path, annotated_name) ,
102- ) ;
103- }
104- None => {
105- builder. insert (
106- insert_pos,
107- format ! ( "\n \n impl {} for {} {{\n \n }}" , trait_path, annotated_name) ,
108- ) ;
125+ format ! ( "\n \n {}" , render_snippet( cap, impl_def. syntax( ) , cursor) ) ,
126+ )
109127 }
110- }
128+ } ;
111129 } )
112130}
113131
132+ fn impl_def_from_trait (
133+ sema : & hir:: Semantics < ide_db:: RootDatabase > ,
134+ annotated_name : & ast:: Name ,
135+ trait_ : Option < hir:: Trait > ,
136+ trait_path : & ast:: Path ,
137+ ) -> Option < ( ast:: Impl , ast:: AssocItem ) > {
138+ let trait_ = trait_?;
139+ let target_scope = sema. scope ( annotated_name. syntax ( ) ) ;
140+ let trait_items = filter_assoc_items ( sema. db , & trait_. items ( sema. db ) , DefaultMethods :: No ) ;
141+ if trait_items. is_empty ( ) {
142+ return None ;
143+ }
144+ let impl_def = make:: impl_trait (
145+ trait_path. clone ( ) ,
146+ make:: path_unqualified ( make:: path_segment ( make:: name_ref ( annotated_name. text ( ) ) ) ) ,
147+ ) ;
148+ let ( impl_def, first_assoc_item) =
149+ add_trait_assoc_items_to_impl ( sema, trait_items, trait_, impl_def, target_scope) ;
150+ Some ( ( impl_def, first_assoc_item) )
151+ }
152+
114153fn update_attribute (
115154 builder : & mut AssistBuilder ,
116155 input : & ast:: TokenTree ,
@@ -133,13 +172,14 @@ fn update_attribute(
133172 let attr_range = attr. syntax ( ) . text_range ( ) ;
134173 builder. delete ( attr_range) ;
135174
136- let line_break_range = attr
175+ if let Some ( line_break_range) = attr
137176 . syntax ( )
138177 . next_sibling_or_token ( )
139178 . filter ( |t| t. kind ( ) == WHITESPACE )
140179 . map ( |t| t. text_range ( ) )
141- . unwrap_or_else ( || TextRange :: new ( TextSize :: from ( 0 ) , TextSize :: from ( 0 ) ) ) ;
142- builder. delete ( line_break_range) ;
180+ {
181+ builder. delete ( line_break_range) ;
182+ }
143183 }
144184}
145185
@@ -150,12 +190,17 @@ mod tests {
150190 use super :: * ;
151191
152192 #[ test]
153- fn add_custom_impl_qualified ( ) {
193+ fn add_custom_impl_debug ( ) {
154194 check_assist (
155195 add_custom_impl,
156196 "
157197mod fmt {
158- pub trait Debug {}
198+ pub struct Error;
199+ pub type Result = Result<(), Error>;
200+ pub struct Formatter<'a>;
201+ pub trait Debug {
202+ fn fmt(&self, f: &mut Formatter<'_>) -> Result;
203+ }
159204}
160205
161206#[derive(Debu<|>g)]
@@ -165,15 +210,71 @@ struct Foo {
165210" ,
166211 "
167212mod fmt {
168- pub trait Debug {}
213+ pub struct Error;
214+ pub type Result = Result<(), Error>;
215+ pub struct Formatter<'a>;
216+ pub trait Debug {
217+ fn fmt(&self, f: &mut Formatter<'_>) -> Result;
218+ }
169219}
170220
171221struct Foo {
172222 bar: String,
173223}
174224
175225impl fmt::Debug for Foo {
176- $0
226+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227+ ${0:todo!()}
228+ }
229+ }
230+ " ,
231+ )
232+ }
233+ #[ test]
234+ fn add_custom_impl_all ( ) {
235+ check_assist (
236+ add_custom_impl,
237+ "
238+ mod foo {
239+ pub trait Bar {
240+ type Qux;
241+ const Baz: usize = 42;
242+ const Fez: usize;
243+ fn foo();
244+ fn bar() {}
245+ }
246+ }
247+
248+ #[derive(<|>Bar)]
249+ struct Foo {
250+ bar: String,
251+ }
252+ " ,
253+ "
254+ mod foo {
255+ pub trait Bar {
256+ type Qux;
257+ const Baz: usize = 42;
258+ const Fez: usize;
259+ fn foo();
260+ fn bar() {}
261+ }
262+ }
263+
264+ struct Foo {
265+ bar: String,
266+ }
267+
268+ impl foo::Bar for Foo {
269+ $0type Qux;
270+
271+ const Baz: usize = 42;
272+
273+ const Fez: usize;
274+
275+ fn foo() {
276+ todo!()
277+ }
177278}
178279" ,
179280 )
0 commit comments