1
1
use std:: iter;
2
2
3
+ use either:: Either ;
3
4
use hir:: { HasSource , HirFileIdExt , ModuleSource } ;
4
5
use ide_db:: {
5
6
assists:: { AssistId , AssistKind } ,
@@ -10,15 +11,14 @@ use ide_db::{
10
11
} ;
11
12
use itertools:: Itertools ;
12
13
use smallvec:: SmallVec ;
13
- use stdx:: format_to;
14
14
use syntax:: {
15
15
algo:: find_node_at_range,
16
16
ast:: {
17
17
self ,
18
18
edit:: { AstNodeEdit , IndentLevel } ,
19
19
make, HasVisibility ,
20
20
} ,
21
- match_ast, ted, AstNode , SourceFile ,
21
+ match_ast, ted, AstNode ,
22
22
SyntaxKind :: { self , WHITESPACE } ,
23
23
SyntaxNode , TextRange ,
24
24
} ;
@@ -114,63 +114,23 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
114
114
let import_paths_to_be_removed = module. resolve_imports ( curr_parent_module, ctx) ;
115
115
module. change_visibility ( record_fields) ;
116
116
117
- let mut body_items: Vec < String > = Vec :: new ( ) ;
118
- let mut items_to_be_processed: Vec < ast:: Item > = module. body_items . clone ( ) ;
117
+ let module_def = generate_module_def ( & impl_parent, & mut module, old_item_indent) ;
119
118
120
- let new_item_indent = if impl_parent. is_some ( ) {
121
- old_item_indent + 2
122
- } else {
123
- items_to_be_processed = [ module. use_items . clone ( ) , items_to_be_processed] . concat ( ) ;
124
- old_item_indent + 1
125
- } ;
126
-
127
- for item in items_to_be_processed {
128
- let item = item. indent ( IndentLevel ( 1 ) ) ;
129
- let mut indented_item = String :: new ( ) ;
130
- format_to ! ( indented_item, "{new_item_indent}{item}" ) ;
131
- body_items. push ( indented_item) ;
132
- }
133
-
134
- let mut body = body_items. join ( "\n \n " ) ;
135
-
136
- if let Some ( impl_) = & impl_parent {
137
- if let Some ( self_ty) = impl_. self_ty ( ) {
138
- let impl_indent = old_item_indent + 1 ;
139
- let mut impl_body_def = String :: new ( ) ;
140
- format_to ! (
141
- impl_body_def,
142
- "{impl_indent}impl {self_ty} {{\n {body}\n {impl_indent}}}" ,
143
- ) ;
144
- body = impl_body_def;
145
-
146
- // Add the import for enum/struct corresponding to given impl block
147
- module. make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
148
- for item in module. use_items {
149
- let item_indent = old_item_indent + 1 ;
150
- body = format ! ( "{item_indent}{item}\n \n {body}" ) ;
151
- }
152
- }
153
- }
154
-
155
- let mut module_def = String :: new ( ) ;
156
- let module_name = module. name ;
157
- format_to ! ( module_def, "mod {module_name} {{\n {body}\n {old_item_indent}}}" ) ;
158
-
159
- let mut usages_to_be_updated_for_curr_file = vec ! [ ] ;
160
- for usages_to_be_updated_for_file in usages_to_be_processed {
161
- if usages_to_be_updated_for_file. 0 == ctx. file_id ( ) {
162
- usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file. 1 ;
119
+ let mut usages_to_be_processed_for_cur_file = vec ! [ ] ;
120
+ for ( file_id, usages) in usages_to_be_processed {
121
+ if file_id == ctx. file_id ( ) {
122
+ usages_to_be_processed_for_cur_file = usages;
163
123
continue ;
164
124
}
165
- builder. edit_file ( usages_to_be_updated_for_file . 0 ) ;
166
- for usage_to_be_processed in usages_to_be_updated_for_file . 1 {
167
- builder. replace ( usage_to_be_processed . 0 , usage_to_be_processed . 1 )
125
+ builder. edit_file ( file_id ) ;
126
+ for ( text_range , usage ) in usages {
127
+ builder. replace ( text_range , usage )
168
128
}
169
129
}
170
130
171
131
builder. edit_file ( ctx. file_id ( ) ) ;
172
- for usage_to_be_processed in usages_to_be_updated_for_curr_file {
173
- builder. replace ( usage_to_be_processed . 0 , usage_to_be_processed . 1 )
132
+ for ( text_range , usage ) in usages_to_be_processed_for_cur_file {
133
+ builder. replace ( text_range , usage ) ;
174
134
}
175
135
176
136
if let Some ( impl_) = impl_parent {
@@ -205,6 +165,37 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
205
165
)
206
166
}
207
167
168
+ fn generate_module_def (
169
+ parent_impl : & Option < ast:: Impl > ,
170
+ module : & mut Module ,
171
+ old_indent : IndentLevel ,
172
+ ) -> String {
173
+ let ( items_to_be_processed, new_item_indent) = if parent_impl. is_some ( ) {
174
+ ( Either :: Left ( module. body_items . iter ( ) ) , old_indent + 2 )
175
+ } else {
176
+ ( Either :: Right ( module. use_items . iter ( ) . chain ( module. body_items . iter ( ) ) ) , old_indent + 1 )
177
+ } ;
178
+
179
+ let mut body = items_to_be_processed
180
+ . map ( |item| item. indent ( IndentLevel ( 1 ) ) )
181
+ . map ( |item| format ! ( "{new_item_indent}{item}" ) )
182
+ . join ( "\n \n " ) ;
183
+
184
+ if let Some ( self_ty) = parent_impl. as_ref ( ) . and_then ( |imp| imp. self_ty ( ) ) {
185
+ let impl_indent = old_indent + 1 ;
186
+ body = format ! ( "{impl_indent}impl {self_ty} {{\n {body}\n {impl_indent}}}" ) ;
187
+
188
+ // Add the import for enum/struct corresponding to given impl block
189
+ module. make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
190
+ for item in module. use_items . iter ( ) {
191
+ body = format ! ( "{impl_indent}{item}\n \n {body}" ) ;
192
+ }
193
+ }
194
+
195
+ let module_name = module. name ;
196
+ format ! ( "mod {module_name} {{\n {body}\n {old_indent}}}" )
197
+ }
198
+
208
199
#[ derive( Debug ) ]
209
200
struct Module {
210
201
text_range : TextRange ,
@@ -240,6 +231,7 @@ impl Module {
240
231
//Here impl is not included as each item inside impl will be tied to the parent of
241
232
//implementing block(a struct, enum, etc), if the parent is in selected module, it will
242
233
//get updated by ADT section given below or if it is not, then we dont need to do any operation
234
+
243
235
for item in & self . body_items {
244
236
match_ast ! {
245
237
match ( item. syntax( ) ) {
@@ -320,48 +312,38 @@ impl Module {
320
312
node_def : Definition ,
321
313
refs_in_files : & mut FxHashMap < FileId , Vec < ( TextRange , String ) > > ,
322
314
) {
323
- for ( file_id, references) in node_def. usages ( & ctx. sema ) . all ( ) {
315
+ let mod_name = self . name ;
316
+ let out_of_sel = |node : & SyntaxNode | !self . text_range . contains_range ( node. text_range ( ) ) ;
317
+ for ( file_id, refs) in node_def. usages ( & ctx. sema ) . all ( ) {
324
318
let source_file = ctx. sema . parse ( file_id) ;
325
- let usages_in_file = references
326
- . into_iter ( )
327
- . filter_map ( |usage| self . get_usage_to_be_processed ( & source_file, usage) ) ;
328
- refs_in_files. entry ( file_id) . or_default ( ) . extend ( usages_in_file) ;
329
- }
330
- }
331
-
332
- fn get_usage_to_be_processed (
333
- & self ,
334
- source_file : & SourceFile ,
335
- FileReference { range, name, .. } : FileReference ,
336
- ) -> Option < ( TextRange , String ) > {
337
- let path: ast:: Path = find_node_at_range ( source_file. syntax ( ) , range) ?;
338
-
339
- for desc in path. syntax ( ) . descendants ( ) {
340
- if desc. to_string ( ) == name. syntax ( ) . to_string ( )
341
- && !self . text_range . contains_range ( desc. text_range ( ) )
342
- {
343
- if let Some ( name_ref) = ast:: NameRef :: cast ( desc) {
344
- let mod_name = self . name ;
345
- return Some ( (
346
- name_ref. syntax ( ) . text_range ( ) ,
347
- format ! ( "{mod_name}::{name_ref}" ) ,
348
- ) ) ;
319
+ let usages = refs. into_iter ( ) . filter_map ( |FileReference { range, name, .. } | {
320
+ let path: ast:: Path = find_node_at_range ( source_file. syntax ( ) , range) ?;
321
+ let name = name. syntax ( ) . to_string ( ) ;
322
+
323
+ for desc in path. syntax ( ) . descendants ( ) {
324
+ if desc. to_string ( ) == name && out_of_sel ( & desc) {
325
+ if let Some ( name_ref) = ast:: NameRef :: cast ( desc) {
326
+ let new_ref = format ! ( "{mod_name}::{name_ref}" ) ;
327
+ return Some ( ( name_ref. syntax ( ) . text_range ( ) , new_ref) ) ;
328
+ }
329
+ }
349
330
}
350
- }
351
- }
352
331
353
- None
332
+ None
333
+ } ) ;
334
+ refs_in_files. entry ( file_id) . or_default ( ) . extend ( usages) ;
335
+ }
354
336
}
355
337
356
338
fn change_visibility ( & mut self , record_fields : Vec < SyntaxNode > ) {
357
339
let ( mut replacements, record_field_parents, impls) =
358
340
get_replacements_for_visibility_change ( & mut self . body_items , false ) ;
359
341
360
- let mut impl_items: Vec < ast :: Item > = impls
342
+ let mut impl_items = impls
361
343
. into_iter ( )
362
344
. flat_map ( |impl_| impl_. syntax ( ) . descendants ( ) )
363
345
. filter_map ( ast:: Item :: cast)
364
- . collect ( ) ;
346
+ . collect_vec ( ) ;
365
347
366
348
let ( mut impl_item_replacements, _, _) =
367
349
get_replacements_for_visibility_change ( & mut impl_items, true ) ;
@@ -444,19 +426,19 @@ impl Module {
444
426
let file = ctx. sema . parse ( file_id) ;
445
427
446
428
// track uses which does not exists in `Use`
447
- let mut exists_inside_sel = false ;
448
- let mut exists_outside_sel = false ;
429
+ let mut uses_exist_in_sel = false ;
430
+ let mut uses_exist_out_sel = false ;
449
431
' outside: for ( _, refs) in usage_res. iter ( ) {
450
432
for x in refs
451
433
. iter ( )
452
434
. filter ( |x| find_node_at_range :: < ast:: Use > ( file. syntax ( ) , x. range ) . is_none ( ) )
453
435
. filter_map ( |x| find_node_at_range :: < ast:: Path > ( file. syntax ( ) , x. range ) )
454
436
{
455
437
let in_selectin = selection_range. contains_range ( x. syntax ( ) . text_range ( ) ) ;
456
- exists_inside_sel |= in_selectin;
457
- exists_outside_sel |= !in_selectin;
438
+ uses_exist_in_sel |= in_selectin;
439
+ uses_exist_out_sel |= !in_selectin;
458
440
459
- if exists_inside_sel && exists_outside_sel {
441
+ if uses_exist_in_sel && uses_exist_out_sel {
460
442
break ' outside;
461
443
}
462
444
}
@@ -475,7 +457,7 @@ impl Module {
475
457
!selection_range. contains_range ( use_stmt. syntax ( ) . text_range ( ) )
476
458
} ) ;
477
459
478
- let mut use_tree_str_opt : Option < Vec < ast:: Path > > = None ;
460
+ let mut use_tree_paths : Option < Vec < ast:: Path > > = None ;
479
461
//Exists inside and outside selection
480
462
// - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
481
463
// module
@@ -489,13 +471,13 @@ impl Module {
489
471
//get the use_tree_str, reconstruct the use stmt in new module
490
472
491
473
let mut import_path_to_be_removed: Option < TextRange > = None ;
492
- if exists_inside_sel && exists_outside_sel {
474
+ if uses_exist_in_sel && uses_exist_out_sel {
493
475
//Changes to be made only inside new module
494
476
495
477
//If use_stmt exists, find the use_tree_str, reconstruct it inside new module
496
478
//If not, insert a use stmt with super and the given nameref
497
479
match self . process_use_stmt_for_import_resolve ( use_stmt, use_node) {
498
- Some ( ( use_tree_str, _) ) => use_tree_str_opt = Some ( use_tree_str) ,
480
+ Some ( ( use_tree_str, _) ) => use_tree_paths = Some ( use_tree_str) ,
499
481
None if def_in_mod && def_out_sel => {
500
482
//Considered only after use_stmt is not present
501
483
//def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel =
@@ -509,7 +491,7 @@ impl Module {
509
491
}
510
492
None => { }
511
493
}
512
- } else if exists_inside_sel && !exists_outside_sel {
494
+ } else if uses_exist_in_sel && !uses_exist_out_sel {
513
495
//Changes to be made inside new module, and remove import from outside
514
496
515
497
if let Some ( ( mut use_tree_str, text_range_opt) ) =
@@ -531,43 +513,42 @@ impl Module {
531
513
}
532
514
}
533
515
534
- use_tree_str_opt = Some ( use_tree_str) ;
516
+ use_tree_paths = Some ( use_tree_str) ;
535
517
} else if def_in_mod && def_out_sel {
536
518
self . make_use_stmt_of_node_with_super ( use_node) ;
537
519
}
538
520
}
539
521
540
- if let Some ( use_tree_str) = use_tree_str_opt {
541
- let mut use_tree_str = use_tree_str;
542
- use_tree_str. reverse ( ) ;
522
+ if let Some ( mut use_tree_paths) = use_tree_paths {
523
+ use_tree_paths. reverse ( ) ;
543
524
544
- if exists_outside_sel || !exists_inside_sel || !def_in_mod || !def_out_sel {
545
- if let Some ( first_path_in_use_tree) = use_tree_str . first ( ) {
525
+ if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel {
526
+ if let Some ( first_path_in_use_tree) = use_tree_paths . first ( ) {
546
527
if first_path_in_use_tree. to_string ( ) . contains ( "super" ) {
547
- use_tree_str . insert ( 0 , make:: ext:: ident_path ( "super" ) ) ;
528
+ use_tree_paths . insert ( 0 , make:: ext:: ident_path ( "super" ) ) ;
548
529
}
549
530
}
550
531
}
551
532
552
- let use_ =
553
- make:: use_ ( None , make:: use_tree ( make:: join_paths ( use_tree_str) , None , None , false ) ) ;
554
- let item = ast:: Item :: from ( use_) ;
555
-
556
- let is_item = match def {
557
- Definition :: Macro ( _) => true ,
558
- Definition :: Module ( _) => true ,
559
- Definition :: Function ( _) => true ,
560
- Definition :: Adt ( _) => true ,
561
- Definition :: Const ( _) => true ,
562
- Definition :: Static ( _) => true ,
563
- Definition :: Trait ( _) => true ,
564
- Definition :: TraitAlias ( _) => true ,
565
- Definition :: TypeAlias ( _) => true ,
566
- _ => false ,
567
- } ;
533
+ let is_item = matches ! (
534
+ def,
535
+ Definition :: Macro ( _)
536
+ | Definition :: Module ( _)
537
+ | Definition :: Function ( _)
538
+ | Definition :: Adt ( _)
539
+ | Definition :: Const ( _)
540
+ | Definition :: Static ( _)
541
+ | Definition :: Trait ( _)
542
+ | Definition :: TraitAlias ( _)
543
+ | Definition :: TypeAlias ( _)
544
+ ) ;
568
545
569
546
if ( def_out_sel || !is_item) && use_stmt_not_in_sel {
570
- self . use_items . insert ( 0 , item. clone ( ) ) ;
547
+ let use_ = make:: use_ (
548
+ None ,
549
+ make:: use_tree ( make:: join_paths ( use_tree_paths) , None , None , false ) ,
550
+ ) ;
551
+ self . use_items . insert ( 0 , ast:: Item :: from ( use_) ) ;
571
552
}
572
553
}
573
554
0 commit comments