@@ -7,6 +7,7 @@ use ide_db::{
77 defs:: { Definition , NameClass , NameRefClass } ,
88 search:: { FileReference , SearchScope } ,
99} ;
10+ use parser:: SyntaxKind :: WHITESPACE ;
1011use stdx:: format_to;
1112use syntax:: {
1213 algo:: find_node_at_range,
@@ -59,6 +60,20 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
5960 syntax:: NodeOrToken :: Token ( t) => t. parent ( ) ?,
6061 } ;
6162
63+ //If the selection is inside impl block, we need to place new module outside impl block,
64+ //as impl blocks cannot contain modules
65+
66+ let mut impl_parent: Option < ast:: Impl > = None ;
67+ let mut impl_child_count: usize = 0 ;
68+ if let Some ( parent_assoc_list) = node. parent ( ) {
69+ if let Some ( parent_impl) = parent_assoc_list. parent ( ) {
70+ if let Some ( impl_) = ast:: Impl :: cast ( parent_impl) {
71+ impl_child_count = parent_assoc_list. children ( ) . count ( ) ;
72+ impl_parent = Some ( impl_) ;
73+ }
74+ }
75+ }
76+
6277 let mut curr_parent_module: Option < ast:: Module > = None ;
6378 if let Some ( mod_syn_opt) = node. ancestors ( ) . find ( |it| ast:: Module :: can_cast ( it. kind ( ) ) ) {
6479 curr_parent_module = ast:: Module :: cast ( mod_syn_opt) ;
@@ -98,18 +113,55 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
98113 "Extract Module" ,
99114 module. text_range ,
100115 |builder| {
101- let _ = & module;
116+ let mut body_items: Vec < String > = Vec :: new ( ) ;
117+ let mut items_to_be_processed: Vec < ast:: Item > = module. body_items . clone ( ) ;
118+ let mut new_item_indent = old_item_indent + 1 ;
102119
103- let mut body_items = Vec :: new ( ) ;
104- let new_item_indent = old_item_indent + 1 ;
105- for item in module. body_items {
120+ if impl_parent. is_some ( ) {
121+ new_item_indent = old_item_indent + 2 ;
122+ } else {
123+ items_to_be_processed = [ module. use_items . clone ( ) , items_to_be_processed] . concat ( ) ;
124+ }
125+
126+ for item in items_to_be_processed {
106127 let item = item. indent ( IndentLevel ( 1 ) ) ;
107128 let mut indented_item = String :: new ( ) ;
108129 format_to ! ( indented_item, "{}{}" , new_item_indent, item. to_string( ) ) ;
109130 body_items. push ( indented_item) ;
110131 }
111132
112- let body = body_items. join ( "\n \n " ) ;
133+ let mut body = body_items. join ( "\n \n " ) ;
134+
135+ if let Some ( impl_) = & impl_parent {
136+ let mut impl_body_def = String :: new ( ) ;
137+
138+ if let Some ( self_ty) = impl_. self_ty ( ) {
139+ format_to ! (
140+ impl_body_def,
141+ "{}impl {} {{\n {}\n {}}}" ,
142+ old_item_indent + 1 ,
143+ self_ty. to_string( ) ,
144+ body,
145+ old_item_indent + 1
146+ ) ;
147+
148+ body = impl_body_def;
149+
150+ // Add the import for enum/struct corresponding to given impl block
151+ if let Some ( _) = module. make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) {
152+ for item in module. use_items {
153+ let mut indented_item = String :: new ( ) ;
154+ format_to ! (
155+ indented_item,
156+ "{}{}" ,
157+ old_item_indent + 1 ,
158+ item. to_string( )
159+ ) ;
160+ body = format ! ( "{}\n \n {}" , indented_item, body) ;
161+ }
162+ }
163+ }
164+ }
113165
114166 let mut module_def = String :: new ( ) ;
115167
@@ -135,7 +187,29 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
135187 for import_path_text_range in import_paths_to_be_removed {
136188 builder. delete ( import_path_text_range) ;
137189 }
138- builder. replace ( module. text_range , module_def)
190+
191+ if let Some ( impl_) = impl_parent {
192+ let node_to_be_removed;
193+
194+ // Remove complete impl block if it has only one child (as such it will be empty
195+ // after deleting that child)
196+ if impl_child_count == 1 {
197+ node_to_be_removed = impl_. syntax ( )
198+ } else {
199+ //Remove selected node
200+ node_to_be_removed = & node;
201+ }
202+
203+ builder. delete ( node_to_be_removed. text_range ( ) ) ;
204+ // Remove preceding indentation from node
205+ if let Some ( range) = indent_range_before_given_node ( & node_to_be_removed) {
206+ builder. delete ( range) ;
207+ }
208+
209+ builder. insert ( impl_. syntax ( ) . text_range ( ) . end ( ) , format ! ( "\n \n {}" , module_def) ) ;
210+ } else {
211+ builder. replace ( module. text_range , module_def)
212+ }
139213 } ,
140214 )
141215}
@@ -144,16 +218,24 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
144218struct Module {
145219 text_range : TextRange ,
146220 name : String ,
147- body_items : Vec < ast:: Item > ,
221+ body_items : Vec < ast:: Item > , // All items except use items
222+ use_items : Vec < ast:: Item > , // Use items are kept separately as they help when the selection is inside an impl block, we can directly take these items and keep them outside generated impl block inside generated module
148223}
149224
150225fn extract_target ( node : & SyntaxNode , selection_range : TextRange ) -> Option < Module > {
226+ let mut use_items = vec ! [ ] ;
227+
151228 let mut body_items: Vec < ast:: Item > = node
152229 . children ( )
153230 . filter_map ( |child| {
154- if let Some ( item) = ast:: Item :: cast ( child) {
155- if selection_range. contains_range ( item. syntax ( ) . text_range ( ) ) {
156- return Some ( item) ;
231+ if selection_range. contains_range ( child. text_range ( ) ) {
232+ let child_kind = child. kind ( ) ;
233+ if let Some ( item) = ast:: Item :: cast ( child) {
234+ if ast:: Use :: can_cast ( child_kind) {
235+ use_items. push ( item) ;
236+ } else {
237+ return Some ( item) ;
238+ }
157239 }
158240 return None ;
159241 }
@@ -165,7 +247,7 @@ fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Modul
165247 body_items. push ( node_item) ;
166248 }
167249
168- Some ( Module { text_range : selection_range, name : "modname" . to_string ( ) , body_items } )
250+ Some ( Module { text_range : selection_range, name : "modname" . to_string ( ) , body_items, use_items } )
169251}
170252
171253impl Module {
@@ -543,23 +625,27 @@ impl Module {
543625 let use_ =
544626 make:: use_ ( None , make:: use_tree ( make:: join_paths ( use_tree_str) , None , None , false ) ) ;
545627 if let Some ( item) = ast:: Item :: cast ( use_. syntax ( ) . clone ( ) ) {
546- self . body_items . insert ( 0 , item) ;
628+ self . use_items . insert ( 0 , item) ;
547629 }
548630 }
549631
550632 import_path_to_be_removed
551633 }
552634
553- fn make_use_stmt_of_node_with_super ( & mut self , node_syntax : & SyntaxNode ) {
635+ fn make_use_stmt_of_node_with_super ( & mut self , node_syntax : & SyntaxNode ) -> Option < ast :: Item > {
554636 let super_path = make:: ext:: ident_path ( "super" ) ;
555637 let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
556638 let use_ = make:: use_ (
557639 None ,
558640 make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
559641 ) ;
642+
560643 if let Some ( item) = ast:: Item :: cast ( use_. syntax ( ) . clone ( ) ) {
561- self . body_items . insert ( 0 , item) ;
644+ self . use_items . insert ( 0 , item. clone ( ) ) ;
645+ return Some ( item) ;
562646 }
647+
648+ return None ;
563649 }
564650
565651 fn process_use_stmt_for_import_resolve (
@@ -859,6 +945,14 @@ fn compare_hir_and_ast_module(
859945 return Some ( ( ) ) ;
860946}
861947
948+ fn indent_range_before_given_node ( node : & SyntaxNode ) -> Option < TextRange > {
949+ let x = node. siblings_with_tokens ( syntax:: Direction :: Prev ) . find ( |x| {
950+ return x. kind ( ) == WHITESPACE ;
951+ } ) ?;
952+
953+ return Some ( x. text_range ( ) ) ;
954+ }
955+
862956#[ cfg( test) ]
863957mod tests {
864958 use crate :: tests:: { check_assist, check_assist_not_applicable} ;
@@ -1435,4 +1529,63 @@ mod modname {
14351529 " ,
14361530 )
14371531 }
1532+
1533+ #[ test]
1534+ fn test_if_inside_impl_block_generate_module_outside ( ) {
1535+ check_assist (
1536+ extract_module,
1537+ r"
1538+ struct A {}
1539+
1540+ impl A {
1541+ $0fn foo() {}$0
1542+ fn bar() {}
1543+ }
1544+ " ,
1545+ r"
1546+ struct A {}
1547+
1548+ impl A {
1549+ fn bar() {}
1550+ }
1551+
1552+ mod modname {
1553+ use super::A;
1554+
1555+ impl A {
1556+ pub(crate) fn foo() {}
1557+ }
1558+ }
1559+ " ,
1560+ )
1561+ }
1562+
1563+ #[ test]
1564+ fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child ( ) {
1565+ check_assist (
1566+ extract_module,
1567+ r"
1568+ struct A {}
1569+ struct B {}
1570+
1571+ impl A {
1572+ $0fn foo(x: B) {}$0
1573+ }
1574+ " ,
1575+ r"
1576+ struct A {}
1577+ struct B {}
1578+
1579+ mod modname {
1580+ use super::B;
1581+
1582+ use super::A;
1583+
1584+ impl A {
1585+ pub(crate) fn foo(x: B) {}
1586+ }
1587+ }
1588+ " ,
1589+ )
1590+ }
14381591}
0 commit comments