]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/extract_module.rs
fix: arbitary noop of assist and same file double writes
[rust.git] / crates / ide_assists / src / handlers / extract_module.rs
1 use std::collections::{HashMap, HashSet};
2
3 use hir::{HasSource, ModuleDef, ModuleSource};
4 use ide_db::{
5     assists::{AssistId, AssistKind},
6     base_db::FileId,
7     defs::{Definition, NameClass, NameRefClass},
8     search::{FileReference, SearchScope},
9 };
10 use stdx::format_to;
11 use syntax::{
12     algo::find_node_at_range,
13     ast::{
14         self,
15         edit::{AstNodeEdit, IndentLevel},
16         make, HasName, HasVisibility,
17     },
18     match_ast, ted, AstNode, SourceFile, SyntaxNode, TextRange,
19 };
20
21 use crate::{AssistContext, Assists};
22
23 use super::remove_unused_param::range_to_remove;
24
25 // Assist: extract_module
26 //
27 // Extracts a selected region as seperate module. All the references, visibility and imports are
28 // resolved.
29 //
30 // ```
31 // $0
32 // fn foo(name: i32) -> i32 {
33 //     name + 1
34 // }
35 // $0
36 //
37 // fn bar(name: i32) -> i32 {
38 //     name + 2
39 // }
40 // ```
41 // ->
42 // ```
43 // mod modname {
44 //     pub(crate) fn foo(name: i32) -> i32 {
45 //         name + 1
46 //     }
47 // }
48 //
49 // fn bar(name: i32) -> i32 {
50 //     name + 2
51 // }
52 // ```
53 pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
54     if ctx.frange.range.is_empty() {
55         return None;
56     }
57
58     let node = ctx.covering_element();
59     let node = match node {
60         syntax::NodeOrToken::Node(n) => n,
61         syntax::NodeOrToken::Token(t) => t.parent()?,
62     };
63
64     let mut curr_parent_module: Option<ast::Module> = None;
65     if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
66         curr_parent_module = ast::Module::cast(mod_syn_opt);
67     }
68
69     let mut module = extract_target(&node, ctx.frange.range)?;
70     if module.body_items.len() == 0 {
71         return None;
72     }
73
74     let old_item_indent = module.body_items[0].indent_level();
75
76     //This takes place in three steps:
77     //
78     //- Firstly, we will update the references(usages) e.g. converting a
79     //  function call bar() to modname::bar(), and similarly for other items
80     //
81     //- Secondly, changing the visibility of each item inside the newly selected module
82     //  i.e. making a fn a() {} to pub(crate) fn a() {}
83     //
84     //- Thirdly, resolving all the imports this includes removing paths from imports
85     //  outside the module, shifting/cloning them inside new module, or shifting the imports, or making
86     //  new import statemnts
87
88     //We are getting item usages and record_fields together, record_fields
89     //for change_visibility and usages for first point mentioned above in the process
90     let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
91
92     let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, &ctx);
93     module.body_items = module.change_visibility(record_fields)?;
94     if module.body_items.len() == 0 {
95         return None;
96     }
97
98     acc.add(
99         AssistId("extract_module", AssistKind::RefactorExtract),
100         "Extract Module",
101         module.text_range,
102         |builder| {
103             let mut body_items = Vec::new();
104             let new_item_indent = old_item_indent + 1;
105             for item in module.body_items {
106                 let item = item.indent(IndentLevel(1));
107                 let mut indented_item = String::new();
108                 format_to!(indented_item, "{}{}", new_item_indent, item.to_string());
109                 body_items.push(indented_item);
110             }
111
112             let body = body_items.join("\n\n");
113
114             let mut module_def = String::new();
115
116             format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
117
118             let mut usages_to_be_updated_for_curr_file = vec![];
119             for usages_to_be_updated_for_file in usages_to_be_processed {
120                 if usages_to_be_updated_for_file.0 == ctx.frange.file_id {
121                     usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
122                     continue;
123                 }
124                 builder.edit_file(usages_to_be_updated_for_file.0);
125                 for usage_to_be_processed in usages_to_be_updated_for_file.1 {
126                     builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
127                 }
128             }
129
130             builder.edit_file(ctx.frange.file_id);
131             for usage_to_be_processed in usages_to_be_updated_for_curr_file {
132                 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
133             }
134
135             for import_path_text_range in import_paths_to_be_removed {
136                 builder.delete(import_path_text_range);
137             }
138             builder.replace(module.text_range, module_def)
139         },
140     )
141 }
142
143 #[derive(Debug)]
144 struct Module {
145     text_range: TextRange,
146     name: String,
147     body_items: Vec<ast::Item>,
148 }
149
150 fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
151     let body_items: Vec<ast::Item> = node
152         .children()
153         .filter_map(|child| {
154             if let Some(item) = ast::Item::cast(child.clone()) {
155                 if selection_range.contains_range(item.syntax().text_range()) {
156                     return Some(item);
157                 }
158                 return None;
159             }
160             None
161         })
162         .collect();
163
164     Some(Module { text_range: selection_range, name: "modname".to_string(), body_items })
165 }
166
167 impl Module {
168     fn get_usages_and_record_fields(
169         &self,
170         ctx: &AssistContext,
171     ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
172         let mut record_fields = Vec::new();
173         let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
174
175         //Here impl is not included as each item inside impl will be tied to the parent of
176         //implementing block(a struct, enum, etc), if the parent is in selected module, it will
177         //get updated by ADT section given below or if it is not, then we dont need to do any operation
178         self.body_items.clone().into_iter().for_each(|item| {
179             match_ast! {
180                 match (item.syntax()) {
181                     ast::Adt(it) => {
182                         if let Some( nod ) = ctx.sema.to_def(&it) {
183                             let node_def = Definition::ModuleDef(nod.into());
184                             self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
185
186                             let mut get_record_fields = |it: ast::Adt| {
187                                 for desc in it.syntax().descendants() {
188                                     if let Some(record_field) = ast::RecordField::cast(desc) {
189                                         record_fields.push(record_field.syntax().clone());
190                                     }
191                                 }
192                             };
193
194                             //Enum Fields are not allowed to explicitly specify pub, it is implied
195                             match it {
196                                 ast::Adt::Struct(_) => get_record_fields(it),
197                                 ast::Adt::Union(_) => get_record_fields(it),
198                                 ast::Adt::Enum(_) => {},
199                             }
200                         }
201                     },
202                     ast::TypeAlias(it) => {
203                         if let Some( nod ) = ctx.sema.to_def(&it) {
204                             let node_def = Definition::ModuleDef(nod.into());
205                             self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
206                         }
207                     },
208                     ast::Const(it) => {
209                         if let Some( nod ) = ctx.sema.to_def(&it) {
210                             let node_def = Definition::ModuleDef(nod.into());
211                             self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
212                         }
213                     },
214                     ast::Static(it) => {
215                         if let Some( nod ) = ctx.sema.to_def(&it) {
216                             let node_def = Definition::ModuleDef(nod.into());
217                             self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
218                         }
219                     },
220                     ast::Fn(it) => {
221                         if let Some( nod ) = ctx.sema.to_def(&it) {
222                             let node_def = Definition::ModuleDef(nod.into());
223                             self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
224                         }
225                     },
226                     _ => (),
227                 }
228             }
229         });
230
231         return (refs, record_fields);
232     }
233
234     fn expand_and_group_usages_file_wise(
235         &self,
236         ctx: &AssistContext,
237         node_def: Definition,
238         refs: &mut HashMap<FileId, Vec<(TextRange, String)>>,
239     ) {
240         for (file_id, references) in node_def.usages(&ctx.sema).all() {
241             if let Some(file_refs) = refs.get_mut(&file_id) {
242                 let mut usages = self.expand_ref_to_usages(references, ctx, file_id);
243                 file_refs.append(&mut usages);
244             } else {
245                 refs.insert(file_id, self.expand_ref_to_usages(references, ctx, file_id));
246             }
247         }
248     }
249
250     fn expand_ref_to_usages(
251         &self,
252         refs: Vec<FileReference>,
253         ctx: &AssistContext,
254         file_id: FileId,
255     ) -> Vec<(TextRange, String)> {
256         let source_file = ctx.sema.parse(file_id);
257
258         let mut usages_to_be_processed_for_file = Vec::new();
259         for usage in refs {
260             if let Some(x) = self.get_usage_to_be_processed(&source_file, usage) {
261                 usages_to_be_processed_for_file.push(x);
262             }
263         }
264
265         usages_to_be_processed_for_file
266     }
267
268     fn get_usage_to_be_processed(
269         &self,
270         source_file: &SourceFile,
271         FileReference { range, name, .. }: FileReference,
272     ) -> Option<(TextRange, String)> {
273         let path: Option<ast::Path> = find_node_at_range(source_file.syntax(), range);
274
275         let path = path?;
276
277         for desc in path.syntax().descendants() {
278             if desc.to_string() == name.syntax().to_string()
279                 && !self.text_range.contains_range(desc.text_range())
280             {
281                 if let Some(name_ref) = ast::NameRef::cast(desc) {
282                     return Some((
283                         name_ref.syntax().text_range(),
284                         format!("{}::{}", self.name, name_ref.to_string()),
285                     ));
286                 }
287             }
288         }
289
290         None
291     }
292
293     fn change_visibility(&self, record_fields: Vec<SyntaxNode>) -> Option<Vec<ast::Item>> {
294         let (body_items, mut replacements, record_field_parents, impls) =
295             get_replacements_for_visibilty_change(self.body_items.clone(), false);
296
297         let impl_items = impls.into_iter().fold(Vec::new(), |mut impl_items, x| {
298             let this_impl_items =
299                 x.syntax().descendants().fold(Vec::new(), |mut this_impl_items, x| {
300                     if let Some(item) = ast::Item::cast(x.clone()) {
301                         this_impl_items.push(item);
302                     }
303                     return this_impl_items;
304                 });
305
306             impl_items.append(&mut this_impl_items.clone());
307             return impl_items;
308         });
309
310         let (_, mut impl_item_replacements, _, _) =
311             get_replacements_for_visibilty_change(impl_items.clone(), true);
312
313         replacements.append(&mut impl_item_replacements);
314
315         record_field_parents.into_iter().for_each(|x| {
316             x.1.descendants().filter_map(|x| ast::RecordField::cast(x)).for_each(|desc| {
317                 let is_record_field_present = record_fields
318                     .clone()
319                     .into_iter()
320                     .find(|x| x.to_string() == desc.to_string())
321                     .is_some();
322                 if is_record_field_present {
323                     replacements.push((desc.visibility().clone(), desc.syntax().clone()));
324                 }
325             });
326         });
327
328         replacements.into_iter().for_each(|(vis, syntax)| {
329             add_change_vis(vis, syntax.first_child_or_token());
330         });
331
332         Some(body_items)
333     }
334
335     fn resolve_imports(
336         &mut self,
337         curr_parent_module: Option<ast::Module>,
338         ctx: &AssistContext,
339     ) -> Vec<TextRange> {
340         let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
341         let mut node_set: HashSet<String> = HashSet::new();
342
343         self.body_items.clone().into_iter().for_each(|item| {
344             item.syntax().descendants().for_each(|x| {
345                 if let Some(name) = ast::Name::cast(x.clone()) {
346                     if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
347                         //Necessary to avoid two same names going through
348                         if !node_set.contains(&name.syntax().to_string()) {
349                             node_set.insert(name.syntax().to_string());
350                             let def_opt: Option<Definition> = match name_classify {
351                                 NameClass::Definition(def) => Some(def),
352                                 _ => None,
353                             };
354
355                             if let Some(def) = def_opt {
356                                 if let Some(import_path) = self
357                                     .process_names_and_namerefs_for_import_resolve(
358                                         def,
359                                         name.syntax(),
360                                         &curr_parent_module,
361                                         ctx,
362                                     )
363                                 {
364                                     import_paths_to_be_removed.push(import_path);
365                                 }
366                             }
367                         }
368                     }
369                 }
370
371                 if let Some(name_ref) = ast::NameRef::cast(x) {
372                     if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
373                         //Necessary to avoid two same names going through
374                         if !node_set.contains(&name_ref.syntax().to_string()) {
375                             node_set.insert(name_ref.syntax().to_string());
376                             let def_opt: Option<Definition> = match name_classify {
377                                 NameRefClass::Definition(def) => Some(def),
378                                 _ => None,
379                             };
380
381                             if let Some(def) = def_opt {
382                                 if let Some(import_path) = self
383                                     .process_names_and_namerefs_for_import_resolve(
384                                         def,
385                                         name_ref.syntax(),
386                                         &curr_parent_module,
387                                         ctx,
388                                     )
389                                 {
390                                     import_paths_to_be_removed.push(import_path);
391                                 }
392                             }
393                         }
394                     }
395                 }
396             });
397         });
398
399         import_paths_to_be_removed
400     }
401
402     fn process_names_and_namerefs_for_import_resolve(
403         &mut self,
404         def: Definition,
405         node_syntax: &SyntaxNode,
406         curr_parent_module: &Option<ast::Module>,
407         ctx: &AssistContext,
408     ) -> Option<TextRange> {
409         //We only need to find in the current file
410         let selection_range = ctx.frange.range;
411         let search_scope = SearchScope::single_file(ctx.frange.file_id);
412         let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
413         let curr_file_id = ctx.frange.file_id;
414         let file = ctx.sema.parse(ctx.frange.file_id);
415
416         let mut exists_inside_sel = false;
417         let mut exists_outside_sel = false;
418         usage_res.clone().into_iter().for_each(|x| {
419             let mut non_use_nodes_itr = (&x.1).into_iter().filter_map(|x| {
420                 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
421                     let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
422                     return path_opt;
423                 }
424
425                 None
426             });
427
428             if non_use_nodes_itr
429                 .clone()
430                 .find(|x| !selection_range.contains_range(x.syntax().text_range()))
431                 .is_some()
432             {
433                 exists_outside_sel = true;
434             }
435             if non_use_nodes_itr
436                 .find(|x| selection_range.contains_range(x.syntax().text_range()))
437                 .is_some()
438             {
439                 exists_inside_sel = true;
440             }
441         });
442
443         let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
444             def,
445             ctx,
446             curr_parent_module,
447             selection_range,
448             curr_file_id,
449         );
450
451         let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
452             let file_id = x.0;
453             let mut use_opt: Option<ast::Use> = None;
454             if file_id == ctx.frange.file_id {
455                 (&x.1).into_iter().for_each(|x| {
456                     let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
457                     if let Some(node) = node_opt {
458                         use_opt = Some(node.clone());
459                     }
460                 });
461             }
462             return use_opt;
463         });
464
465         let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
466         //Exists inside and outside selection
467         // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
468         // module
469         // - Use stmt for item is not present ->
470         //If it is not found, the definition is either ported inside new module or it stays
471         //outside:
472         //- Def is inside: Nothing to import
473         //- Def is outside: Import it inside with super
474
475         //Exists inside selection but not outside -> Check for the import of it in original module,
476         //get the use_tree_str, reconstruct the use stmt in new module
477
478         let mut import_path_to_be_removed: Option<TextRange> = None;
479         if exists_inside_sel && exists_outside_sel {
480             //Changes to be made only inside new module
481
482             //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
483             //If not, insert a use stmt with super and the given nameref
484             if let Some((use_tree_str, _)) =
485                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
486             {
487                 use_tree_str_opt = Some(use_tree_str);
488             } else if source_exists_outside_sel_in_same_mod {
489                 //Considered only after use_stmt is not present
490                 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
491                 //true for all cases)
492                 // false | false -> Do nothing
493                 // false | true -> If source is in selection -> nothing to do, If source is outside
494                 // mod -> ust_stmt transversal
495                 // true  | false -> super import insertion
496                 // true  | true -> super import insertion
497                 self.make_use_stmt_of_node_with_super(node_syntax);
498             }
499         } else if exists_inside_sel && !exists_outside_sel {
500             //Changes to be made inside new module, and remove import from outside
501
502             if let Some((use_tree_str, text_range_opt)) =
503                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
504             {
505                 if let Some(text_range) = text_range_opt {
506                     import_path_to_be_removed = Some(text_range);
507                 }
508                 use_tree_str_opt = Some(use_tree_str);
509             } else if source_exists_outside_sel_in_same_mod {
510                 self.make_use_stmt_of_node_with_super(node_syntax);
511             }
512         }
513
514         if let Some(use_tree_str) = use_tree_str_opt {
515             let mut use_tree_str = use_tree_str.clone();
516             use_tree_str.reverse();
517             if use_tree_str[0].to_string().contains("super") {
518                 let super_path = make::ext::ident_path("super");
519                 use_tree_str.insert(0, super_path)
520             }
521
522             let use_ =
523                 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
524             if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
525                 self.body_items.insert(0, item);
526             }
527         }
528
529         import_path_to_be_removed
530     }
531
532     fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) {
533         let super_path = make::ext::ident_path("super");
534         let node_path = make::ext::ident_path(&node_syntax.to_string());
535         let use_ = make::use_(
536             None,
537             make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
538         );
539         if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
540             self.body_items.insert(0, item);
541         }
542     }
543
544     fn process_use_stmt_for_import_resolve(
545         &self,
546         use_stmt_opt: Option<ast::Use>,
547         node_syntax: &SyntaxNode,
548     ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
549         if let Some(use_stmt) = use_stmt_opt {
550             for desc in use_stmt.syntax().descendants() {
551                 if let Some(path_seg) = ast::PathSegment::cast(desc) {
552                     if path_seg.syntax().to_string() == node_syntax.to_string() {
553                         let mut use_tree_str = vec![path_seg.parent_path()];
554                         get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
555                         for ancs in path_seg.syntax().ancestors() {
556                             //Here we are looking for use_tree with same string value as node
557                             //passed above as the range_to_remove function looks for a comma and
558                             //then includes it in the text range to remove it. But the comma only
559                             //appears at the use_tree level
560                             if let Some(use_tree) = ast::UseTree::cast(ancs) {
561                                 if use_tree.syntax().to_string() == node_syntax.to_string() {
562                                     return Some((
563                                         use_tree_str,
564                                         Some(range_to_remove(use_tree.syntax())),
565                                     ));
566                                 }
567                             }
568                         }
569
570                         return Some((use_tree_str, None));
571                     }
572                 }
573             }
574         }
575
576         None
577     }
578 }
579
580 fn does_source_exists_outside_sel_in_same_mod(
581     def: Definition,
582     ctx: &AssistContext,
583     curr_parent_module: &Option<ast::Module>,
584     selection_range: TextRange,
585     curr_file_id: FileId,
586 ) -> bool {
587     let mut source_exists_outside_sel_in_same_mod = false;
588     match def {
589         Definition::ModuleDef(it) => match it {
590             ModuleDef::Module(x) => {
591                 let source = x.definition_source(ctx.db());
592                 let have_same_parent;
593                 if let Some(ast_module) = &curr_parent_module {
594                     if let Some(hir_module) = x.parent(ctx.db()) {
595                         have_same_parent =
596                             compare_hir_and_ast_module(&ast_module, hir_module, ctx).is_some();
597                     } else {
598                         let source_file_id = source.file_id.original_file(ctx.db());
599                         have_same_parent = source_file_id == curr_file_id;
600                     }
601                 } else {
602                     let source_file_id = source.file_id.original_file(ctx.db());
603                     have_same_parent = source_file_id == curr_file_id;
604                 }
605
606                 if have_same_parent {
607                     match source.value {
608                         ModuleSource::Module(module_) => {
609                             source_exists_outside_sel_in_same_mod =
610                                 !selection_range.contains_range(module_.syntax().text_range());
611                         }
612                         _ => {}
613                     }
614                 }
615             }
616             ModuleDef::Function(x) => {
617                 if let Some(source) = x.source(ctx.db()) {
618                     let have_same_parent;
619                     if let Some(ast_module) = &curr_parent_module {
620                         have_same_parent =
621                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
622                                 .is_some();
623                     } else {
624                         let source_file_id = source.file_id.original_file(ctx.db());
625                         have_same_parent = source_file_id == curr_file_id;
626                     }
627
628                     if have_same_parent {
629                         source_exists_outside_sel_in_same_mod =
630                             !selection_range.contains_range(source.value.syntax().text_range());
631                     }
632                 }
633             }
634             ModuleDef::Adt(x) => {
635                 if let Some(source) = x.source(ctx.db()) {
636                     let have_same_parent;
637                     if let Some(ast_module) = &curr_parent_module {
638                         have_same_parent =
639                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
640                                 .is_some();
641                     } else {
642                         let source_file_id = source.file_id.original_file(ctx.db());
643                         have_same_parent = source_file_id == curr_file_id;
644                     }
645
646                     if have_same_parent {
647                         source_exists_outside_sel_in_same_mod =
648                             !selection_range.contains_range(source.value.syntax().text_range());
649                     }
650                 }
651             }
652             ModuleDef::Variant(x) => {
653                 if let Some(source) = x.source(ctx.db()) {
654                     let have_same_parent;
655                     if let Some(ast_module) = &curr_parent_module {
656                         have_same_parent =
657                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
658                                 .is_some();
659                     } else {
660                         let source_file_id = source.file_id.original_file(ctx.db());
661                         have_same_parent = source_file_id == curr_file_id;
662                     }
663
664                     if have_same_parent {
665                         source_exists_outside_sel_in_same_mod =
666                             !selection_range.contains_range(source.value.syntax().text_range());
667                     }
668                 }
669             }
670             ModuleDef::Const(x) => {
671                 if let Some(source) = x.source(ctx.db()) {
672                     let have_same_parent;
673                     if let Some(ast_module) = &curr_parent_module {
674                         have_same_parent =
675                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
676                                 .is_some();
677                     } else {
678                         let source_file_id = source.file_id.original_file(ctx.db());
679                         have_same_parent = source_file_id == curr_file_id;
680                     }
681
682                     if have_same_parent {
683                         source_exists_outside_sel_in_same_mod =
684                             !selection_range.contains_range(source.value.syntax().text_range());
685                     }
686                 }
687             }
688             ModuleDef::Static(x) => {
689                 if let Some(source) = x.source(ctx.db()) {
690                     let have_same_parent;
691                     if let Some(ast_module) = &curr_parent_module {
692                         have_same_parent =
693                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
694                                 .is_some();
695                     } else {
696                         let source_file_id = source.file_id.original_file(ctx.db());
697                         have_same_parent = source_file_id == curr_file_id;
698                     }
699
700                     if have_same_parent {
701                         source_exists_outside_sel_in_same_mod =
702                             !selection_range.contains_range(source.value.syntax().text_range());
703                     }
704                 }
705             }
706             ModuleDef::Trait(x) => {
707                 if let Some(source) = x.source(ctx.db()) {
708                     let have_same_parent;
709                     if let Some(ast_module) = &curr_parent_module {
710                         have_same_parent =
711                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
712                                 .is_some();
713                     } else {
714                         let source_file_id = source.file_id.original_file(ctx.db());
715                         have_same_parent = source_file_id == curr_file_id;
716                     }
717
718                     if have_same_parent {
719                         source_exists_outside_sel_in_same_mod =
720                             !selection_range.contains_range(source.value.syntax().text_range());
721                     }
722                 }
723             }
724             ModuleDef::TypeAlias(x) => {
725                 if let Some(source) = x.source(ctx.db()) {
726                     let have_same_parent;
727                     if let Some(ast_module) = &curr_parent_module {
728                         have_same_parent =
729                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
730                                 .is_some();
731                     } else {
732                         let source_file_id = source.file_id.original_file(ctx.db());
733                         have_same_parent = source_file_id == curr_file_id;
734                     }
735
736                     if have_same_parent {
737                         source_exists_outside_sel_in_same_mod =
738                             !selection_range.contains_range(source.value.syntax().text_range());
739                     }
740                 }
741             }
742             _ => {}
743         },
744         _ => {}
745     }
746
747     return source_exists_outside_sel_in_same_mod;
748 }
749
750 fn get_replacements_for_visibilty_change(
751     items: Vec<ast::Item>,
752     is_clone_for_updated: bool,
753 ) -> (
754     Vec<ast::Item>,
755     Vec<(Option<ast::Visibility>, SyntaxNode)>,
756     Vec<(Option<ast::Visibility>, SyntaxNode)>,
757     Vec<ast::Impl>,
758 ) {
759     let mut replacements = Vec::new();
760     let mut record_field_parents = Vec::new();
761     let mut impls = Vec::new();
762     let mut body_items = Vec::new();
763
764     items.into_iter().for_each(|item| {
765         let mut item = item;
766         if !is_clone_for_updated {
767             item = item.clone_for_update();
768         }
769         body_items.push(item.clone());
770         //Use stmts are ignored
771         match item {
772             ast::Item::Const(it) => {
773                 replacements.push((it.visibility().clone(), it.syntax().clone()))
774             }
775             ast::Item::Enum(it) => {
776                 replacements.push((it.visibility().clone(), it.syntax().clone()))
777             }
778             ast::Item::ExternCrate(it) => {
779                 replacements.push((it.visibility().clone(), it.syntax().clone()))
780             }
781             ast::Item::Fn(it) => replacements.push((it.visibility().clone(), it.syntax().clone())),
782             ast::Item::Impl(it) => impls.push(it),
783             ast::Item::MacroRules(it) => {
784                 replacements.push((it.visibility().clone(), it.syntax().clone()))
785             }
786             ast::Item::MacroDef(it) => {
787                 replacements.push((it.visibility().clone(), it.syntax().clone()))
788             }
789             ast::Item::Module(it) => {
790                 replacements.push((it.visibility().clone(), it.syntax().clone()))
791             }
792             ast::Item::Static(it) => {
793                 replacements.push((it.visibility().clone(), it.syntax().clone()))
794             }
795             ast::Item::Struct(it) => {
796                 replacements.push((it.visibility().clone(), it.syntax().clone()));
797                 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
798             }
799             ast::Item::Trait(it) => {
800                 replacements.push((it.visibility().clone(), it.syntax().clone()))
801             }
802             ast::Item::TypeAlias(it) => {
803                 replacements.push((it.visibility().clone(), it.syntax().clone()))
804             }
805             ast::Item::Union(it) => {
806                 replacements.push((it.visibility().clone(), it.syntax().clone()));
807                 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
808             }
809             _ => (),
810         }
811     });
812
813     return (body_items, replacements, record_field_parents, impls);
814 }
815
816 fn get_use_tree_paths_from_path(
817     path: ast::Path,
818     use_tree_str: &mut Vec<ast::Path>,
819 ) -> Option<&mut Vec<ast::Path>> {
820     path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
821         if let Some(use_tree) = ast::UseTree::cast(x.clone()) {
822             if let Some(upper_tree_path) = use_tree.path() {
823                 if upper_tree_path.to_string() != path.to_string() {
824                     use_tree_str.push(upper_tree_path.clone());
825                     get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
826                     return Some(use_tree);
827                 }
828             }
829         }
830         None
831     })?;
832
833     Some(use_tree_str)
834 }
835
836 fn add_change_vis(
837     vis: Option<ast::Visibility>,
838     node_or_token_opt: Option<syntax::SyntaxElement>,
839 ) -> Option<()> {
840     if let Some(vis) = vis {
841         if vis.syntax().text() == "pub" {
842             ted::replace(vis.syntax(), make::visibility_pub_crate().syntax().clone_for_update());
843         }
844     } else {
845         if let Some(node_or_token) = node_or_token_opt {
846             let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
847             if let Some(node) = node_or_token.as_node() {
848                 ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
849             }
850             if let Some(token) = node_or_token.as_token() {
851                 ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
852             }
853         }
854     }
855
856     Some(())
857 }
858
859 fn compare_hir_and_ast_module(
860     ast_module: &ast::Module,
861     hir_module: hir::Module,
862     ctx: &AssistContext,
863 ) -> Option<()> {
864     let hir_mod_name = hir_module.name(ctx.db())?;
865     let ast_mod_name = ast_module.name()?;
866     if hir_mod_name.to_string() != ast_mod_name.to_string() {
867         return None;
868     }
869
870     return Some(());
871 }
872
873 #[cfg(test)]
874 mod tests {
875     use crate::tests::{check_assist, check_assist_not_applicable};
876
877     use super::*;
878
879     #[test]
880     fn test_not_applicable_without_selection() {
881         check_assist_not_applicable(
882             extract_module,
883             r"
884                 $0pub struct PublicStruct {
885                     field: i32,
886                 }
887             ",
888         )
889     }
890
891     #[test]
892     fn test_extract_module() {
893         check_assist(
894             extract_module,
895             r"
896             mod thirdpartycrate {
897                 pub mod nest {
898                     pub struct SomeType;
899                     pub struct SomeType2;
900                 }
901                 pub struct SomeType1;
902             }
903
904             mod bar {
905                 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
906
907                 pub struct PublicStruct {
908                     field: PrivateStruct,
909                     field1: SomeType1,
910                 }
911
912                 impl PublicStruct {
913                     pub fn new() -> Self {
914                         Self { field: PrivateStruct::new(), field1: SomeType1 }
915                     }
916                 }
917
918                 fn foo() {
919                     let _s = PrivateStruct::new();
920                     let _a = bar();
921                 }
922
923                 $0
924                 struct PrivateStruct {
925                     inner: SomeType,
926                 }
927
928                 pub struct PrivateStruct1 {
929                     pub inner: i32,
930                 }
931
932                 impl PrivateStruct {
933                     fn new() -> Self {
934                          PrivateStruct { inner: SomeType }
935                     }
936                 }
937
938                 fn bar() -> i32 {
939                     2
940                 }
941                 $0
942             }
943             ",
944             r"
945             mod thirdpartycrate {
946                 pub mod nest {
947                     pub struct SomeType;
948                     pub struct SomeType2;
949                 }
950                 pub struct SomeType1;
951             }
952
953             mod bar {
954                 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
955
956                 pub struct PublicStruct {
957                     field: modname::PrivateStruct,
958                     field1: SomeType1,
959                 }
960
961                 impl PublicStruct {
962                     pub fn new() -> Self {
963                         Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
964                     }
965                 }
966
967                 fn foo() {
968                     let _s = modname::PrivateStruct::new();
969                     let _a = modname::bar();
970                 }
971
972                 mod modname {
973                     use crate::thirdpartycrate::nest::SomeType;
974
975                     pub(crate) struct PrivateStruct {
976                         pub(crate) inner: SomeType,
977                     }
978
979                     pub(crate) struct PrivateStruct1 {
980                         pub(crate) inner: i32,
981                     }
982
983                     impl PrivateStruct {
984                         pub(crate) fn new() -> Self {
985                              PrivateStruct { inner: SomeType }
986                         }
987                     }
988
989                     pub(crate) fn bar() -> i32 {
990                         2
991                     }
992                 }
993             }
994             ",
995         );
996     }
997
998     #[test]
999     fn test_extract_module_for_function_only() {
1000         check_assist(
1001             extract_module,
1002             r"
1003                 $0
1004                 fn foo(name: i32) -> i32 {
1005                     name + 1
1006                 }
1007                 $0
1008
1009                 fn bar(name: i32) -> i32 {
1010                     name + 2
1011                 }
1012             ",
1013             r"
1014                 mod modname {
1015                     pub(crate) fn foo(name: i32) -> i32 {
1016                         name + 1
1017                     }
1018                 }
1019
1020                 fn bar(name: i32) -> i32 {
1021                     name + 2
1022                 }
1023             ",
1024         )
1025     }
1026
1027     #[test]
1028     fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1029         check_assist(
1030             extract_module,
1031             r"
1032             mod impl_play {
1033                 $0
1034                 struct A {}
1035
1036                 impl A {
1037                     pub fn new_a() -> i32 {
1038                         2
1039                     }
1040                 }
1041                 $0
1042
1043                 fn a() {
1044                     let _a = A::new_a();
1045                 }
1046             }
1047             ",
1048             r"
1049             mod impl_play {
1050                 mod modname {
1051                     pub(crate) struct A {}
1052
1053                     impl A {
1054                         pub(crate) fn new_a() -> i32 {
1055                             2
1056                         }
1057                     }
1058                 }
1059
1060                 fn a() {
1061                     let _a = modname::A::new_a();
1062                 }
1063             }
1064             ",
1065         )
1066     }
1067
1068     #[test]
1069     fn test_import_resolve_when_its_only_inside_selection() {
1070         check_assist(
1071             extract_module,
1072             r"
1073             mod foo {
1074                 pub struct PrivateStruct;
1075                 pub struct PrivateStruct1;
1076             }
1077
1078             mod bar {
1079                 use super::foo::{PrivateStruct, PrivateStruct1};
1080
1081                 $0
1082                 struct Strukt {
1083                     field: PrivateStruct,
1084                 }
1085                 $0
1086
1087                 struct Strukt1 {
1088                     field: PrivateStruct1,
1089                 }
1090             }
1091             ",
1092             r"
1093             mod foo {
1094                 pub struct PrivateStruct;
1095                 pub struct PrivateStruct1;
1096             }
1097
1098             mod bar {
1099                 use super::foo::{PrivateStruct1};
1100
1101                 mod modname {
1102                     use super::super::foo::PrivateStruct;
1103
1104                     pub(crate) struct Strukt {
1105                         pub(crate) field: PrivateStruct,
1106                     }
1107                 }
1108
1109                 struct Strukt1 {
1110                     field: PrivateStruct1,
1111                 }
1112             }
1113             ",
1114         )
1115     }
1116
1117     #[test]
1118     fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1119         check_assist(
1120             extract_module,
1121             r"
1122             mod foo {
1123                 pub struct PrivateStruct;
1124             }
1125
1126             mod bar {
1127                 use super::foo::PrivateStruct;
1128
1129                 $0
1130                 struct Strukt {
1131                     field: PrivateStruct,
1132                 }
1133                 $0
1134
1135                 struct Strukt1 {
1136                     field: PrivateStruct,
1137                 }
1138             }
1139             ",
1140             r"
1141             mod foo {
1142                 pub struct PrivateStruct;
1143             }
1144
1145             mod bar {
1146                 use super::foo::PrivateStruct;
1147
1148                 mod modname {
1149                     use super::super::foo::PrivateStruct;
1150
1151                     pub(crate) struct Strukt {
1152                         pub(crate) field: PrivateStruct,
1153                     }
1154                 }
1155
1156                 struct Strukt1 {
1157                     field: PrivateStruct,
1158                 }
1159             }
1160             ",
1161         )
1162     }
1163
1164     #[test]
1165     fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1166         check_assist(
1167             extract_module,
1168             r"
1169             mod bar {
1170                 pub struct PrivateStruct;
1171
1172                 $0
1173                 struct Strukt {
1174                     field: PrivateStruct,
1175                 }
1176                 $0
1177
1178                 struct Strukt1 {
1179                     field: PrivateStruct,
1180                 }
1181             }
1182             ",
1183             r"
1184             mod bar {
1185                 pub struct PrivateStruct;
1186
1187                 mod modname {
1188                     use super::PrivateStruct;
1189
1190                     pub(crate) struct Strukt {
1191                         pub(crate) field: PrivateStruct,
1192                     }
1193                 }
1194
1195                 struct Strukt1 {
1196                     field: PrivateStruct,
1197                 }
1198             }
1199             ",
1200         )
1201     }
1202
1203     #[test]
1204     fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
1205         check_assist(
1206             extract_module,
1207             r"
1208             mod impl_play {
1209                 struct A {}
1210
1211                 $0
1212                 impl A {
1213                     pub fn new_a() -> i32 {
1214                         2
1215                     }
1216                 }
1217                 $0
1218
1219                 fn a() {
1220                     let _a = A::new_a();
1221                 }
1222             }
1223             ",
1224             r"
1225             mod impl_play {
1226                 struct A {}
1227
1228                 mod modname {
1229                     use super::A;
1230
1231                     impl A {
1232                         pub(crate) fn new_a() -> i32 {
1233                             2
1234                         }
1235                     }
1236                 }
1237
1238                 fn a() {
1239                     let _a = A::new_a();
1240                 }
1241             }
1242             ",
1243         )
1244     }
1245
1246     #[test]
1247     fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1248     ) {
1249         check_assist(
1250             extract_module,
1251             r"
1252             mod foo {
1253                 pub struct A {}
1254             }
1255             mod impl_play {
1256                 use super::foo::A;
1257
1258                 $0
1259                 impl A {
1260                     pub fn new_a() -> i32 {
1261                         2
1262                     }
1263                 }
1264                 $0
1265
1266                 fn a() {
1267                     let _a = A::new_a();
1268                 }
1269             }
1270             ",
1271             r"
1272             mod foo {
1273                 pub struct A {}
1274             }
1275             mod impl_play {
1276                 use super::foo::A;
1277
1278                 mod modname {
1279                     use super::super::foo::A;
1280
1281                     impl A {
1282                         pub(crate) fn new_a() -> i32 {
1283                             2
1284                         }
1285                     }
1286                 }
1287
1288                 fn a() {
1289                     let _a = A::new_a();
1290                 }
1291             }
1292             ",
1293         )
1294     }
1295
1296     #[test]
1297     fn test_import_resolve_for_trait_bounds_on_function() {
1298         check_assist(
1299             extract_module,
1300             r"
1301             mod impl_play2 {
1302                 trait JustATrait {}
1303
1304                 $0
1305                 struct A {}
1306
1307                 fn foo<T: JustATrait>(arg: T) -> T {
1308                     arg
1309                 }
1310
1311                 impl JustATrait for A {}
1312
1313                 fn bar() {
1314                     let a = A {};
1315                     foo(a);
1316                 }
1317                 $0
1318             }
1319             ",
1320             r"
1321             mod impl_play2 {
1322                 trait JustATrait {}
1323
1324                 mod modname {
1325                     use super::JustATrait;
1326
1327                     pub(crate) struct A {}
1328
1329                     pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1330                         arg
1331                     }
1332
1333                     impl JustATrait for A {}
1334
1335                     pub(crate) fn bar() {
1336                         let a = A {};
1337                         foo(a);
1338                     }
1339                 }
1340             }
1341             ",
1342         )
1343     }
1344
1345     #[test]
1346     fn test_extract_module_for_module() {
1347         check_assist(
1348             extract_module,
1349             r"
1350             mod impl_play2 {
1351                 $0
1352                 mod impl_play {
1353                     pub struct A {}
1354                 }
1355                 $0
1356             }
1357             ",
1358             r"
1359             mod impl_play2 {
1360                 mod modname {
1361                     pub(crate) mod impl_play {
1362                         pub struct A {}
1363                     }
1364                 }
1365             }
1366             ",
1367         )
1368     }
1369
1370     #[test]
1371     fn test_extract_module_with_multiple_files() {
1372         check_assist(
1373             extract_module,
1374             r"
1375             //- /main.rs
1376             mod foo;
1377
1378             use foo::PrivateStruct;
1379
1380             pub struct Strukt {
1381                 field: PrivateStruct,
1382             }
1383
1384             fn main() {
1385                 $0
1386                 struct Strukt1 {
1387                     field: Strukt,
1388                 }
1389                 $0
1390             }
1391             //- /foo.rs
1392             pub struct PrivateStruct;
1393             ",
1394             r"
1395             mod foo;
1396
1397             use foo::PrivateStruct;
1398
1399             pub struct Strukt {
1400                 field: PrivateStruct,
1401             }
1402
1403             fn main() {
1404                 mod modname {
1405                     use super::Strukt;
1406
1407                     pub(crate) struct Strukt1 {
1408                         pub(crate) field: Strukt,
1409                     }
1410                 }
1411             }
1412             ",
1413         )
1414     }
1415 }