1
- use std:: iter ;
1
+ use std:: ops :: RangeInclusive ;
2
2
3
- use either:: Either ;
4
3
use hir:: { HasSource , ModuleSource } ;
5
4
use ide_db:: {
6
5
FileId , FxHashMap , FxHashSet ,
@@ -82,7 +81,15 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
82
81
curr_parent_module = ast:: Module :: cast ( mod_syn_opt) ;
83
82
}
84
83
85
- let mut module = extract_target ( & node, ctx. selection_trimmed ( ) ) ?;
84
+ let selection_range = ctx. selection_trimmed ( ) ;
85
+ let ( mut module, module_text_range) = if let Some ( item) = ast:: Item :: cast ( node. clone ( ) ) {
86
+ let module = extract_single_target ( & item) ;
87
+ ( module, node. text_range ( ) )
88
+ } else {
89
+ let ( module, range) = extract_child_target ( & node, selection_range) ?;
90
+ let module_text_range = range. start ( ) . text_range ( ) . cover ( range. end ( ) . text_range ( ) ) ;
91
+ ( module, module_text_range)
92
+ } ;
86
93
if module. body_items . is_empty ( ) {
87
94
return None ;
88
95
}
@@ -92,7 +99,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
92
99
acc. add (
93
100
AssistId :: refactor_extract ( "extract_module" ) ,
94
101
"Extract Module" ,
95
- module . text_range ,
102
+ module_text_range ,
96
103
|builder| {
97
104
//This takes place in three steps:
98
105
//
@@ -110,17 +117,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
110
117
//for change_visibility and usages for first point mentioned above in the process
111
118
112
119
let ( usages_to_be_processed, record_fields, use_stmts_to_be_inserted) =
113
- module. get_usages_and_record_fields ( ctx) ;
120
+ module. get_usages_and_record_fields ( ctx, module_text_range ) ;
114
121
115
122
builder. edit_file ( ctx. vfs_file_id ( ) ) ;
116
123
use_stmts_to_be_inserted. into_iter ( ) . for_each ( |( _, use_stmt) | {
117
124
builder. insert ( ctx. selection_trimmed ( ) . end ( ) , format ! ( "\n {use_stmt}" ) ) ;
118
125
} ) ;
119
126
120
- let import_paths_to_be_removed = module. resolve_imports ( curr_parent_module, ctx) ;
127
+ let import_items = module. resolve_imports ( curr_parent_module, ctx) ;
121
128
module. change_visibility ( record_fields) ;
122
129
123
- let module_def = generate_module_def ( & impl_parent, & mut module, old_item_indent) ;
130
+ let module_def = generate_module_def ( & impl_parent, module, old_item_indent) . to_string ( ) ;
124
131
125
132
let mut usages_to_be_processed_for_cur_file = vec ! [ ] ;
126
133
for ( file_id, usages) in usages_to_be_processed {
@@ -157,54 +164,63 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
157
164
158
165
builder. insert ( impl_. syntax ( ) . text_range ( ) . end ( ) , format ! ( "\n \n {module_def}" ) ) ;
159
166
} else {
160
- for import_path_text_range in import_paths_to_be_removed {
161
- if module. text_range . intersect ( import_path_text_range) . is_some ( ) {
162
- module. text_range = module. text_range . cover ( import_path_text_range) ;
163
- } else {
164
- builder. delete ( import_path_text_range) ;
167
+ for import_item in import_items {
168
+ if !module_text_range. contains_range ( import_item) {
169
+ builder. delete ( import_item) ;
165
170
}
166
171
}
167
-
168
- builder. replace ( module. text_range , module_def)
172
+ builder. replace ( module_text_range, module_def)
169
173
}
170
174
} ,
171
175
)
172
176
}
173
177
174
178
fn generate_module_def (
175
179
parent_impl : & Option < ast:: Impl > ,
176
- module : & mut Module ,
180
+ module : Module ,
177
181
old_indent : IndentLevel ,
178
- ) -> String {
179
- let ( items_to_be_processed, new_item_indent) = if parent_impl. is_some ( ) {
180
- ( Either :: Left ( module. body_items . iter ( ) ) , old_indent + 2 )
182
+ ) -> ast:: Module {
183
+ let Module { name, body_items, use_items } = module;
184
+ let items = if let Some ( self_ty) = parent_impl. as_ref ( ) . and_then ( |imp| imp. self_ty ( ) ) {
185
+ let assoc_items = body_items
186
+ . into_iter ( )
187
+ . map ( |item| item. syntax ( ) . clone ( ) )
188
+ . filter_map ( ast:: AssocItem :: cast)
189
+ . map ( |it| it. indent ( IndentLevel ( 1 ) ) )
190
+ . collect_vec ( ) ;
191
+ let assoc_item_list = make:: assoc_item_list ( Some ( assoc_items) ) ;
192
+ let impl_ = make:: impl_ ( None , None , None , self_ty. clone ( ) , None , Some ( assoc_item_list) ) ;
193
+ // Add the import for enum/struct corresponding to given impl block
194
+ let use_impl = make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
195
+ let mut module_body_items = use_items;
196
+ module_body_items. insert ( 0 , use_impl) ;
197
+ module_body_items. push ( ast:: Item :: Impl ( impl_) ) ;
198
+ module_body_items
181
199
} else {
182
- ( Either :: Right ( module . use_items . iter ( ) . chain ( module . body_items . iter ( ) ) ) , old_indent + 1 )
200
+ [ use_items, body_items] . concat ( )
183
201
} ;
184
202
185
- let mut body = items_to_be_processed
186
- . map ( |item| item. indent ( IndentLevel ( 1 ) ) )
187
- . map ( |item| format ! ( "{new_item_indent}{item}" ) )
188
- . join ( "\n \n " ) ;
203
+ let items = items. into_iter ( ) . map ( |it| it. reset_indent ( ) . indent ( IndentLevel ( 1 ) ) ) . collect_vec ( ) ;
204
+ let module_body = make:: item_list ( Some ( items) ) ;
189
205
190
- if let Some ( self_ty ) = parent_impl . as_ref ( ) . and_then ( |imp| imp . self_ty ( ) ) {
191
- let impl_indent = old_indent + 1 ;
192
- body = format ! ( "{impl_indent}impl {self_ty} {{ \n {body} \n {impl_indent}}}" ) ;
206
+ let module_name = make :: name ( name ) ;
207
+ make :: mod_ ( module_name , Some ( module_body ) ) . indent ( old_indent)
208
+ }
193
209
194
- // Add the import for enum/struct corresponding to given impl block
195
- module. make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
196
- for item in module. use_items . iter ( ) {
197
- body = format ! ( "{impl_indent}{item}\n \n {body}" ) ;
198
- }
199
- }
210
+ fn make_use_stmt_of_node_with_super ( node_syntax : & SyntaxNode ) -> ast:: Item {
211
+ let super_path = make:: ext:: ident_path ( "super" ) ;
212
+ let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
213
+ let use_ = make:: use_ (
214
+ None ,
215
+ None ,
216
+ make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
217
+ ) ;
200
218
201
- let module_name = module. name ;
202
- format ! ( "mod {module_name} {{\n {body}\n {old_indent}}}" )
219
+ ast:: Item :: from ( use_)
203
220
}
204
221
205
222
#[ derive( Debug ) ]
206
223
struct Module {
207
- text_range : TextRange ,
208
224
name : & ' static str ,
209
225
/// All items except use items.
210
226
body_items : Vec < ast:: Item > ,
@@ -214,22 +230,37 @@ struct Module {
214
230
use_items : Vec < ast:: Item > ,
215
231
}
216
232
217
- fn extract_target ( node : & SyntaxNode , selection_range : TextRange ) -> Option < Module > {
233
+ fn extract_single_target ( node : & ast:: Item ) -> Module {
234
+ let ( body_items, use_items) = if matches ! ( node, ast:: Item :: Use ( _) ) {
235
+ ( Vec :: new ( ) , vec ! [ node. clone( ) ] )
236
+ } else {
237
+ ( vec ! [ node. clone( ) ] , Vec :: new ( ) )
238
+ } ;
239
+ let name = "modname" ;
240
+ Module { name, body_items, use_items }
241
+ }
242
+
243
+ fn extract_child_target (
244
+ node : & SyntaxNode ,
245
+ selection_range : TextRange ,
246
+ ) -> Option < ( Module , RangeInclusive < SyntaxNode > ) > {
218
247
let selected_nodes = node
219
248
. children ( )
220
249
. filter ( |node| selection_range. contains_range ( node. text_range ( ) ) )
221
- . chain ( iter:: once ( node. clone ( ) ) ) ;
222
- let ( use_items, body_items) = selected_nodes
223
250
. filter_map ( ast:: Item :: cast)
224
- . partition ( |item| matches ! ( item, ast:: Item :: Use ( ..) ) ) ;
225
-
226
- Some ( Module { text_range : selection_range, name : "modname" , body_items, use_items } )
251
+ . collect_vec ( ) ;
252
+ let start = selected_nodes. first ( ) ?. syntax ( ) . clone ( ) ;
253
+ let end = selected_nodes. last ( ) ?. syntax ( ) . clone ( ) ;
254
+ let ( use_items, body_items) : ( Vec < ast:: Item > , Vec < ast:: Item > ) =
255
+ selected_nodes. into_iter ( ) . partition ( |item| matches ! ( item, ast:: Item :: Use ( ..) ) ) ;
256
+ Some ( ( Module { name : "modname" , body_items, use_items } , start..=end) )
227
257
}
228
258
229
259
impl Module {
230
260
fn get_usages_and_record_fields (
231
261
& self ,
232
262
ctx : & AssistContext < ' _ > ,
263
+ replace_range : TextRange ,
233
264
) -> ( FxHashMap < FileId , Vec < ( TextRange , String ) > > , Vec < SyntaxNode > , FxHashMap < TextSize , ast:: Use > )
234
265
{
235
266
let mut adt_fields = Vec :: new ( ) ;
@@ -247,7 +278,7 @@ impl Module {
247
278
ast:: Adt ( it) => {
248
279
if let Some ( nod ) = ctx. sema. to_def( & it) {
249
280
let node_def = Definition :: Adt ( nod) ;
250
- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
281
+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
251
282
252
283
//Enum Fields are not allowed to explicitly specify pub, it is implied
253
284
match it {
@@ -281,30 +312,30 @@ impl Module {
281
312
ast:: TypeAlias ( it) => {
282
313
if let Some ( nod ) = ctx. sema. to_def( & it) {
283
314
let node_def = Definition :: TypeAlias ( nod) ;
284
- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
315
+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
285
316
}
286
317
} ,
287
318
ast:: Const ( it) => {
288
319
if let Some ( nod ) = ctx. sema. to_def( & it) {
289
320
let node_def = Definition :: Const ( nod) ;
290
- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
321
+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
291
322
}
292
323
} ,
293
324
ast:: Static ( it) => {
294
325
if let Some ( nod ) = ctx. sema. to_def( & it) {
295
326
let node_def = Definition :: Static ( nod) ;
296
- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
327
+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
297
328
}
298
329
} ,
299
330
ast:: Fn ( it) => {
300
331
if let Some ( nod ) = ctx. sema. to_def( & it) {
301
332
let node_def = Definition :: Function ( nod) ;
302
- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
333
+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
303
334
}
304
335
} ,
305
336
ast:: Macro ( it) => {
306
337
if let Some ( nod) = ctx. sema. to_def( & it) {
307
- self . expand_and_group_usages_file_wise( ctx, Definition :: Macro ( nod) , & mut refs, & mut use_stmts_to_be_inserted) ;
338
+ self . expand_and_group_usages_file_wise( ctx, replace_range , Definition :: Macro ( nod) , & mut refs, & mut use_stmts_to_be_inserted) ;
308
339
}
309
340
} ,
310
341
_ => ( ) ,
@@ -318,6 +349,7 @@ impl Module {
318
349
fn expand_and_group_usages_file_wise (
319
350
& self ,
320
351
ctx : & AssistContext < ' _ > ,
352
+ replace_range : TextRange ,
321
353
node_def : Definition ,
322
354
refs_in_files : & mut FxHashMap < FileId , Vec < ( TextRange , String ) > > ,
323
355
use_stmts_to_be_inserted : & mut FxHashMap < TextSize , ast:: Use > ,
@@ -327,7 +359,7 @@ impl Module {
327
359
syntax:: NodeOrToken :: Node ( node) => node,
328
360
syntax:: NodeOrToken :: Token ( tok) => tok. parent ( ) . unwrap ( ) , // won't panic
329
361
} ;
330
- let out_of_sel = |node : & SyntaxNode | !self . text_range . contains_range ( node. text_range ( ) ) ;
362
+ let out_of_sel = |node : & SyntaxNode | !replace_range . contains_range ( node. text_range ( ) ) ;
331
363
let mut use_stmts_set = FxHashSet :: default ( ) ;
332
364
333
365
for ( file_id, refs) in node_def. usages ( & ctx. sema ) . all ( ) {
@@ -527,7 +559,8 @@ impl Module {
527
559
// mod -> ust_stmt transversal
528
560
// true | false -> super import insertion
529
561
// true | true -> super import insertion
530
- self . make_use_stmt_of_node_with_super ( use_node) ;
562
+ let super_use_node = make_use_stmt_of_node_with_super ( use_node) ;
563
+ self . use_items . insert ( 0 , super_use_node) ;
531
564
}
532
565
None => { }
533
566
}
@@ -556,7 +589,8 @@ impl Module {
556
589
557
590
use_tree_paths = Some ( use_tree_str) ;
558
591
} else if def_in_mod && def_out_sel {
559
- self . make_use_stmt_of_node_with_super ( use_node) ;
592
+ let super_use_node = make_use_stmt_of_node_with_super ( use_node) ;
593
+ self . use_items . insert ( 0 , super_use_node) ;
560
594
}
561
595
}
562
596
@@ -596,20 +630,6 @@ impl Module {
596
630
import_path_to_be_removed
597
631
}
598
632
599
- fn make_use_stmt_of_node_with_super ( & mut self , node_syntax : & SyntaxNode ) -> ast:: Item {
600
- let super_path = make:: ext:: ident_path ( "super" ) ;
601
- let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
602
- let use_ = make:: use_ (
603
- None ,
604
- None ,
605
- make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
606
- ) ;
607
-
608
- let item = ast:: Item :: from ( use_) ;
609
- self . use_items . insert ( 0 , item. clone ( ) ) ;
610
- item
611
- }
612
-
613
633
fn process_use_stmt_for_import_resolve (
614
634
& self ,
615
635
use_stmt : Option < ast:: Use > ,
@@ -1424,10 +1444,10 @@ $0fn foo(x: B) {}$0
1424
1444
struct B {}
1425
1445
1426
1446
mod modname {
1427
- use super::B;
1428
-
1429
1447
use super::A;
1430
1448
1449
+ use super::B;
1450
+
1431
1451
impl A {
1432
1452
pub(crate) fn foo(x: B) {}
1433
1453
}
@@ -1739,4 +1759,27 @@ fn main() {
1739
1759
"# ,
1740
1760
) ;
1741
1761
}
1762
+
1763
+ #[ test]
1764
+ fn test_miss_select_item ( ) {
1765
+ check_assist (
1766
+ extract_module,
1767
+ r#"
1768
+ mod foo {
1769
+ mod $0bar {
1770
+ fn foo(){}$0
1771
+ }
1772
+ }
1773
+ "# ,
1774
+ r#"
1775
+ mod foo {
1776
+ mod modname {
1777
+ pub(crate) mod bar {
1778
+ fn foo(){}
1779
+ }
1780
+ }
1781
+ }
1782
+ "# ,
1783
+ )
1784
+ }
1742
1785
}
0 commit comments