]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/extract_module.rs
fix: making tests compatible with new trimmed sel_range
[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 // $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.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     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::ModuleDef(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::ModuleDef(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::ModuleDef(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::ModuleDef(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::ModuleDef(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.to_string()),
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 impl_items = impls.into_iter().fold(Vec::new(), |mut impl_items, x| {
315             let this_impl_items =
316                 x.syntax().descendants().fold(Vec::new(), |mut this_impl_items, x| {
317                     if let Some(item) = ast::Item::cast(x.clone()) {
318                         this_impl_items.push(item);
319                     }
320                     return this_impl_items;
321                 });
322
323             impl_items.append(&mut this_impl_items.clone());
324             return impl_items;
325         });
326
327         let (_, mut impl_item_replacements, _, _) =
328             get_replacements_for_visibilty_change(impl_items.clone(), true);
329
330         replacements.append(&mut impl_item_replacements);
331
332         record_field_parents.into_iter().for_each(|x| {
333             x.1.descendants().filter_map(|x| ast::RecordField::cast(x)).for_each(|desc| {
334                 let is_record_field_present = record_fields
335                     .clone()
336                     .into_iter()
337                     .find(|x| x.to_string() == desc.to_string())
338                     .is_some();
339                 if is_record_field_present {
340                     replacements.push((desc.visibility().clone(), desc.syntax().clone()));
341                 }
342             });
343         });
344
345         replacements.into_iter().for_each(|(vis, syntax)| {
346             add_change_vis(vis, syntax.first_child_or_token());
347         });
348
349         Some(body_items)
350     }
351
352     fn resolve_imports(
353         &mut self,
354         curr_parent_module: Option<ast::Module>,
355         ctx: &AssistContext,
356     ) -> Vec<TextRange> {
357         let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
358         let mut node_set: HashSet<String> = HashSet::new();
359
360         self.body_items.clone().into_iter().for_each(|item| {
361             item.syntax().descendants().for_each(|x| {
362                 if let Some(name) = ast::Name::cast(x.clone()) {
363                     if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
364                         //Necessary to avoid two same names going through
365                         if !node_set.contains(&name.syntax().to_string()) {
366                             node_set.insert(name.syntax().to_string());
367                             let def_opt: Option<Definition> = match name_classify {
368                                 NameClass::Definition(def) => Some(def),
369                                 _ => None,
370                             };
371
372                             if let Some(def) = def_opt {
373                                 if let Some(import_path) = self
374                                     .process_names_and_namerefs_for_import_resolve(
375                                         def,
376                                         name.syntax(),
377                                         &curr_parent_module,
378                                         ctx,
379                                     )
380                                 {
381                                     import_paths_to_be_removed.push(import_path);
382                                 }
383                             }
384                         }
385                     }
386                 }
387
388                 if let Some(name_ref) = ast::NameRef::cast(x) {
389                     if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
390                         //Necessary to avoid two same names going through
391                         if !node_set.contains(&name_ref.syntax().to_string()) {
392                             node_set.insert(name_ref.syntax().to_string());
393                             let def_opt: Option<Definition> = match name_classify {
394                                 NameRefClass::Definition(def) => Some(def),
395                                 _ => None,
396                             };
397
398                             if let Some(def) = def_opt {
399                                 if let Some(import_path) = self
400                                     .process_names_and_namerefs_for_import_resolve(
401                                         def,
402                                         name_ref.syntax(),
403                                         &curr_parent_module,
404                                         ctx,
405                                     )
406                                 {
407                                     import_paths_to_be_removed.push(import_path);
408                                 }
409                             }
410                         }
411                     }
412                 }
413             });
414         });
415
416         import_paths_to_be_removed
417     }
418
419     fn process_names_and_namerefs_for_import_resolve(
420         &mut self,
421         def: Definition,
422         node_syntax: &SyntaxNode,
423         curr_parent_module: &Option<ast::Module>,
424         ctx: &AssistContext,
425     ) -> Option<TextRange> {
426         //We only need to find in the current file
427         let selection_range = ctx.selection_trimmed();
428         let curr_file_id = ctx.file_id();
429         let search_scope = SearchScope::single_file(curr_file_id);
430         let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
431         let file = ctx.sema.parse(curr_file_id);
432
433         let mut exists_inside_sel = false;
434         let mut exists_outside_sel = false;
435         usage_res.clone().into_iter().for_each(|x| {
436             let mut non_use_nodes_itr = (&x.1).into_iter().filter_map(|x| {
437                 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
438                     let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
439                     return path_opt;
440                 }
441
442                 None
443             });
444
445             if non_use_nodes_itr
446                 .clone()
447                 .find(|x| !selection_range.contains_range(x.syntax().text_range()))
448                 .is_some()
449             {
450                 exists_outside_sel = true;
451             }
452             if non_use_nodes_itr
453                 .find(|x| selection_range.contains_range(x.syntax().text_range()))
454                 .is_some()
455             {
456                 exists_inside_sel = true;
457             }
458         });
459
460         let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
461             def,
462             ctx,
463             curr_parent_module,
464             selection_range,
465             curr_file_id,
466         );
467
468         let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
469             let file_id = x.0;
470             let mut use_opt: Option<ast::Use> = None;
471             if file_id == curr_file_id {
472                 (&x.1).into_iter().for_each(|x| {
473                     let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
474                     if let Some(node) = node_opt {
475                         use_opt = Some(node.clone());
476                     }
477                 });
478             }
479             return use_opt;
480         });
481
482         let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
483         //Exists inside and outside selection
484         // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
485         // module
486         // - Use stmt for item is not present ->
487         //If it is not found, the definition is either ported inside new module or it stays
488         //outside:
489         //- Def is inside: Nothing to import
490         //- Def is outside: Import it inside with super
491
492         //Exists inside selection but not outside -> Check for the import of it in original module,
493         //get the use_tree_str, reconstruct the use stmt in new module
494
495         let mut import_path_to_be_removed: Option<TextRange> = None;
496         if exists_inside_sel && exists_outside_sel {
497             //Changes to be made only inside new module
498
499             //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
500             //If not, insert a use stmt with super and the given nameref
501             if let Some((use_tree_str, _)) =
502                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
503             {
504                 use_tree_str_opt = Some(use_tree_str);
505             } else if source_exists_outside_sel_in_same_mod {
506                 //Considered only after use_stmt is not present
507                 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
508                 //true for all cases)
509                 // false | false -> Do nothing
510                 // false | true -> If source is in selection -> nothing to do, If source is outside
511                 // mod -> ust_stmt transversal
512                 // true  | false -> super import insertion
513                 // true  | true -> super import insertion
514                 self.make_use_stmt_of_node_with_super(node_syntax);
515             }
516         } else if exists_inside_sel && !exists_outside_sel {
517             //Changes to be made inside new module, and remove import from outside
518
519             if let Some((use_tree_str, text_range_opt)) =
520                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
521             {
522                 if let Some(text_range) = text_range_opt {
523                     import_path_to_be_removed = Some(text_range);
524                 }
525                 use_tree_str_opt = Some(use_tree_str);
526             } else if source_exists_outside_sel_in_same_mod {
527                 self.make_use_stmt_of_node_with_super(node_syntax);
528             }
529         }
530
531         if let Some(use_tree_str) = use_tree_str_opt {
532             let mut use_tree_str = use_tree_str.clone();
533             use_tree_str.reverse();
534             if use_tree_str[0].to_string().contains("super") {
535                 let super_path = make::ext::ident_path("super");
536                 use_tree_str.insert(0, super_path)
537             }
538
539             let use_ =
540                 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
541             if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
542                 self.body_items.insert(0, item);
543             }
544         }
545
546         import_path_to_be_removed
547     }
548
549     fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) {
550         let super_path = make::ext::ident_path("super");
551         let node_path = make::ext::ident_path(&node_syntax.to_string());
552         let use_ = make::use_(
553             None,
554             make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
555         );
556         if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
557             self.body_items.insert(0, item);
558         }
559     }
560
561     fn process_use_stmt_for_import_resolve(
562         &self,
563         use_stmt_opt: Option<ast::Use>,
564         node_syntax: &SyntaxNode,
565     ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
566         if let Some(use_stmt) = use_stmt_opt {
567             for desc in use_stmt.syntax().descendants() {
568                 if let Some(path_seg) = ast::PathSegment::cast(desc) {
569                     if path_seg.syntax().to_string() == node_syntax.to_string() {
570                         let mut use_tree_str = vec![path_seg.parent_path()];
571                         get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
572                         for ancs in path_seg.syntax().ancestors() {
573                             //Here we are looking for use_tree with same string value as node
574                             //passed above as the range_to_remove function looks for a comma and
575                             //then includes it in the text range to remove it. But the comma only
576                             //appears at the use_tree level
577                             if let Some(use_tree) = ast::UseTree::cast(ancs) {
578                                 if use_tree.syntax().to_string() == node_syntax.to_string() {
579                                     return Some((
580                                         use_tree_str,
581                                         Some(range_to_remove(use_tree.syntax())),
582                                     ));
583                                 }
584                             }
585                         }
586
587                         return Some((use_tree_str, None));
588                     }
589                 }
590             }
591         }
592
593         None
594     }
595 }
596
597 fn does_source_exists_outside_sel_in_same_mod(
598     def: Definition,
599     ctx: &AssistContext,
600     curr_parent_module: &Option<ast::Module>,
601     selection_range: TextRange,
602     curr_file_id: FileId,
603 ) -> bool {
604     let mut source_exists_outside_sel_in_same_mod = false;
605     match def {
606         Definition::ModuleDef(it) => match it {
607             ModuleDef::Module(x) => {
608                 let source = x.definition_source(ctx.db());
609                 let have_same_parent;
610                 if let Some(ast_module) = &curr_parent_module {
611                     if let Some(hir_module) = x.parent(ctx.db()) {
612                         have_same_parent =
613                             compare_hir_and_ast_module(&ast_module, hir_module, ctx).is_some();
614                     } else {
615                         let source_file_id = source.file_id.original_file(ctx.db());
616                         have_same_parent = source_file_id == curr_file_id;
617                     }
618                 } else {
619                     let source_file_id = source.file_id.original_file(ctx.db());
620                     have_same_parent = source_file_id == curr_file_id;
621                 }
622
623                 if have_same_parent {
624                     match source.value {
625                         ModuleSource::Module(module_) => {
626                             source_exists_outside_sel_in_same_mod =
627                                 !selection_range.contains_range(module_.syntax().text_range());
628                         }
629                         _ => {}
630                     }
631                 }
632             }
633             ModuleDef::Function(x) => {
634                 if let Some(source) = x.source(ctx.db()) {
635                     let have_same_parent;
636                     if let Some(ast_module) = &curr_parent_module {
637                         have_same_parent =
638                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
639                                 .is_some();
640                     } else {
641                         let source_file_id = source.file_id.original_file(ctx.db());
642                         have_same_parent = source_file_id == curr_file_id;
643                     }
644
645                     if have_same_parent {
646                         source_exists_outside_sel_in_same_mod =
647                             !selection_range.contains_range(source.value.syntax().text_range());
648                     }
649                 }
650             }
651             ModuleDef::Adt(x) => {
652                 if let Some(source) = x.source(ctx.db()) {
653                     let have_same_parent;
654                     if let Some(ast_module) = &curr_parent_module {
655                         have_same_parent =
656                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
657                                 .is_some();
658                     } else {
659                         let source_file_id = source.file_id.original_file(ctx.db());
660                         have_same_parent = source_file_id == curr_file_id;
661                     }
662
663                     if have_same_parent {
664                         source_exists_outside_sel_in_same_mod =
665                             !selection_range.contains_range(source.value.syntax().text_range());
666                     }
667                 }
668             }
669             ModuleDef::Variant(x) => {
670                 if let Some(source) = x.source(ctx.db()) {
671                     let have_same_parent;
672                     if let Some(ast_module) = &curr_parent_module {
673                         have_same_parent =
674                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
675                                 .is_some();
676                     } else {
677                         let source_file_id = source.file_id.original_file(ctx.db());
678                         have_same_parent = source_file_id == curr_file_id;
679                     }
680
681                     if have_same_parent {
682                         source_exists_outside_sel_in_same_mod =
683                             !selection_range.contains_range(source.value.syntax().text_range());
684                     }
685                 }
686             }
687             ModuleDef::Const(x) => {
688                 if let Some(source) = x.source(ctx.db()) {
689                     let have_same_parent;
690                     if let Some(ast_module) = &curr_parent_module {
691                         have_same_parent =
692                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
693                                 .is_some();
694                     } else {
695                         let source_file_id = source.file_id.original_file(ctx.db());
696                         have_same_parent = source_file_id == curr_file_id;
697                     }
698
699                     if have_same_parent {
700                         source_exists_outside_sel_in_same_mod =
701                             !selection_range.contains_range(source.value.syntax().text_range());
702                     }
703                 }
704             }
705             ModuleDef::Static(x) => {
706                 if let Some(source) = x.source(ctx.db()) {
707                     let have_same_parent;
708                     if let Some(ast_module) = &curr_parent_module {
709                         have_same_parent =
710                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
711                                 .is_some();
712                     } else {
713                         let source_file_id = source.file_id.original_file(ctx.db());
714                         have_same_parent = source_file_id == curr_file_id;
715                     }
716
717                     if have_same_parent {
718                         source_exists_outside_sel_in_same_mod =
719                             !selection_range.contains_range(source.value.syntax().text_range());
720                     }
721                 }
722             }
723             ModuleDef::Trait(x) => {
724                 if let Some(source) = x.source(ctx.db()) {
725                     let have_same_parent;
726                     if let Some(ast_module) = &curr_parent_module {
727                         have_same_parent =
728                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
729                                 .is_some();
730                     } else {
731                         let source_file_id = source.file_id.original_file(ctx.db());
732                         have_same_parent = source_file_id == curr_file_id;
733                     }
734
735                     if have_same_parent {
736                         source_exists_outside_sel_in_same_mod =
737                             !selection_range.contains_range(source.value.syntax().text_range());
738                     }
739                 }
740             }
741             ModuleDef::TypeAlias(x) => {
742                 if let Some(source) = x.source(ctx.db()) {
743                     let have_same_parent;
744                     if let Some(ast_module) = &curr_parent_module {
745                         have_same_parent =
746                             compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
747                                 .is_some();
748                     } else {
749                         let source_file_id = source.file_id.original_file(ctx.db());
750                         have_same_parent = source_file_id == curr_file_id;
751                     }
752
753                     if have_same_parent {
754                         source_exists_outside_sel_in_same_mod =
755                             !selection_range.contains_range(source.value.syntax().text_range());
756                     }
757                 }
758             }
759             _ => {}
760         },
761         _ => {}
762     }
763
764     return source_exists_outside_sel_in_same_mod;
765 }
766
767 fn get_replacements_for_visibilty_change(
768     items: Vec<ast::Item>,
769     is_clone_for_updated: bool,
770 ) -> (
771     Vec<ast::Item>,
772     Vec<(Option<ast::Visibility>, SyntaxNode)>,
773     Vec<(Option<ast::Visibility>, SyntaxNode)>,
774     Vec<ast::Impl>,
775 ) {
776     let mut replacements = Vec::new();
777     let mut record_field_parents = Vec::new();
778     let mut impls = Vec::new();
779     let mut body_items = Vec::new();
780
781     items.into_iter().for_each(|item| {
782         let mut item = item;
783         if !is_clone_for_updated {
784             item = item.clone_for_update();
785         }
786         body_items.push(item.clone());
787         //Use stmts are ignored
788         match item {
789             ast::Item::Const(it) => {
790                 replacements.push((it.visibility().clone(), it.syntax().clone()))
791             }
792             ast::Item::Enum(it) => {
793                 replacements.push((it.visibility().clone(), it.syntax().clone()))
794             }
795             ast::Item::ExternCrate(it) => {
796                 replacements.push((it.visibility().clone(), it.syntax().clone()))
797             }
798             ast::Item::Fn(it) => replacements.push((it.visibility().clone(), it.syntax().clone())),
799             ast::Item::Impl(it) => impls.push(it),
800             ast::Item::MacroRules(it) => {
801                 replacements.push((it.visibility().clone(), it.syntax().clone()))
802             }
803             ast::Item::MacroDef(it) => {
804                 replacements.push((it.visibility().clone(), it.syntax().clone()))
805             }
806             ast::Item::Module(it) => {
807                 replacements.push((it.visibility().clone(), it.syntax().clone()))
808             }
809             ast::Item::Static(it) => {
810                 replacements.push((it.visibility().clone(), it.syntax().clone()))
811             }
812             ast::Item::Struct(it) => {
813                 replacements.push((it.visibility().clone(), it.syntax().clone()));
814                 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
815             }
816             ast::Item::Trait(it) => {
817                 replacements.push((it.visibility().clone(), it.syntax().clone()))
818             }
819             ast::Item::TypeAlias(it) => {
820                 replacements.push((it.visibility().clone(), it.syntax().clone()))
821             }
822             ast::Item::Union(it) => {
823                 replacements.push((it.visibility().clone(), it.syntax().clone()));
824                 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
825             }
826             _ => (),
827         }
828     });
829
830     return (body_items, replacements, record_field_parents, impls);
831 }
832
833 fn get_use_tree_paths_from_path(
834     path: ast::Path,
835     use_tree_str: &mut Vec<ast::Path>,
836 ) -> Option<&mut Vec<ast::Path>> {
837     path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
838         if let Some(use_tree) = ast::UseTree::cast(x.clone()) {
839             if let Some(upper_tree_path) = use_tree.path() {
840                 if upper_tree_path.to_string() != path.to_string() {
841                     use_tree_str.push(upper_tree_path.clone());
842                     get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
843                     return Some(use_tree);
844                 }
845             }
846         }
847         None
848     })?;
849
850     Some(use_tree_str)
851 }
852
853 fn add_change_vis(
854     vis: Option<ast::Visibility>,
855     node_or_token_opt: Option<syntax::SyntaxElement>,
856 ) -> Option<()> {
857     if let Some(vis) = vis {
858         if vis.syntax().text() == "pub" {
859             ted::replace(vis.syntax(), make::visibility_pub_crate().syntax().clone_for_update());
860         }
861     } else {
862         if let Some(node_or_token) = node_or_token_opt {
863             let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
864             if let Some(node) = node_or_token.as_node() {
865                 ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
866             }
867             if let Some(token) = node_or_token.as_token() {
868                 ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
869             }
870         }
871     }
872
873     Some(())
874 }
875
876 fn compare_hir_and_ast_module(
877     ast_module: &ast::Module,
878     hir_module: hir::Module,
879     ctx: &AssistContext,
880 ) -> Option<()> {
881     let hir_mod_name = hir_module.name(ctx.db())?;
882     let ast_mod_name = ast_module.name()?;
883     if hir_mod_name.to_string() != ast_mod_name.to_string() {
884         return None;
885     }
886
887     return Some(());
888 }
889
890 #[cfg(test)]
891 mod tests {
892     use crate::tests::{check_assist, check_assist_not_applicable};
893
894     use super::*;
895
896     #[test]
897     fn test_not_applicable_without_selection() {
898         check_assist_not_applicable(
899             extract_module,
900             r"
901 $0pub struct PublicStruct {
902     field: i32,
903 }
904             ",
905         )
906     }
907
908     #[test]
909     fn test_extract_module() {
910         check_assist(
911             extract_module,
912             r"
913             mod thirdpartycrate {
914                 pub mod nest {
915                     pub struct SomeType;
916                     pub struct SomeType2;
917                 }
918                 pub struct SomeType1;
919             }
920
921             mod bar {
922                 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
923
924                 pub struct PublicStruct {
925                     field: PrivateStruct,
926                     field1: SomeType1,
927                 }
928
929                 impl PublicStruct {
930                     pub fn new() -> Self {
931                         Self { field: PrivateStruct::new(), field1: SomeType1 }
932                     }
933                 }
934
935                 fn foo() {
936                     let _s = PrivateStruct::new();
937                     let _a = bar();
938                 }
939
940 $0struct PrivateStruct {
941     inner: SomeType,
942 }
943
944 pub struct PrivateStruct1 {
945     pub inner: i32,
946 }
947
948 impl PrivateStruct {
949     fn new() -> Self {
950          PrivateStruct { inner: SomeType }
951     }
952 }
953
954 fn bar() -> i32 {
955     2
956 }$0
957             }
958             ",
959             r"
960             mod thirdpartycrate {
961                 pub mod nest {
962                     pub struct SomeType;
963                     pub struct SomeType2;
964                 }
965                 pub struct SomeType1;
966             }
967
968             mod bar {
969                 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
970
971                 pub struct PublicStruct {
972                     field: modname::PrivateStruct,
973                     field1: SomeType1,
974                 }
975
976                 impl PublicStruct {
977                     pub fn new() -> Self {
978                         Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
979                     }
980                 }
981
982                 fn foo() {
983                     let _s = modname::PrivateStruct::new();
984                     let _a = modname::bar();
985                 }
986
987 mod modname {
988     use crate::thirdpartycrate::nest::SomeType;
989
990     pub(crate) struct PrivateStruct {
991         pub(crate) inner: SomeType,
992     }
993
994     pub(crate) struct PrivateStruct1 {
995         pub(crate) inner: i32,
996     }
997
998     impl PrivateStruct {
999         pub(crate) fn new() -> Self {
1000              PrivateStruct { inner: SomeType }
1001         }
1002     }
1003
1004     pub(crate) fn bar() -> i32 {
1005         2
1006     }
1007 }
1008             }
1009             ",
1010         );
1011     }
1012
1013     #[test]
1014     fn test_extract_module_for_function_only() {
1015         check_assist(
1016             extract_module,
1017             r"
1018 $0fn foo(name: i32) -> i32 {
1019     name + 1
1020 }$0
1021
1022                 fn bar(name: i32) -> i32 {
1023                     name + 2
1024                 }
1025             ",
1026             r"
1027 mod modname {
1028     pub(crate) fn foo(name: i32) -> i32 {
1029         name + 1
1030     }
1031 }
1032
1033                 fn bar(name: i32) -> i32 {
1034                     name + 2
1035                 }
1036             ",
1037         )
1038     }
1039
1040     #[test]
1041     fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1042         check_assist(
1043             extract_module,
1044             r"
1045             mod impl_play {
1046 $0struct A {}
1047
1048 impl A {
1049     pub fn new_a() -> i32 {
1050         2
1051     }
1052 }$0
1053
1054                 fn a() {
1055                     let _a = A::new_a();
1056                 }
1057             }
1058             ",
1059             r"
1060             mod impl_play {
1061 mod modname {
1062     pub(crate) struct A {}
1063
1064     impl A {
1065         pub(crate) fn new_a() -> i32 {
1066             2
1067         }
1068     }
1069 }
1070
1071                 fn a() {
1072                     let _a = modname::A::new_a();
1073                 }
1074             }
1075             ",
1076         )
1077     }
1078
1079     #[test]
1080     fn test_import_resolve_when_its_only_inside_selection() {
1081         check_assist(
1082             extract_module,
1083             r"
1084             mod foo {
1085                 pub struct PrivateStruct;
1086                 pub struct PrivateStruct1;
1087             }
1088
1089             mod bar {
1090                 use super::foo::{PrivateStruct, PrivateStruct1};
1091
1092 $0struct Strukt {
1093     field: PrivateStruct,
1094 }$0
1095
1096                 struct Strukt1 {
1097                     field: PrivateStruct1,
1098                 }
1099             }
1100             ",
1101             r"
1102             mod foo {
1103                 pub struct PrivateStruct;
1104                 pub struct PrivateStruct1;
1105             }
1106
1107             mod bar {
1108                 use super::foo::{PrivateStruct1};
1109
1110 mod modname {
1111     use super::super::foo::PrivateStruct;
1112
1113     pub(crate) struct Strukt {
1114         pub(crate) field: PrivateStruct,
1115     }
1116 }
1117
1118                 struct Strukt1 {
1119                     field: PrivateStruct1,
1120                 }
1121             }
1122             ",
1123         )
1124     }
1125
1126     #[test]
1127     fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1128         check_assist(
1129             extract_module,
1130             r"
1131             mod foo {
1132                 pub struct PrivateStruct;
1133             }
1134
1135             mod bar {
1136                 use super::foo::PrivateStruct;
1137
1138 $0struct Strukt {
1139     field: PrivateStruct,
1140 }$0
1141
1142                 struct Strukt1 {
1143                     field: PrivateStruct,
1144                 }
1145             }
1146             ",
1147             r"
1148             mod foo {
1149                 pub struct PrivateStruct;
1150             }
1151
1152             mod bar {
1153                 use super::foo::PrivateStruct;
1154
1155 mod modname {
1156     use super::super::foo::PrivateStruct;
1157
1158     pub(crate) struct Strukt {
1159         pub(crate) field: PrivateStruct,
1160     }
1161 }
1162
1163                 struct Strukt1 {
1164                     field: PrivateStruct,
1165                 }
1166             }
1167             ",
1168         )
1169     }
1170
1171     #[test]
1172     fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1173         check_assist(
1174             extract_module,
1175             r"
1176             mod bar {
1177                 pub struct PrivateStruct;
1178
1179 $0struct Strukt {
1180     field: PrivateStruct,
1181 }$0
1182
1183                 struct Strukt1 {
1184                     field: PrivateStruct,
1185                 }
1186             }
1187             ",
1188             r"
1189             mod bar {
1190                 pub struct PrivateStruct;
1191
1192 mod modname {
1193     use super::PrivateStruct;
1194
1195     pub(crate) struct Strukt {
1196         pub(crate) field: PrivateStruct,
1197     }
1198 }
1199
1200                 struct Strukt1 {
1201                     field: PrivateStruct,
1202                 }
1203             }
1204             ",
1205         )
1206     }
1207
1208     #[test]
1209     fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
1210         check_assist(
1211             extract_module,
1212             r"
1213             mod impl_play {
1214                 struct A {}
1215
1216 $0impl A {
1217     pub fn new_a() -> i32 {
1218         2
1219     }
1220 }$0
1221
1222                 fn a() {
1223                     let _a = A::new_a();
1224                 }
1225             }
1226             ",
1227             r"
1228             mod impl_play {
1229                 struct A {}
1230
1231 mod modname {
1232     use super::A;
1233
1234     impl A {
1235         pub(crate) fn new_a() -> i32 {
1236             2
1237         }
1238     }
1239 }
1240
1241                 fn a() {
1242                     let _a = A::new_a();
1243                 }
1244             }
1245             ",
1246         )
1247     }
1248
1249     #[test]
1250     fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1251     ) {
1252         check_assist(
1253             extract_module,
1254             r"
1255             mod foo {
1256                 pub struct A {}
1257             }
1258             mod impl_play {
1259                 use super::foo::A;
1260
1261 $0impl A {
1262     pub fn new_a() -> i32 {
1263         2
1264     }
1265 }$0
1266
1267                 fn a() {
1268                     let _a = A::new_a();
1269                 }
1270             }
1271             ",
1272             r"
1273             mod foo {
1274                 pub struct A {}
1275             }
1276             mod impl_play {
1277                 use super::foo::A;
1278
1279 mod modname {
1280     use super::super::foo::A;
1281
1282     impl A {
1283         pub(crate) fn new_a() -> i32 {
1284             2
1285         }
1286     }
1287 }
1288
1289                 fn a() {
1290                     let _a = A::new_a();
1291                 }
1292             }
1293             ",
1294         )
1295     }
1296
1297     #[test]
1298     fn test_import_resolve_for_trait_bounds_on_function() {
1299         check_assist(
1300             extract_module,
1301             r"
1302             mod impl_play2 {
1303                 trait JustATrait {}
1304
1305 $0struct 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 }$0
1317             }
1318             ",
1319             r"
1320             mod impl_play2 {
1321                 trait JustATrait {}
1322
1323 mod modname {
1324     use super::JustATrait;
1325
1326     pub(crate) struct A {}
1327
1328     pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1329         arg
1330     }
1331
1332     impl JustATrait for A {}
1333
1334     pub(crate) fn bar() {
1335         let a = A {};
1336         foo(a);
1337     }
1338 }
1339             }
1340             ",
1341         )
1342     }
1343
1344     #[test]
1345     fn test_extract_module_for_module() {
1346         check_assist(
1347             extract_module,
1348             r"
1349             mod impl_play2 {
1350 $0mod impl_play {
1351     pub struct A {}
1352 }$0
1353             }
1354             ",
1355             r"
1356             mod impl_play2 {
1357 mod modname {
1358     pub(crate) mod impl_play {
1359         pub struct A {}
1360     }
1361 }
1362             }
1363             ",
1364         )
1365     }
1366
1367     #[test]
1368     fn test_extract_module_with_multiple_files() {
1369         check_assist(
1370             extract_module,
1371             r"
1372             //- /main.rs
1373             mod foo;
1374
1375             use foo::PrivateStruct;
1376
1377             pub struct Strukt {
1378                 field: PrivateStruct,
1379             }
1380
1381             fn main() {
1382                 $0struct Strukt1 {
1383                     field: Strukt,
1384                 }$0
1385             }
1386             //- /foo.rs
1387             pub struct PrivateStruct;
1388             ",
1389             r"
1390             mod foo;
1391
1392             use foo::PrivateStruct;
1393
1394             pub struct Strukt {
1395                 field: PrivateStruct,
1396             }
1397
1398             fn main() {
1399                 mod modname {
1400                     use super::Strukt;
1401
1402                     pub(crate) struct Strukt1 {
1403                         pub(crate) field: Strukt,
1404                     }
1405                 }
1406             }
1407             ",
1408         )
1409     }
1410 }