1 use std::collections::{HashMap, HashSet};
3 use hir::{HasSource, ModuleDef, 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, SyntaxNode, TextRange,
21 use crate::{AssistContext, Assists};
23 use super::remove_unused_param::range_to_remove;
25 // Assist: extract_module
27 // Extracts a selected region as seperate module. All the references, visibility and imports are
32 // fn 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.frange.range.is_empty() {
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 let mut curr_parent_module: Option<ast::Module> = None;
65 if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
66 curr_parent_module = ast::Module::cast(mod_syn_opt);
69 let mut module = extract_target(&node, ctx.frange.range)?;
70 if module.body_items.len() == 0 {
74 let old_item_indent = module.body_items[0].indent_level();
76 //This takes place in three steps:
78 //- Firstly, we will update the references(usages) e.g. converting a
79 // function call bar() to modname::bar(), and similarly for other items
81 //- Secondly, changing the visibility of each item inside the newly selected module
82 // i.e. making a fn a() {} to pub(crate) fn a() {}
84 //- Thirdly, resolving all the imports this includes removing paths from imports
85 // outside the module, shifting/cloning them inside new module, or shifting the imports, or making
86 // new import statemnts
88 //We are getting item usages and record_fields together, record_fields
89 //for change_visibility and usages for first point mentioned above in the process
90 let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
92 let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, &ctx);
93 module.body_items = module.change_visibility(record_fields)?;
94 if module.body_items.len() == 0 {
99 AssistId("extract_module", AssistKind::RefactorExtract),
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);
112 let body = body_items.join("\n\n");
114 let mut module_def = String::new();
116 format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent);
118 let mut usages_to_be_updated_for_curr_file = vec![];
119 for usages_to_be_updated_for_file in usages_to_be_processed {
120 if usages_to_be_updated_for_file.0 == ctx.frange.file_id {
121 usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
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)
130 builder.edit_file(ctx.frange.file_id);
131 for usage_to_be_processed in usages_to_be_updated_for_curr_file {
132 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
135 for import_path_text_range in import_paths_to_be_removed {
136 builder.delete(import_path_text_range);
138 builder.replace(module.text_range, module_def)
145 text_range: TextRange,
147 body_items: Vec<ast::Item>,
150 fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
151 let body_items: Vec<ast::Item> = node
153 .filter_map(|child| {
154 if let Some(item) = ast::Item::cast(child.clone()) {
155 if selection_range.contains_range(item.syntax().text_range()) {
164 Some(Module { text_range: selection_range, name: "modname".to_string(), body_items })
168 fn get_usages_and_record_fields(
171 ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
172 let mut record_fields = Vec::new();
173 let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
175 //Here impl is not included as each item inside impl will be tied to the parent of
176 //implementing block(a struct, enum, etc), if the parent is in selected module, it will
177 //get updated by ADT section given below or if it is not, then we dont need to do any operation
178 self.body_items.clone().into_iter().for_each(|item| {
180 match (item.syntax()) {
182 if let Some( nod ) = ctx.sema.to_def(&it) {
183 let node_def = Definition::ModuleDef(nod.into());
184 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
186 let mut get_record_fields = |it: ast::Adt| {
187 for desc in it.syntax().descendants() {
188 if let Some(record_field) = ast::RecordField::cast(desc) {
189 record_fields.push(record_field.syntax().clone());
194 //Enum Fields are not allowed to explicitly specify pub, it is implied
196 ast::Adt::Struct(_) => get_record_fields(it),
197 ast::Adt::Union(_) => get_record_fields(it),
198 ast::Adt::Enum(_) => {},
202 ast::TypeAlias(it) => {
203 if let Some( nod ) = ctx.sema.to_def(&it) {
204 let node_def = Definition::ModuleDef(nod.into());
205 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
209 if let Some( nod ) = ctx.sema.to_def(&it) {
210 let node_def = Definition::ModuleDef(nod.into());
211 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
215 if let Some( nod ) = ctx.sema.to_def(&it) {
216 let node_def = Definition::ModuleDef(nod.into());
217 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
221 if let Some( nod ) = ctx.sema.to_def(&it) {
222 let node_def = Definition::ModuleDef(nod.into());
223 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
231 return (refs, record_fields);
234 fn expand_and_group_usages_file_wise(
237 node_def: Definition,
238 refs: &mut HashMap<FileId, Vec<(TextRange, String)>>,
240 for (file_id, references) in node_def.usages(&ctx.sema).all() {
241 if let Some(file_refs) = refs.get_mut(&file_id) {
242 let mut usages = self.expand_ref_to_usages(references, ctx, file_id);
243 file_refs.append(&mut usages);
245 refs.insert(file_id, self.expand_ref_to_usages(references, ctx, file_id));
250 fn expand_ref_to_usages(
252 refs: Vec<FileReference>,
255 ) -> Vec<(TextRange, String)> {
256 let source_file = ctx.sema.parse(file_id);
258 let mut usages_to_be_processed_for_file = Vec::new();
260 if let Some(x) = self.get_usage_to_be_processed(&source_file, usage) {
261 usages_to_be_processed_for_file.push(x);
265 usages_to_be_processed_for_file
268 fn get_usage_to_be_processed(
270 source_file: &SourceFile,
271 FileReference { range, name, .. }: FileReference,
272 ) -> Option<(TextRange, String)> {
273 let path: Option<ast::Path> = find_node_at_range(source_file.syntax(), range);
277 for desc in path.syntax().descendants() {
278 if desc.to_string() == name.syntax().to_string()
279 && !self.text_range.contains_range(desc.text_range())
281 if let Some(name_ref) = ast::NameRef::cast(desc) {
283 name_ref.syntax().text_range(),
284 format!("{}::{}", self.name, name_ref.to_string()),
293 fn change_visibility(&self, record_fields: Vec<SyntaxNode>) -> Option<Vec<ast::Item>> {
294 let (body_items, mut replacements, record_field_parents, impls) =
295 get_replacements_for_visibilty_change(self.body_items.clone(), false);
297 let impl_items = impls.into_iter().fold(Vec::new(), |mut impl_items, x| {
298 let this_impl_items =
299 x.syntax().descendants().fold(Vec::new(), |mut this_impl_items, x| {
300 if let Some(item) = ast::Item::cast(x.clone()) {
301 this_impl_items.push(item);
303 return this_impl_items;
306 impl_items.append(&mut this_impl_items.clone());
310 let (_, mut impl_item_replacements, _, _) =
311 get_replacements_for_visibilty_change(impl_items.clone(), true);
313 replacements.append(&mut impl_item_replacements);
315 record_field_parents.into_iter().for_each(|x| {
316 x.1.descendants().filter_map(|x| ast::RecordField::cast(x)).for_each(|desc| {
317 let is_record_field_present = record_fields
320 .find(|x| x.to_string() == desc.to_string())
322 if is_record_field_present {
323 replacements.push((desc.visibility().clone(), desc.syntax().clone()));
328 replacements.into_iter().for_each(|(vis, syntax)| {
329 add_change_vis(vis, syntax.first_child_or_token());
337 curr_parent_module: Option<ast::Module>,
339 ) -> Vec<TextRange> {
340 let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
341 let mut node_set: HashSet<String> = HashSet::new();
343 self.body_items.clone().into_iter().for_each(|item| {
344 item.syntax().descendants().for_each(|x| {
345 if let Some(name) = ast::Name::cast(x.clone()) {
346 if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
347 //Necessary to avoid two same names going through
348 if !node_set.contains(&name.syntax().to_string()) {
349 node_set.insert(name.syntax().to_string());
350 let def_opt: Option<Definition> = match name_classify {
351 NameClass::Definition(def) => Some(def),
355 if let Some(def) = def_opt {
356 if let Some(import_path) = self
357 .process_names_and_namerefs_for_import_resolve(
364 import_paths_to_be_removed.push(import_path);
371 if let Some(name_ref) = ast::NameRef::cast(x) {
372 if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
373 //Necessary to avoid two same names going through
374 if !node_set.contains(&name_ref.syntax().to_string()) {
375 node_set.insert(name_ref.syntax().to_string());
376 let def_opt: Option<Definition> = match name_classify {
377 NameRefClass::Definition(def) => Some(def),
381 if let Some(def) = def_opt {
382 if let Some(import_path) = self
383 .process_names_and_namerefs_for_import_resolve(
390 import_paths_to_be_removed.push(import_path);
399 import_paths_to_be_removed
402 fn process_names_and_namerefs_for_import_resolve(
405 node_syntax: &SyntaxNode,
406 curr_parent_module: &Option<ast::Module>,
408 ) -> Option<TextRange> {
409 //We only need to find in the current file
410 let selection_range = ctx.frange.range;
411 let search_scope = SearchScope::single_file(ctx.frange.file_id);
412 let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
413 let curr_file_id = ctx.frange.file_id;
414 let file = ctx.sema.parse(ctx.frange.file_id);
416 let mut exists_inside_sel = false;
417 let mut exists_outside_sel = false;
418 usage_res.clone().into_iter().for_each(|x| {
419 let mut non_use_nodes_itr = (&x.1).into_iter().filter_map(|x| {
420 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
421 let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
430 .find(|x| !selection_range.contains_range(x.syntax().text_range()))
433 exists_outside_sel = true;
436 .find(|x| selection_range.contains_range(x.syntax().text_range()))
439 exists_inside_sel = true;
443 let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
451 let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
453 let mut use_opt: Option<ast::Use> = None;
454 if file_id == ctx.frange.file_id {
455 (&x.1).into_iter().for_each(|x| {
456 let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
457 if let Some(node) = node_opt {
458 use_opt = Some(node.clone());
465 let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
466 //Exists inside and outside selection
467 // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
469 // - Use stmt for item is not present ->
470 //If it is not found, the definition is either ported inside new module or it stays
472 //- Def is inside: Nothing to import
473 //- Def is outside: Import it inside with super
475 //Exists inside selection but not outside -> Check for the import of it in original module,
476 //get the use_tree_str, reconstruct the use stmt in new module
478 let mut import_path_to_be_removed: Option<TextRange> = None;
479 if exists_inside_sel && exists_outside_sel {
480 //Changes to be made only inside new module
482 //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
483 //If not, insert a use stmt with super and the given nameref
484 if let Some((use_tree_str, _)) =
485 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
487 use_tree_str_opt = Some(use_tree_str);
488 } else if source_exists_outside_sel_in_same_mod {
489 //Considered only after use_stmt is not present
490 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
491 //true for all cases)
492 // false | false -> Do nothing
493 // false | true -> If source is in selection -> nothing to do, If source is outside
494 // mod -> ust_stmt transversal
495 // true | false -> super import insertion
496 // true | true -> super import insertion
497 self.make_use_stmt_of_node_with_super(node_syntax);
499 } else if exists_inside_sel && !exists_outside_sel {
500 //Changes to be made inside new module, and remove import from outside
502 if let Some((use_tree_str, text_range_opt)) =
503 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
505 if let Some(text_range) = text_range_opt {
506 import_path_to_be_removed = Some(text_range);
508 use_tree_str_opt = Some(use_tree_str);
509 } else if source_exists_outside_sel_in_same_mod {
510 self.make_use_stmt_of_node_with_super(node_syntax);
514 if let Some(use_tree_str) = use_tree_str_opt {
515 let mut use_tree_str = use_tree_str.clone();
516 use_tree_str.reverse();
517 if use_tree_str[0].to_string().contains("super") {
518 let super_path = make::ext::ident_path("super");
519 use_tree_str.insert(0, super_path)
523 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
524 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
525 self.body_items.insert(0, item);
529 import_path_to_be_removed
532 fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) {
533 let super_path = make::ext::ident_path("super");
534 let node_path = make::ext::ident_path(&node_syntax.to_string());
535 let use_ = make::use_(
537 make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
539 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
540 self.body_items.insert(0, item);
544 fn process_use_stmt_for_import_resolve(
546 use_stmt_opt: Option<ast::Use>,
547 node_syntax: &SyntaxNode,
548 ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
549 if let Some(use_stmt) = use_stmt_opt {
550 for desc in use_stmt.syntax().descendants() {
551 if let Some(path_seg) = ast::PathSegment::cast(desc) {
552 if path_seg.syntax().to_string() == node_syntax.to_string() {
553 let mut use_tree_str = vec![path_seg.parent_path()];
554 get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
555 for ancs in path_seg.syntax().ancestors() {
556 //Here we are looking for use_tree with same string value as node
557 //passed above as the range_to_remove function looks for a comma and
558 //then includes it in the text range to remove it. But the comma only
559 //appears at the use_tree level
560 if let Some(use_tree) = ast::UseTree::cast(ancs) {
561 if use_tree.syntax().to_string() == node_syntax.to_string() {
564 Some(range_to_remove(use_tree.syntax())),
570 return Some((use_tree_str, None));
580 fn does_source_exists_outside_sel_in_same_mod(
583 curr_parent_module: &Option<ast::Module>,
584 selection_range: TextRange,
585 curr_file_id: FileId,
587 let mut source_exists_outside_sel_in_same_mod = false;
589 Definition::ModuleDef(it) => match it {
590 ModuleDef::Module(x) => {
591 let source = x.definition_source(ctx.db());
592 let have_same_parent;
593 if let Some(ast_module) = &curr_parent_module {
594 if let Some(hir_module) = x.parent(ctx.db()) {
596 compare_hir_and_ast_module(&ast_module, hir_module, ctx).is_some();
598 let source_file_id = source.file_id.original_file(ctx.db());
599 have_same_parent = source_file_id == curr_file_id;
602 let source_file_id = source.file_id.original_file(ctx.db());
603 have_same_parent = source_file_id == curr_file_id;
606 if have_same_parent {
608 ModuleSource::Module(module_) => {
609 source_exists_outside_sel_in_same_mod =
610 !selection_range.contains_range(module_.syntax().text_range());
616 ModuleDef::Function(x) => {
617 if let Some(source) = x.source(ctx.db()) {
618 let have_same_parent;
619 if let Some(ast_module) = &curr_parent_module {
621 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
624 let source_file_id = source.file_id.original_file(ctx.db());
625 have_same_parent = source_file_id == curr_file_id;
628 if have_same_parent {
629 source_exists_outside_sel_in_same_mod =
630 !selection_range.contains_range(source.value.syntax().text_range());
634 ModuleDef::Adt(x) => {
635 if let Some(source) = x.source(ctx.db()) {
636 let have_same_parent;
637 if let Some(ast_module) = &curr_parent_module {
639 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
642 let source_file_id = source.file_id.original_file(ctx.db());
643 have_same_parent = source_file_id == curr_file_id;
646 if have_same_parent {
647 source_exists_outside_sel_in_same_mod =
648 !selection_range.contains_range(source.value.syntax().text_range());
652 ModuleDef::Variant(x) => {
653 if let Some(source) = x.source(ctx.db()) {
654 let have_same_parent;
655 if let Some(ast_module) = &curr_parent_module {
657 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
660 let source_file_id = source.file_id.original_file(ctx.db());
661 have_same_parent = source_file_id == curr_file_id;
664 if have_same_parent {
665 source_exists_outside_sel_in_same_mod =
666 !selection_range.contains_range(source.value.syntax().text_range());
670 ModuleDef::Const(x) => {
671 if let Some(source) = x.source(ctx.db()) {
672 let have_same_parent;
673 if let Some(ast_module) = &curr_parent_module {
675 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
678 let source_file_id = source.file_id.original_file(ctx.db());
679 have_same_parent = source_file_id == curr_file_id;
682 if have_same_parent {
683 source_exists_outside_sel_in_same_mod =
684 !selection_range.contains_range(source.value.syntax().text_range());
688 ModuleDef::Static(x) => {
689 if let Some(source) = x.source(ctx.db()) {
690 let have_same_parent;
691 if let Some(ast_module) = &curr_parent_module {
693 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
696 let source_file_id = source.file_id.original_file(ctx.db());
697 have_same_parent = source_file_id == curr_file_id;
700 if have_same_parent {
701 source_exists_outside_sel_in_same_mod =
702 !selection_range.contains_range(source.value.syntax().text_range());
706 ModuleDef::Trait(x) => {
707 if let Some(source) = x.source(ctx.db()) {
708 let have_same_parent;
709 if let Some(ast_module) = &curr_parent_module {
711 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
714 let source_file_id = source.file_id.original_file(ctx.db());
715 have_same_parent = source_file_id == curr_file_id;
718 if have_same_parent {
719 source_exists_outside_sel_in_same_mod =
720 !selection_range.contains_range(source.value.syntax().text_range());
724 ModuleDef::TypeAlias(x) => {
725 if let Some(source) = x.source(ctx.db()) {
726 let have_same_parent;
727 if let Some(ast_module) = &curr_parent_module {
729 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx)
732 let source_file_id = source.file_id.original_file(ctx.db());
733 have_same_parent = source_file_id == curr_file_id;
736 if have_same_parent {
737 source_exists_outside_sel_in_same_mod =
738 !selection_range.contains_range(source.value.syntax().text_range());
747 return source_exists_outside_sel_in_same_mod;
750 fn get_replacements_for_visibilty_change(
751 items: Vec<ast::Item>,
752 is_clone_for_updated: bool,
755 Vec<(Option<ast::Visibility>, SyntaxNode)>,
756 Vec<(Option<ast::Visibility>, SyntaxNode)>,
759 let mut replacements = Vec::new();
760 let mut record_field_parents = Vec::new();
761 let mut impls = Vec::new();
762 let mut body_items = Vec::new();
764 items.into_iter().for_each(|item| {
766 if !is_clone_for_updated {
767 item = item.clone_for_update();
769 body_items.push(item.clone());
770 //Use stmts are ignored
772 ast::Item::Const(it) => {
773 replacements.push((it.visibility().clone(), it.syntax().clone()))
775 ast::Item::Enum(it) => {
776 replacements.push((it.visibility().clone(), it.syntax().clone()))
778 ast::Item::ExternCrate(it) => {
779 replacements.push((it.visibility().clone(), it.syntax().clone()))
781 ast::Item::Fn(it) => replacements.push((it.visibility().clone(), it.syntax().clone())),
782 ast::Item::Impl(it) => impls.push(it),
783 ast::Item::MacroRules(it) => {
784 replacements.push((it.visibility().clone(), it.syntax().clone()))
786 ast::Item::MacroDef(it) => {
787 replacements.push((it.visibility().clone(), it.syntax().clone()))
789 ast::Item::Module(it) => {
790 replacements.push((it.visibility().clone(), it.syntax().clone()))
792 ast::Item::Static(it) => {
793 replacements.push((it.visibility().clone(), it.syntax().clone()))
795 ast::Item::Struct(it) => {
796 replacements.push((it.visibility().clone(), it.syntax().clone()));
797 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
799 ast::Item::Trait(it) => {
800 replacements.push((it.visibility().clone(), it.syntax().clone()))
802 ast::Item::TypeAlias(it) => {
803 replacements.push((it.visibility().clone(), it.syntax().clone()))
805 ast::Item::Union(it) => {
806 replacements.push((it.visibility().clone(), it.syntax().clone()));
807 record_field_parents.push((it.visibility().clone(), it.syntax().clone()));
813 return (body_items, replacements, record_field_parents, impls);
816 fn get_use_tree_paths_from_path(
818 use_tree_str: &mut Vec<ast::Path>,
819 ) -> Option<&mut Vec<ast::Path>> {
820 path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
821 if let Some(use_tree) = ast::UseTree::cast(x.clone()) {
822 if let Some(upper_tree_path) = use_tree.path() {
823 if upper_tree_path.to_string() != path.to_string() {
824 use_tree_str.push(upper_tree_path.clone());
825 get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
826 return Some(use_tree);
837 vis: Option<ast::Visibility>,
838 node_or_token_opt: Option<syntax::SyntaxElement>,
840 if let Some(vis) = vis {
841 if vis.syntax().text() == "pub" {
842 ted::replace(vis.syntax(), make::visibility_pub_crate().syntax().clone_for_update());
845 if let Some(node_or_token) = node_or_token_opt {
846 let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
847 if let Some(node) = node_or_token.as_node() {
848 ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
850 if let Some(token) = node_or_token.as_token() {
851 ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
859 fn compare_hir_and_ast_module(
860 ast_module: &ast::Module,
861 hir_module: hir::Module,
864 let hir_mod_name = hir_module.name(ctx.db())?;
865 let ast_mod_name = ast_module.name()?;
866 if hir_mod_name.to_string() != ast_mod_name.to_string() {
875 use crate::tests::{check_assist, check_assist_not_applicable};
880 fn test_not_applicable_without_selection() {
881 check_assist_not_applicable(
884 $0pub struct PublicStruct {
892 fn test_extract_module() {
896 mod thirdpartycrate {
899 pub struct SomeType2;
901 pub struct SomeType1;
905 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
907 pub struct PublicStruct {
908 field: PrivateStruct,
913 pub fn new() -> Self {
914 Self { field: PrivateStruct::new(), field1: SomeType1 }
919 let _s = PrivateStruct::new();
924 struct PrivateStruct {
928 pub struct PrivateStruct1 {
934 PrivateStruct { inner: SomeType }
945 mod thirdpartycrate {
948 pub struct SomeType2;
950 pub struct SomeType1;
954 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
956 pub struct PublicStruct {
957 field: modname::PrivateStruct,
962 pub fn new() -> Self {
963 Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
968 let _s = modname::PrivateStruct::new();
969 let _a = modname::bar();
973 use crate::thirdpartycrate::nest::SomeType;
975 pub(crate) struct PrivateStruct {
976 pub(crate) inner: SomeType,
979 pub(crate) struct PrivateStruct1 {
980 pub(crate) inner: i32,
984 pub(crate) fn new() -> Self {
985 PrivateStruct { inner: SomeType }
989 pub(crate) fn bar() -> i32 {
999 fn test_extract_module_for_function_only() {
1004 fn foo(name: i32) -> i32 {
1009 fn bar(name: i32) -> i32 {
1015 pub(crate) fn foo(name: i32) -> i32 {
1020 fn bar(name: i32) -> i32 {
1028 fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1037 pub fn new_a() -> i32 {
1044 let _a = A::new_a();
1051 pub(crate) struct A {}
1054 pub(crate) fn new_a() -> i32 {
1061 let _a = modname::A::new_a();
1069 fn test_import_resolve_when_its_only_inside_selection() {
1074 pub struct PrivateStruct;
1075 pub struct PrivateStruct1;
1079 use super::foo::{PrivateStruct, PrivateStruct1};
1083 field: PrivateStruct,
1088 field: PrivateStruct1,
1094 pub struct PrivateStruct;
1095 pub struct PrivateStruct1;
1099 use super::foo::{PrivateStruct1};
1102 use super::super::foo::PrivateStruct;
1104 pub(crate) struct Strukt {
1105 pub(crate) field: PrivateStruct,
1110 field: PrivateStruct1,
1118 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1123 pub struct PrivateStruct;
1127 use super::foo::PrivateStruct;
1131 field: PrivateStruct,
1136 field: PrivateStruct,
1142 pub struct PrivateStruct;
1146 use super::foo::PrivateStruct;
1149 use super::super::foo::PrivateStruct;
1151 pub(crate) struct Strukt {
1152 pub(crate) field: PrivateStruct,
1157 field: PrivateStruct,
1165 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1170 pub struct PrivateStruct;
1174 field: PrivateStruct,
1179 field: PrivateStruct,
1185 pub struct PrivateStruct;
1188 use super::PrivateStruct;
1190 pub(crate) struct Strukt {
1191 pub(crate) field: PrivateStruct,
1196 field: PrivateStruct,
1204 fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
1213 pub fn new_a() -> i32 {
1220 let _a = A::new_a();
1232 pub(crate) fn new_a() -> i32 {
1239 let _a = A::new_a();
1247 fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1260 pub fn new_a() -> i32 {
1267 let _a = A::new_a();
1279 use super::super::foo::A;
1282 pub(crate) fn new_a() -> i32 {
1289 let _a = A::new_a();
1297 fn test_import_resolve_for_trait_bounds_on_function() {
1307 fn foo<T: JustATrait>(arg: T) -> T {
1311 impl JustATrait for A {}
1325 use super::JustATrait;
1327 pub(crate) struct A {}
1329 pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1333 impl JustATrait for A {}
1335 pub(crate) fn bar() {
1346 fn test_extract_module_for_module() {
1361 pub(crate) mod impl_play {
1371 fn test_extract_module_with_multiple_files() {
1378 use foo::PrivateStruct;
1381 field: PrivateStruct,
1392 pub struct PrivateStruct;
1397 use foo::PrivateStruct;
1400 field: PrivateStruct,
1407 pub(crate) struct Strukt1 {
1408 pub(crate) field: Strukt,