11use std:: iter;
22
3+ use either:: Either ;
34use hir:: { HasSource , HirFileIdExt , ModuleSource } ;
45use ide_db:: {
56 assists:: { AssistId , AssistKind } ,
@@ -10,15 +11,14 @@ use ide_db::{
1011} ;
1112use itertools:: Itertools ;
1213use smallvec:: SmallVec ;
13- use stdx:: format_to;
1414use syntax:: {
1515 algo:: find_node_at_range,
1616 ast:: {
1717 self ,
1818 edit:: { AstNodeEdit , IndentLevel } ,
1919 make, HasVisibility ,
2020 } ,
21- match_ast, ted, AstNode , SourceFile ,
21+ match_ast, ted, AstNode ,
2222 SyntaxKind :: { self , WHITESPACE } ,
2323 SyntaxNode , TextRange ,
2424} ;
@@ -114,63 +114,23 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
114114 let import_paths_to_be_removed = module. resolve_imports ( curr_parent_module, ctx) ;
115115 module. change_visibility ( record_fields) ;
116116
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) ;
119118
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;
163123 continue ;
164124 }
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 )
168128 }
169129 }
170130
171131 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 ) ;
174134 }
175135
176136 if let Some ( impl_) = impl_parent {
@@ -205,6 +165,37 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
205165 )
206166}
207167
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+
208199#[ derive( Debug ) ]
209200struct Module {
210201 text_range : TextRange ,
@@ -240,6 +231,7 @@ impl Module {
240231 //Here impl is not included as each item inside impl will be tied to the parent of
241232 //implementing block(a struct, enum, etc), if the parent is in selected module, it will
242233 //get updated by ADT section given below or if it is not, then we dont need to do any operation
234+
243235 for item in & self . body_items {
244236 match_ast ! {
245237 match ( item. syntax( ) ) {
@@ -320,48 +312,38 @@ impl Module {
320312 node_def : Definition ,
321313 refs_in_files : & mut FxHashMap < FileId , Vec < ( TextRange , String ) > > ,
322314 ) {
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 ( ) {
324318 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+ }
349330 }
350- }
351- }
352331
353- None
332+ None
333+ } ) ;
334+ refs_in_files. entry ( file_id) . or_default ( ) . extend ( usages) ;
335+ }
354336 }
355337
356338 fn change_visibility ( & mut self , record_fields : Vec < SyntaxNode > ) {
357339 let ( mut replacements, record_field_parents, impls) =
358340 get_replacements_for_visibility_change ( & mut self . body_items , false ) ;
359341
360- let mut impl_items: Vec < ast :: Item > = impls
342+ let mut impl_items = impls
361343 . into_iter ( )
362344 . flat_map ( |impl_| impl_. syntax ( ) . descendants ( ) )
363345 . filter_map ( ast:: Item :: cast)
364- . collect ( ) ;
346+ . collect_vec ( ) ;
365347
366348 let ( mut impl_item_replacements, _, _) =
367349 get_replacements_for_visibility_change ( & mut impl_items, true ) ;
@@ -444,19 +426,19 @@ impl Module {
444426 let file = ctx. sema . parse ( file_id) ;
445427
446428 // 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 ;
449431 ' outside: for ( _, refs) in usage_res. iter ( ) {
450432 for x in refs
451433 . iter ( )
452434 . filter ( |x| find_node_at_range :: < ast:: Use > ( file. syntax ( ) , x. range ) . is_none ( ) )
453435 . filter_map ( |x| find_node_at_range :: < ast:: Path > ( file. syntax ( ) , x. range ) )
454436 {
455437 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;
458440
459- if exists_inside_sel && exists_outside_sel {
441+ if uses_exist_in_sel && uses_exist_out_sel {
460442 break ' outside;
461443 }
462444 }
@@ -475,7 +457,7 @@ impl Module {
475457 !selection_range. contains_range ( use_stmt. syntax ( ) . text_range ( ) )
476458 } ) ;
477459
478- let mut use_tree_str_opt : Option < Vec < ast:: Path > > = None ;
460+ let mut use_tree_paths : Option < Vec < ast:: Path > > = None ;
479461 //Exists inside and outside selection
480462 // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
481463 // module
@@ -489,13 +471,13 @@ impl Module {
489471 //get the use_tree_str, reconstruct the use stmt in new module
490472
491473 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 {
493475 //Changes to be made only inside new module
494476
495477 //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
496478 //If not, insert a use stmt with super and the given nameref
497479 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) ,
499481 None if def_in_mod && def_out_sel => {
500482 //Considered only after use_stmt is not present
501483 //def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel =
@@ -509,7 +491,7 @@ impl Module {
509491 }
510492 None => { }
511493 }
512- } else if exists_inside_sel && !exists_outside_sel {
494+ } else if uses_exist_in_sel && !uses_exist_out_sel {
513495 //Changes to be made inside new module, and remove import from outside
514496
515497 if let Some ( ( mut use_tree_str, text_range_opt) ) =
@@ -531,43 +513,42 @@ impl Module {
531513 }
532514 }
533515
534- use_tree_str_opt = Some ( use_tree_str) ;
516+ use_tree_paths = Some ( use_tree_str) ;
535517 } else if def_in_mod && def_out_sel {
536518 self . make_use_stmt_of_node_with_super ( use_node) ;
537519 }
538520 }
539521
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 ( ) ;
543524
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 ( ) {
546527 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" ) ) ;
548529 }
549530 }
550531 }
551532
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+ ) ;
568545
569546 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_) ) ;
571552 }
572553 }
573554
0 commit comments