@@ -7,6 +7,7 @@ use ide_db::{
7
7
defs:: { Definition , NameClass , NameRefClass } ,
8
8
search:: { FileReference , SearchScope } ,
9
9
} ;
10
+ use parser:: SyntaxKind :: WHITESPACE ;
10
11
use stdx:: format_to;
11
12
use syntax:: {
12
13
algo:: find_node_at_range,
@@ -59,6 +60,20 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
59
60
syntax:: NodeOrToken :: Token ( t) => t. parent ( ) ?,
60
61
} ;
61
62
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
+
62
77
let mut curr_parent_module: Option < ast:: Module > = None ;
63
78
if let Some ( mod_syn_opt) = node. ancestors ( ) . find ( |it| ast:: Module :: can_cast ( it. kind ( ) ) ) {
64
79
curr_parent_module = ast:: Module :: cast ( mod_syn_opt) ;
@@ -98,18 +113,55 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
98
113
"Extract Module" ,
99
114
module. text_range ,
100
115
|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 ;
102
119
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 {
106
127
let item = item. indent ( IndentLevel ( 1 ) ) ;
107
128
let mut indented_item = String :: new ( ) ;
108
129
format_to ! ( indented_item, "{}{}" , new_item_indent, item. to_string( ) ) ;
109
130
body_items. push ( indented_item) ;
110
131
}
111
132
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
+ }
113
165
114
166
let mut module_def = String :: new ( ) ;
115
167
@@ -135,7 +187,29 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
135
187
for import_path_text_range in import_paths_to_be_removed {
136
188
builder. delete ( import_path_text_range) ;
137
189
}
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
+ }
139
213
} ,
140
214
)
141
215
}
@@ -144,16 +218,24 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<(
144
218
struct Module {
145
219
text_range : TextRange ,
146
220
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
148
223
}
149
224
150
225
fn extract_target ( node : & SyntaxNode , selection_range : TextRange ) -> Option < Module > {
226
+ let mut use_items = vec ! [ ] ;
227
+
151
228
let mut body_items: Vec < ast:: Item > = node
152
229
. children ( )
153
230
. 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
+ }
157
239
}
158
240
return None ;
159
241
}
@@ -165,7 +247,7 @@ fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Modul
165
247
body_items. push ( node_item) ;
166
248
}
167
249
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 } )
169
251
}
170
252
171
253
impl Module {
@@ -543,23 +625,27 @@ impl Module {
543
625
let use_ =
544
626
make:: use_ ( None , make:: use_tree ( make:: join_paths ( use_tree_str) , None , None , false ) ) ;
545
627
if let Some ( item) = ast:: Item :: cast ( use_. syntax ( ) . clone ( ) ) {
546
- self . body_items . insert ( 0 , item) ;
628
+ self . use_items . insert ( 0 , item) ;
547
629
}
548
630
}
549
631
550
632
import_path_to_be_removed
551
633
}
552
634
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 > {
554
636
let super_path = make:: ext:: ident_path ( "super" ) ;
555
637
let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
556
638
let use_ = make:: use_ (
557
639
None ,
558
640
make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
559
641
) ;
642
+
560
643
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) ;
562
646
}
647
+
648
+ return None ;
563
649
}
564
650
565
651
fn process_use_stmt_for_import_resolve (
@@ -859,6 +945,14 @@ fn compare_hir_and_ast_module(
859
945
return Some ( ( ) ) ;
860
946
}
861
947
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
+
862
956
#[ cfg( test) ]
863
957
mod tests {
864
958
use crate :: tests:: { check_assist, check_assist_not_applicable} ;
@@ -1435,4 +1529,63 @@ mod modname {
1435
1529
" ,
1436
1530
)
1437
1531
}
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
+ }
1438
1591
}
0 commit comments