1 use std::collections::{HashMap, HashSet};
3 use hir::{HasSource, ModuleSource};
5 assists::{AssistId, AssistKind},
7 defs::{Definition, NameClass, NameRefClass},
8 search::{FileReference, SearchScope},
12 algo::find_node_at_range,
15 edit::{AstNodeEdit, IndentLevel},
16 make, HasName, HasVisibility,
18 match_ast, ted, AstNode, SourceFile,
19 SyntaxKind::WHITESPACE,
20 SyntaxNode, TextRange,
23 use crate::{AssistContext, Assists};
25 use super::remove_unused_param::range_to_remove;
27 // Assist: extract_module
29 // Extracts a selected region as seperate module. All the references, visibility and imports are
33 // $0fn foo(name: i32) -> i32 {
37 // fn bar(name: i32) -> i32 {
44 // pub(crate) fn foo(name: i32) -> i32 {
49 // fn bar(name: i32) -> i32 {
53 pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
54 if ctx.has_empty_selection() {
58 let node = ctx.covering_element();
59 let node = match node {
60 syntax::NodeOrToken::Node(n) => n,
61 syntax::NodeOrToken::Token(t) => t.parent()?,
64 //If the selection is inside impl block, we need to place new module outside impl block,
65 //as impl blocks cannot contain modules
67 let mut impl_parent: Option<ast::Impl> = None;
68 let mut impl_child_count: usize = 0;
69 if let Some(parent_assoc_list) = node.parent() {
70 if let Some(parent_impl) = parent_assoc_list.parent() {
71 if let Some(impl_) = ast::Impl::cast(parent_impl) {
72 impl_child_count = parent_assoc_list.children().count();
73 impl_parent = Some(impl_);
78 let mut curr_parent_module: Option<ast::Module> = None;
79 if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
80 curr_parent_module = ast::Module::cast(mod_syn_opt);
83 let mut module = extract_target(&node, ctx.selection_trimmed())?;
84 if module.body_items.len() == 0 {
88 let old_item_indent = module.body_items[0].indent_level();
90 //This takes place in three steps:
92 //- Firstly, we will update the references(usages) e.g. converting a
93 // function call bar() to modname::bar(), and similarly for other items
95 //- Secondly, changing the visibility of each item inside the newly selected module
96 // i.e. making a fn a() {} to pub(crate) fn a() {}
98 //- Thirdly, resolving all the imports this includes removing paths from imports
99 // outside the module, shifting/cloning them inside new module, or shifting the imports, or making
100 // new import statemnts
102 //We are getting item usages and record_fields together, record_fields
103 //for change_visibility and usages for first point mentioned above in the process
104 let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
106 let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
107 module.body_items = module.change_visibility(record_fields)?;
108 if module.body_items.len() == 0 {
113 AssistId("extract_module", AssistKind::RefactorExtract),
117 let mut body_items: Vec<String> = Vec::new();
118 let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
119 let mut new_item_indent = old_item_indent + 1;
121 if impl_parent.is_some() {
122 new_item_indent = old_item_indent + 2;
124 items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat();
127 for item in items_to_be_processed {
128 let item = item.indent(IndentLevel(1));
129 let mut indented_item = String::new();
130 format_to!(indented_item, "{}{}", new_item_indent, item.to_string());
131 body_items.push(indented_item);
134 let mut body = body_items.join("\n\n");
136 if let Some(impl_) = &impl_parent {
137 let mut impl_body_def = String::new();
139 if let Some(self_ty) = impl_.self_ty() {
142 "{}impl {} {{\n{}\n{}}}",
149 body = impl_body_def;
151 // Add the import for enum/struct corresponding to given impl block
152 if let Some(_) = module.make_use_stmt_of_node_with_super(self_ty.syntax()) {
153 for item in module.use_items {
154 let mut indented_item = String::new();
161 body = format!("{}\n\n{}", indented_item, body);
167 let mut module_def = String::new();
169 format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
171 let mut usages_to_be_updated_for_curr_file = vec![];
172 for usages_to_be_updated_for_file in usages_to_be_processed {
173 if usages_to_be_updated_for_file.0 == ctx.file_id() {
174 usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
177 builder.edit_file(usages_to_be_updated_for_file.0);
178 for usage_to_be_processed in usages_to_be_updated_for_file.1 {
179 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
183 builder.edit_file(ctx.file_id());
184 for usage_to_be_processed in usages_to_be_updated_for_curr_file {
185 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
188 for import_path_text_range in import_paths_to_be_removed {
189 builder.delete(import_path_text_range);
192 if let Some(impl_) = impl_parent {
193 let node_to_be_removed;
195 // Remove complete impl block if it has only one child (as such it will be empty
196 // after deleting that child)
197 if impl_child_count == 1 {
198 node_to_be_removed = impl_.syntax()
200 //Remove selected node
201 node_to_be_removed = &node;
204 builder.delete(node_to_be_removed.text_range());
205 // Remove preceding indentation from node
206 if let Some(range) = indent_range_before_given_node(node_to_be_removed) {
207 builder.delete(range);
210 builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def));
212 builder.replace(module.text_range, module_def)
220 text_range: TextRange,
222 body_items: Vec<ast::Item>, // All items except use items
223 use_items: Vec<ast::Item>, // Use items are kept separately as they help when the selection is inside an impl block, we can directly take these items and keep them outside generated impl block inside generated module
226 fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
227 let mut use_items = vec![];
229 let mut body_items: Vec<ast::Item> = node
231 .filter_map(|child| {
232 if selection_range.contains_range(child.text_range()) {
233 let child_kind = child.kind();
234 if let Some(item) = ast::Item::cast(child) {
235 if ast::Use::can_cast(child_kind) {
236 use_items.push(item);
247 if let Some(node_item) = ast::Item::cast(node.clone()) {
248 body_items.push(node_item);
251 Some(Module { text_range: selection_range, name: "modname".to_string(), body_items, use_items })
255 fn get_usages_and_record_fields(
258 ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
259 let mut adt_fields = Vec::new();
260 let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
262 //Here impl is not included as each item inside impl will be tied to the parent of
263 //implementing block(a struct, enum, etc), if the parent is in selected module, it will
264 //get updated by ADT section given below or if it is not, then we dont need to do any operation
265 self.body_items.iter().cloned().for_each(|item| {
267 match (item.syntax()) {
269 if let Some( nod ) = ctx.sema.to_def(&it) {
270 let node_def = Definition::Adt(nod);
271 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
273 //Enum Fields are not allowed to explicitly specify pub, it is implied
275 ast::Adt::Struct(x) => {
276 if let Some(field_list) = x.field_list() {
278 ast::FieldList::RecordFieldList(record_field_list) => {
279 record_field_list.fields().for_each(|record_field| {
280 adt_fields.push(record_field.syntax().clone());
283 ast::FieldList::TupleFieldList(tuple_field_list) => {
284 tuple_field_list.fields().for_each(|tuple_field| {
285 adt_fields.push(tuple_field.syntax().clone());
291 ast::Adt::Union(x) => {
292 if let Some(record_field_list) = x.record_field_list() {
293 record_field_list.fields().for_each(|record_field| {
294 adt_fields.push(record_field.syntax().clone());
298 ast::Adt::Enum(_) => {},
302 ast::TypeAlias(it) => {
303 if let Some( nod ) = ctx.sema.to_def(&it) {
304 let node_def = Definition::TypeAlias(nod);
305 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
309 if let Some( nod ) = ctx.sema.to_def(&it) {
310 let node_def = Definition::Const(nod);
311 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
315 if let Some( nod ) = ctx.sema.to_def(&it) {
316 let node_def = Definition::Static(nod);
317 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
321 if let Some( nod ) = ctx.sema.to_def(&it) {
322 let node_def = Definition::Function(nod);
323 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
327 if let Some(nod) = ctx.sema.to_def(&it) {
328 self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs);
336 return (refs, adt_fields);
339 fn expand_and_group_usages_file_wise(
342 node_def: Definition,
343 refs: &mut HashMap<FileId, Vec<(TextRange, String)>>,
345 for (file_id, references) in node_def.usages(&ctx.sema).all() {
346 if let Some(file_refs) = refs.get_mut(&file_id) {
347 let mut usages = self.expand_ref_to_usages(references, ctx, file_id);
348 file_refs.append(&mut usages);
350 refs.insert(file_id, self.expand_ref_to_usages(references, ctx, file_id));
355 fn expand_ref_to_usages(
357 refs: Vec<FileReference>,
360 ) -> Vec<(TextRange, String)> {
361 let source_file = ctx.sema.parse(file_id);
363 let mut usages_to_be_processed_for_file = Vec::new();
365 if let Some(x) = self.get_usage_to_be_processed(&source_file, usage) {
366 usages_to_be_processed_for_file.push(x);
370 usages_to_be_processed_for_file
373 fn get_usage_to_be_processed(
375 source_file: &SourceFile,
376 FileReference { range, name, .. }: FileReference,
377 ) -> Option<(TextRange, String)> {
378 let path: Option<ast::Path> = find_node_at_range(source_file.syntax(), range);
382 for desc in path.syntax().descendants() {
383 if desc.to_string() == name.syntax().to_string()
384 && !self.text_range.contains_range(desc.text_range())
386 if let Some(name_ref) = ast::NameRef::cast(desc) {
388 name_ref.syntax().text_range(),
389 format!("{}::{}", self.name, name_ref),
398 fn change_visibility(&self, record_fields: Vec<SyntaxNode>) -> Option<Vec<ast::Item>> {
399 let (body_items, mut replacements, record_field_parents, impls) =
400 get_replacements_for_visibilty_change(self.body_items.clone(), false);
402 let mut impl_items = Vec::new();
404 let mut this_impl_items = Vec::new();
405 for node in impl_.syntax().descendants() {
406 if let Some(item) = ast::Item::cast(node) {
407 this_impl_items.push(item);
411 impl_items.append(&mut this_impl_items);
414 let (_, mut impl_item_replacements, _, _) =
415 get_replacements_for_visibilty_change(impl_items, true);
417 replacements.append(&mut impl_item_replacements);
419 record_field_parents.into_iter().for_each(|x| {
420 x.1.descendants().filter_map(ast::RecordField::cast).for_each(|desc| {
421 let is_record_field_present = record_fields
424 .find(|x| x.to_string() == desc.to_string())
426 if is_record_field_present {
427 replacements.push((desc.visibility(), desc.syntax().clone()));
432 replacements.into_iter().for_each(|(vis, syntax)| {
433 add_change_vis(vis, syntax.first_child_or_token());
441 curr_parent_module: Option<ast::Module>,
443 ) -> Vec<TextRange> {
444 let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
445 let mut node_set: HashSet<String> = HashSet::new();
447 self.body_items.clone().into_iter().for_each(|item| {
448 item.syntax().descendants().for_each(|x| {
449 if let Some(name) = ast::Name::cast(x.clone()) {
450 if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
451 //Necessary to avoid two same names going through
452 if !node_set.contains(&name.syntax().to_string()) {
453 node_set.insert(name.syntax().to_string());
454 let def_opt: Option<Definition> = match name_classify {
455 NameClass::Definition(def) => Some(def),
459 if let Some(def) = def_opt {
460 if let Some(import_path) = self
461 .process_names_and_namerefs_for_import_resolve(
468 import_paths_to_be_removed.push(import_path);
475 if let Some(name_ref) = ast::NameRef::cast(x) {
476 if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
477 //Necessary to avoid two same names going through
478 if !node_set.contains(&name_ref.syntax().to_string()) {
479 node_set.insert(name_ref.syntax().to_string());
480 let def_opt: Option<Definition> = match name_classify {
481 NameRefClass::Definition(def) => Some(def),
485 if let Some(def) = def_opt {
486 if let Some(import_path) = self
487 .process_names_and_namerefs_for_import_resolve(
494 import_paths_to_be_removed.push(import_path);
503 import_paths_to_be_removed
506 fn process_names_and_namerefs_for_import_resolve(
509 node_syntax: &SyntaxNode,
510 curr_parent_module: &Option<ast::Module>,
512 ) -> Option<TextRange> {
513 //We only need to find in the current file
514 let selection_range = ctx.selection_trimmed();
515 let curr_file_id = ctx.file_id();
516 let search_scope = SearchScope::single_file(curr_file_id);
517 let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
518 let file = ctx.sema.parse(curr_file_id);
520 let mut exists_inside_sel = false;
521 let mut exists_outside_sel = false;
522 usage_res.clone().into_iter().for_each(|x| {
523 let mut non_use_nodes_itr = (&x.1).into_iter().filter_map(|x| {
524 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
525 let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
534 .find(|x| !selection_range.contains_range(x.syntax().text_range()))
537 exists_outside_sel = true;
540 .find(|x| selection_range.contains_range(x.syntax().text_range()))
543 exists_inside_sel = true;
547 let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
555 let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
557 let mut use_opt: Option<ast::Use> = None;
558 if file_id == curr_file_id {
559 (&x.1).into_iter().for_each(|x| {
560 let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
561 if let Some(node) = node_opt {
562 use_opt = Some(node);
569 let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
570 //Exists inside and outside selection
571 // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
573 // - Use stmt for item is not present ->
574 //If it is not found, the definition is either ported inside new module or it stays
576 //- Def is inside: Nothing to import
577 //- Def is outside: Import it inside with super
579 //Exists inside selection but not outside -> Check for the import of it in original module,
580 //get the use_tree_str, reconstruct the use stmt in new module
582 let mut import_path_to_be_removed: Option<TextRange> = None;
583 if exists_inside_sel && exists_outside_sel {
584 //Changes to be made only inside new module
586 //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
587 //If not, insert a use stmt with super and the given nameref
588 if let Some((use_tree_str, _)) =
589 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
591 use_tree_str_opt = Some(use_tree_str);
592 } else if source_exists_outside_sel_in_same_mod {
593 //Considered only after use_stmt is not present
594 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
595 //true for all cases)
596 // false | false -> Do nothing
597 // false | true -> If source is in selection -> nothing to do, If source is outside
598 // mod -> ust_stmt transversal
599 // true | false -> super import insertion
600 // true | true -> super import insertion
601 self.make_use_stmt_of_node_with_super(node_syntax);
603 } else if exists_inside_sel && !exists_outside_sel {
604 //Changes to be made inside new module, and remove import from outside
606 if let Some((use_tree_str, text_range_opt)) =
607 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
609 if let Some(text_range) = text_range_opt {
610 import_path_to_be_removed = Some(text_range);
612 use_tree_str_opt = Some(use_tree_str);
613 } else if source_exists_outside_sel_in_same_mod {
614 self.make_use_stmt_of_node_with_super(node_syntax);
618 if let Some(use_tree_str) = use_tree_str_opt {
619 let mut use_tree_str = use_tree_str;
620 use_tree_str.reverse();
621 if use_tree_str[0].to_string().contains("super") {
622 let super_path = make::ext::ident_path("super");
623 use_tree_str.insert(0, super_path)
627 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
628 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
629 self.use_items.insert(0, item);
633 import_path_to_be_removed
636 fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> Option<ast::Item> {
637 let super_path = make::ext::ident_path("super");
638 let node_path = make::ext::ident_path(&node_syntax.to_string());
639 let use_ = make::use_(
641 make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
644 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
645 self.use_items.insert(0, item.clone());
652 fn process_use_stmt_for_import_resolve(
654 use_stmt_opt: Option<ast::Use>,
655 node_syntax: &SyntaxNode,
656 ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
657 if let Some(use_stmt) = use_stmt_opt {
658 for desc in use_stmt.syntax().descendants() {
659 if let Some(path_seg) = ast::PathSegment::cast(desc) {
660 if path_seg.syntax().to_string() == node_syntax.to_string() {
661 let mut use_tree_str = vec![path_seg.parent_path()];
662 get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
663 for ancs in path_seg.syntax().ancestors() {
664 //Here we are looking for use_tree with same string value as node
665 //passed above as the range_to_remove function looks for a comma and
666 //then includes it in the text range to remove it. But the comma only
667 //appears at the use_tree level
668 if let Some(use_tree) = ast::UseTree::cast(ancs) {
669 if use_tree.syntax().to_string() == node_syntax.to_string() {
672 Some(range_to_remove(use_tree.syntax())),
678 return Some((use_tree_str, None));
688 fn does_source_exists_outside_sel_in_same_mod(
691 curr_parent_module: &Option<ast::Module>,
692 selection_range: TextRange,
693 curr_file_id: FileId,
695 let mut source_exists_outside_sel_in_same_mod = false;
697 Definition::Module(x) => {
698 let source = x.definition_source(ctx.db());
699 let have_same_parent;
700 if let Some(ast_module) = &curr_parent_module {
701 if let Some(hir_module) = x.parent(ctx.db()) {
703 compare_hir_and_ast_module(ast_module, hir_module, ctx).is_some();
705 let source_file_id = source.file_id.original_file(ctx.db());
706 have_same_parent = source_file_id == curr_file_id;
709 let source_file_id = source.file_id.original_file(ctx.db());
710 have_same_parent = source_file_id == curr_file_id;
713 if have_same_parent {
715 ModuleSource::Module(module_) => {
716 source_exists_outside_sel_in_same_mod =
717 !selection_range.contains_range(module_.syntax().text_range());
723 Definition::Function(x) => {
724 if let Some(source) = x.source(ctx.db()) {
725 let have_same_parent;
726 if let Some(ast_module) = &curr_parent_module {
728 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
730 let source_file_id = source.file_id.original_file(ctx.db());
731 have_same_parent = source_file_id == curr_file_id;
734 if have_same_parent {
735 source_exists_outside_sel_in_same_mod =
736 !selection_range.contains_range(source.value.syntax().text_range());
740 Definition::Adt(x) => {
741 if let Some(source) = x.source(ctx.db()) {
742 let have_same_parent;
743 if let Some(ast_module) = &curr_parent_module {
745 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
747 let source_file_id = source.file_id.original_file(ctx.db());
748 have_same_parent = source_file_id == curr_file_id;
751 if have_same_parent {
752 source_exists_outside_sel_in_same_mod =
753 !selection_range.contains_range(source.value.syntax().text_range());
757 Definition::Variant(x) => {
758 if let Some(source) = x.source(ctx.db()) {
759 let have_same_parent;
760 if let Some(ast_module) = &curr_parent_module {
762 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
764 let source_file_id = source.file_id.original_file(ctx.db());
765 have_same_parent = source_file_id == curr_file_id;
768 if have_same_parent {
769 source_exists_outside_sel_in_same_mod =
770 !selection_range.contains_range(source.value.syntax().text_range());
774 Definition::Const(x) => {
775 if let Some(source) = x.source(ctx.db()) {
776 let have_same_parent;
777 if let Some(ast_module) = &curr_parent_module {
779 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
781 let source_file_id = source.file_id.original_file(ctx.db());
782 have_same_parent = source_file_id == curr_file_id;
785 if have_same_parent {
786 source_exists_outside_sel_in_same_mod =
787 !selection_range.contains_range(source.value.syntax().text_range());
791 Definition::Static(x) => {
792 if let Some(source) = x.source(ctx.db()) {
793 let have_same_parent;
794 if let Some(ast_module) = &curr_parent_module {
796 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
798 let source_file_id = source.file_id.original_file(ctx.db());
799 have_same_parent = source_file_id == curr_file_id;
802 if have_same_parent {
803 source_exists_outside_sel_in_same_mod =
804 !selection_range.contains_range(source.value.syntax().text_range());
808 Definition::Trait(x) => {
809 if let Some(source) = x.source(ctx.db()) {
810 let have_same_parent;
811 if let Some(ast_module) = &curr_parent_module {
813 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
815 let source_file_id = source.file_id.original_file(ctx.db());
816 have_same_parent = source_file_id == curr_file_id;
819 if have_same_parent {
820 source_exists_outside_sel_in_same_mod =
821 !selection_range.contains_range(source.value.syntax().text_range());
825 Definition::TypeAlias(x) => {
826 if let Some(source) = x.source(ctx.db()) {
827 let have_same_parent;
828 if let Some(ast_module) = &curr_parent_module {
830 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some();
832 let source_file_id = source.file_id.original_file(ctx.db());
833 have_same_parent = source_file_id == curr_file_id;
836 if have_same_parent {
837 source_exists_outside_sel_in_same_mod =
838 !selection_range.contains_range(source.value.syntax().text_range());
845 return source_exists_outside_sel_in_same_mod;
848 fn get_replacements_for_visibilty_change(
849 items: Vec<ast::Item>,
850 is_clone_for_updated: bool,
853 Vec<(Option<ast::Visibility>, SyntaxNode)>,
854 Vec<(Option<ast::Visibility>, SyntaxNode)>,
857 let mut replacements = Vec::new();
858 let mut record_field_parents = Vec::new();
859 let mut impls = Vec::new();
860 let mut body_items = Vec::new();
862 items.into_iter().for_each(|item| {
864 if !is_clone_for_updated {
865 item = item.clone_for_update();
867 body_items.push(item.clone());
868 //Use stmts are ignored
870 ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())),
871 ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())),
872 ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())),
873 ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())),
874 //Associated item's visibility should not be changed
875 ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it),
876 ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())),
877 ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())),
878 ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())),
879 ast::Item::Struct(it) => {
880 replacements.push((it.visibility(), it.syntax().clone()));
881 record_field_parents.push((it.visibility(), it.syntax().clone()));
883 ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())),
884 ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())),
885 ast::Item::Union(it) => {
886 replacements.push((it.visibility(), it.syntax().clone()));
887 record_field_parents.push((it.visibility(), it.syntax().clone()));
893 return (body_items, replacements, record_field_parents, impls);
896 fn get_use_tree_paths_from_path(
898 use_tree_str: &mut Vec<ast::Path>,
899 ) -> Option<&mut Vec<ast::Path>> {
900 path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
901 if let Some(use_tree) = ast::UseTree::cast(x) {
902 if let Some(upper_tree_path) = use_tree.path() {
903 if upper_tree_path.to_string() != path.to_string() {
904 use_tree_str.push(upper_tree_path.clone());
905 get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
906 return Some(use_tree);
917 vis: Option<ast::Visibility>,
918 node_or_token_opt: Option<syntax::SyntaxElement>,
921 if let Some(node_or_token) = node_or_token_opt {
922 let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
923 if let Some(node) = node_or_token.as_node() {
924 ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
926 if let Some(token) = node_or_token.as_token() {
927 ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
935 fn compare_hir_and_ast_module(
936 ast_module: &ast::Module,
937 hir_module: hir::Module,
940 let hir_mod_name = hir_module.name(ctx.db())?;
941 let ast_mod_name = ast_module.name()?;
942 if hir_mod_name.to_string() != ast_mod_name.to_string() {
949 fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
950 let x = node.siblings_with_tokens(syntax::Direction::Prev).find(|x| {
951 return x.kind() == WHITESPACE;
954 return Some(x.text_range());
959 use crate::tests::{check_assist, check_assist_not_applicable};
964 fn test_not_applicable_without_selection() {
965 check_assist_not_applicable(
968 $0pub struct PublicStruct {
976 fn test_extract_module() {
980 mod thirdpartycrate {
983 pub struct SomeType2;
985 pub struct SomeType1;
989 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
991 pub struct PublicStruct {
992 field: PrivateStruct,
997 pub fn new() -> Self {
998 Self { field: PrivateStruct::new(), field1: SomeType1 }
1003 let _s = PrivateStruct::new();
1007 $0struct PrivateStruct {
1011 pub struct PrivateStruct1 {
1015 impl PrivateStruct {
1017 PrivateStruct { inner: SomeType }
1027 mod thirdpartycrate {
1029 pub struct SomeType;
1030 pub struct SomeType2;
1032 pub struct SomeType1;
1036 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
1038 pub struct PublicStruct {
1039 field: modname::PrivateStruct,
1044 pub fn new() -> Self {
1045 Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
1050 let _s = modname::PrivateStruct::new();
1051 let _a = modname::bar();
1055 use crate::thirdpartycrate::nest::SomeType;
1057 pub(crate) struct PrivateStruct {
1058 pub(crate) inner: SomeType,
1061 pub struct PrivateStruct1 {
1065 impl PrivateStruct {
1066 pub(crate) fn new() -> Self {
1067 PrivateStruct { inner: SomeType }
1071 pub(crate) fn bar() -> i32 {
1081 fn test_extract_module_for_function_only() {
1085 $0fn foo(name: i32) -> i32 {
1089 fn bar(name: i32) -> i32 {
1095 pub(crate) fn foo(name: i32) -> i32 {
1100 fn bar(name: i32) -> i32 {
1108 fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1116 pub fn new_a() -> i32 {
1122 let _a = A::new_a();
1129 pub(crate) struct A {}
1132 pub fn new_a() -> i32 {
1139 let _a = modname::A::new_a();
1147 fn test_import_resolve_when_its_only_inside_selection() {
1152 pub struct PrivateStruct;
1153 pub struct PrivateStruct1;
1157 use super::foo::{PrivateStruct, PrivateStruct1};
1160 field: PrivateStruct,
1164 field: PrivateStruct1,
1170 pub struct PrivateStruct;
1171 pub struct PrivateStruct1;
1175 use super::foo::{PrivateStruct1};
1178 use super::super::foo::PrivateStruct;
1180 pub(crate) struct Strukt {
1181 pub(crate) field: PrivateStruct,
1186 field: PrivateStruct1,
1194 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1199 pub struct PrivateStruct;
1203 use super::foo::PrivateStruct;
1206 field: PrivateStruct,
1210 field: PrivateStruct,
1216 pub struct PrivateStruct;
1220 use super::foo::PrivateStruct;
1223 use super::super::foo::PrivateStruct;
1225 pub(crate) struct Strukt {
1226 pub(crate) field: PrivateStruct,
1231 field: PrivateStruct,
1239 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1244 pub struct PrivateStruct;
1247 field: PrivateStruct,
1251 field: PrivateStruct,
1257 pub struct PrivateStruct;
1260 use super::PrivateStruct;
1262 pub(crate) struct Strukt {
1263 pub(crate) field: PrivateStruct,
1268 field: PrivateStruct,
1276 fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
1284 pub fn new_a() -> i32 {
1290 let _a = A::new_a();
1302 pub fn new_a() -> i32 {
1309 let _a = A::new_a();
1317 fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1329 pub fn new_a() -> i32 {
1335 let _a = A::new_a();
1347 use super::super::foo::A;
1350 pub fn new_a() -> i32 {
1357 let _a = A::new_a();
1365 fn test_import_resolve_for_trait_bounds_on_function() {
1374 fn foo<T: JustATrait>(arg: T) -> T {
1378 impl JustATrait for A {}
1391 use super::JustATrait;
1393 pub(crate) struct A {}
1395 pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1399 impl JustATrait for A {}
1401 pub(crate) fn bar() {
1412 fn test_extract_module_for_module() {
1425 pub(crate) mod impl_play {
1435 fn test_extract_module_with_multiple_files() {
1442 use foo::PrivateStruct;
1445 field: PrivateStruct,
1454 pub struct PrivateStruct;
1459 use foo::PrivateStruct;
1462 field: PrivateStruct,
1469 pub(crate) struct Strukt1 {
1470 pub(crate) field: Strukt,
1479 fn test_extract_module_macro_rules() {
1500 fn test_do_not_apply_visibility_modifier_to_trait_impl_items() {
1510 $0impl ATrait for A {
1535 fn test_if_inside_impl_block_generate_module_outside() {
1557 pub(crate) fn foo() {}
1565 fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child() {
1586 pub(crate) fn foo(x: B) {}