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