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, 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
31 // $0fn foo(name: i32) -> i32 {
35 // fn bar(name: i32) -> i32 {
42 // pub(crate) fn foo(name: i32) -> i32 {
47 // fn bar(name: i32) -> i32 {
51 pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
52 if ctx.has_empty_selection() {
56 let node = ctx.covering_element();
57 let node = match node {
58 syntax::NodeOrToken::Node(n) => n,
59 syntax::NodeOrToken::Token(t) => t.parent()?,
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);
67 let mut module = extract_target(&node, ctx.selection_trimmed())?;
68 if module.body_items.len() == 0 {
72 let old_item_indent = module.body_items[0].indent_level();
74 //This takes place in three steps:
76 //- Firstly, we will update the references(usages) e.g. converting a
77 // function call bar() to modname::bar(), and similarly for other items
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() {}
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
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);
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 {
97 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.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.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 mut body_items: Vec<ast::Item> = node
153 .filter_map(|child| {
154 if let Some(item) = ast::Item::cast(child) {
155 if selection_range.contains_range(item.syntax().text_range()) {
164 if let Some(node_item) = ast::Item::cast(node.clone()) {
165 body_items.push(node_item);
168 Some(Module { text_range: selection_range, name: "modname".to_string(), body_items })
172 fn get_usages_and_record_fields(
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();
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.iter().cloned().for_each(|item| {
184 match (item.syntax()) {
186 if let Some( nod ) = ctx.sema.to_def(&it) {
187 let node_def = Definition::Adt(nod.into());
188 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
190 //Enum Fields are not allowed to explicitly specify pub, it is implied
192 ast::Adt::Struct(x) => {
193 if let Some(field_list) = x.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());
200 ast::FieldList::TupleFieldList(tuple_field_list) => {
201 tuple_field_list.fields().for_each(|tuple_field| {
202 adt_fields.push(tuple_field.syntax().clone());
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());
215 ast::Adt::Enum(_) => {},
219 ast::TypeAlias(it) => {
220 if let Some( nod ) = ctx.sema.to_def(&it) {
221 let node_def = Definition::TypeAlias(nod.into());
222 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
226 if let Some( nod ) = ctx.sema.to_def(&it) {
227 let node_def = Definition::Const(nod.into());
228 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
232 if let Some( nod ) = ctx.sema.to_def(&it) {
233 let node_def = Definition::Static(nod.into());
234 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
238 if let Some( nod ) = ctx.sema.to_def(&it) {
239 let node_def = Definition::Function(nod.into());
240 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
244 if let Some(nod) = ctx.sema.to_def(&it) {
245 self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs);
253 return (refs, adt_fields);
256 fn expand_and_group_usages_file_wise(
259 node_def: Definition,
260 refs: &mut HashMap<FileId, Vec<(TextRange, String)>>,
262 for (file_id, references) in node_def.usages(&ctx.sema).all() {
263 if let Some(file_refs) = refs.get_mut(&file_id) {
264 let mut usages = self.expand_ref_to_usages(references, ctx, file_id);
265 file_refs.append(&mut usages);
267 refs.insert(file_id, self.expand_ref_to_usages(references, ctx, file_id));
272 fn expand_ref_to_usages(
274 refs: Vec<FileReference>,
277 ) -> Vec<(TextRange, String)> {
278 let source_file = ctx.sema.parse(file_id);
280 let mut usages_to_be_processed_for_file = Vec::new();
282 if let Some(x) = self.get_usage_to_be_processed(&source_file, usage) {
283 usages_to_be_processed_for_file.push(x);
287 usages_to_be_processed_for_file
290 fn get_usage_to_be_processed(
292 source_file: &SourceFile,
293 FileReference { range, name, .. }: FileReference,
294 ) -> Option<(TextRange, String)> {
295 let path: Option<ast::Path> = find_node_at_range(source_file.syntax(), range);
299 for desc in path.syntax().descendants() {
300 if desc.to_string() == name.syntax().to_string()
301 && !self.text_range.contains_range(desc.text_range())
303 if let Some(name_ref) = ast::NameRef::cast(desc) {
305 name_ref.syntax().text_range(),
306 format!("{}::{}", self.name, name_ref),
315 fn change_visibility(&self, record_fields: Vec<SyntaxNode>) -> Option<Vec<ast::Item>> {
316 let (body_items, mut replacements, record_field_parents, impls) =
317 get_replacements_for_visibilty_change(self.body_items.clone(), false);
319 let mut impl_items = Vec::new();
321 let mut this_impl_items = Vec::new();
322 for node in impl_.syntax().descendants() {
323 if let Some(item) = ast::Item::cast(node) {
324 this_impl_items.push(item);
328 impl_items.append(&mut this_impl_items);
331 let (_, mut impl_item_replacements, _, _) =
332 get_replacements_for_visibilty_change(impl_items, true);
334 replacements.append(&mut impl_item_replacements);
336 record_field_parents.into_iter().for_each(|x| {
337 x.1.descendants().filter_map(|x| ast::RecordField::cast(x)).for_each(|desc| {
338 let is_record_field_present = record_fields
341 .find(|x| x.to_string() == desc.to_string())
343 if is_record_field_present {
344 replacements.push((desc.visibility(), desc.syntax().clone()));
349 replacements.into_iter().for_each(|(vis, syntax)| {
350 add_change_vis(vis, syntax.first_child_or_token());
358 curr_parent_module: Option<ast::Module>,
360 ) -> Vec<TextRange> {
361 let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
362 let mut node_set: HashSet<String> = HashSet::new();
364 self.body_items.clone().into_iter().for_each(|item| {
365 item.syntax().descendants().for_each(|x| {
366 if let Some(name) = ast::Name::cast(x.clone()) {
367 if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
368 //Necessary to avoid two same names going through
369 if !node_set.contains(&name.syntax().to_string()) {
370 node_set.insert(name.syntax().to_string());
371 let def_opt: Option<Definition> = match name_classify {
372 NameClass::Definition(def) => Some(def),
376 if let Some(def) = def_opt {
377 if let Some(import_path) = self
378 .process_names_and_namerefs_for_import_resolve(
385 import_paths_to_be_removed.push(import_path);
392 if let Some(name_ref) = ast::NameRef::cast(x) {
393 if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
394 //Necessary to avoid two same names going through
395 if !node_set.contains(&name_ref.syntax().to_string()) {
396 node_set.insert(name_ref.syntax().to_string());
397 let def_opt: Option<Definition> = match name_classify {
398 NameRefClass::Definition(def) => Some(def),
402 if let Some(def) = def_opt {
403 if let Some(import_path) = self
404 .process_names_and_namerefs_for_import_resolve(
411 import_paths_to_be_removed.push(import_path);
420 import_paths_to_be_removed
423 fn process_names_and_namerefs_for_import_resolve(
426 node_syntax: &SyntaxNode,
427 curr_parent_module: &Option<ast::Module>,
429 ) -> Option<TextRange> {
430 //We only need to find in the current file
431 let selection_range = ctx.selection_trimmed();
432 let curr_file_id = ctx.file_id();
433 let search_scope = SearchScope::single_file(curr_file_id);
434 let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
435 let file = ctx.sema.parse(curr_file_id);
437 let mut exists_inside_sel = false;
438 let mut exists_outside_sel = false;
439 usage_res.clone().into_iter().for_each(|x| {
440 let mut non_use_nodes_itr = (&x.1).into_iter().filter_map(|x| {
441 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
442 let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
451 .find(|x| !selection_range.contains_range(x.syntax().text_range()))
454 exists_outside_sel = true;
457 .find(|x| selection_range.contains_range(x.syntax().text_range()))
460 exists_inside_sel = true;
464 let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
472 let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|x| {
474 let mut use_opt: Option<ast::Use> = None;
475 if file_id == curr_file_id {
476 (&x.1).into_iter().for_each(|x| {
477 let node_opt: Option<ast::Use> = find_node_at_range(file.syntax(), x.range);
478 if let Some(node) = node_opt {
479 use_opt = Some(node);
486 let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
487 //Exists inside and outside selection
488 // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
490 // - Use stmt for item is not present ->
491 //If it is not found, the definition is either ported inside new module or it stays
493 //- Def is inside: Nothing to import
494 //- Def is outside: Import it inside with super
496 //Exists inside selection but not outside -> Check for the import of it in original module,
497 //get the use_tree_str, reconstruct the use stmt in new module
499 let mut import_path_to_be_removed: Option<TextRange> = None;
500 if exists_inside_sel && exists_outside_sel {
501 //Changes to be made only inside new module
503 //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
504 //If not, insert a use stmt with super and the given nameref
505 if let Some((use_tree_str, _)) =
506 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
508 use_tree_str_opt = Some(use_tree_str);
509 } else if source_exists_outside_sel_in_same_mod {
510 //Considered only after use_stmt is not present
511 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
512 //true for all cases)
513 // false | false -> Do nothing
514 // false | true -> If source is in selection -> nothing to do, If source is outside
515 // mod -> ust_stmt transversal
516 // true | false -> super import insertion
517 // true | true -> super import insertion
518 self.make_use_stmt_of_node_with_super(node_syntax);
520 } else if exists_inside_sel && !exists_outside_sel {
521 //Changes to be made inside new module, and remove import from outside
523 if let Some((use_tree_str, text_range_opt)) =
524 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
526 if let Some(text_range) = text_range_opt {
527 import_path_to_be_removed = Some(text_range);
529 use_tree_str_opt = Some(use_tree_str);
530 } else if source_exists_outside_sel_in_same_mod {
531 self.make_use_stmt_of_node_with_super(node_syntax);
535 if let Some(use_tree_str) = use_tree_str_opt {
536 let mut use_tree_str = use_tree_str;
537 use_tree_str.reverse();
538 if use_tree_str[0].to_string().contains("super") {
539 let super_path = make::ext::ident_path("super");
540 use_tree_str.insert(0, super_path)
544 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
545 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
546 self.body_items.insert(0, item);
550 import_path_to_be_removed
553 fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) {
554 let super_path = make::ext::ident_path("super");
555 let node_path = make::ext::ident_path(&node_syntax.to_string());
556 let use_ = make::use_(
558 make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
560 if let Some(item) = ast::Item::cast(use_.syntax().clone()) {
561 self.body_items.insert(0, item);
565 fn process_use_stmt_for_import_resolve(
567 use_stmt_opt: Option<ast::Use>,
568 node_syntax: &SyntaxNode,
569 ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
570 if let Some(use_stmt) = use_stmt_opt {
571 for desc in use_stmt.syntax().descendants() {
572 if let Some(path_seg) = ast::PathSegment::cast(desc) {
573 if path_seg.syntax().to_string() == node_syntax.to_string() {
574 let mut use_tree_str = vec![path_seg.parent_path()];
575 get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
576 for ancs in path_seg.syntax().ancestors() {
577 //Here we are looking for use_tree with same string value as node
578 //passed above as the range_to_remove function looks for a comma and
579 //then includes it in the text range to remove it. But the comma only
580 //appears at the use_tree level
581 if let Some(use_tree) = ast::UseTree::cast(ancs) {
582 if use_tree.syntax().to_string() == node_syntax.to_string() {
585 Some(range_to_remove(use_tree.syntax())),
591 return Some((use_tree_str, None));
601 fn does_source_exists_outside_sel_in_same_mod(
604 curr_parent_module: &Option<ast::Module>,
605 selection_range: TextRange,
606 curr_file_id: FileId,
608 let mut source_exists_outside_sel_in_same_mod = false;
610 Definition::Module(x) => {
611 let source = x.definition_source(ctx.db());
612 let have_same_parent;
613 if let Some(ast_module) = &curr_parent_module {
614 if let Some(hir_module) = x.parent(ctx.db()) {
616 compare_hir_and_ast_module(&ast_module, hir_module, ctx).is_some();
618 let source_file_id = source.file_id.original_file(ctx.db());
619 have_same_parent = source_file_id == curr_file_id;
622 let source_file_id = source.file_id.original_file(ctx.db());
623 have_same_parent = source_file_id == curr_file_id;
626 if have_same_parent {
628 ModuleSource::Module(module_) => {
629 source_exists_outside_sel_in_same_mod =
630 !selection_range.contains_range(module_.syntax().text_range());
636 Definition::Function(x) => {
637 if let Some(source) = x.source(ctx.db()) {
638 let have_same_parent;
639 if let Some(ast_module) = &curr_parent_module {
641 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
643 let source_file_id = source.file_id.original_file(ctx.db());
644 have_same_parent = source_file_id == curr_file_id;
647 if have_same_parent {
648 source_exists_outside_sel_in_same_mod =
649 !selection_range.contains_range(source.value.syntax().text_range());
653 Definition::Adt(x) => {
654 if let Some(source) = x.source(ctx.db()) {
655 let have_same_parent;
656 if let Some(ast_module) = &curr_parent_module {
658 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
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 Definition::Variant(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).is_some();
677 let source_file_id = source.file_id.original_file(ctx.db());
678 have_same_parent = source_file_id == curr_file_id;
681 if have_same_parent {
682 source_exists_outside_sel_in_same_mod =
683 !selection_range.contains_range(source.value.syntax().text_range());
687 Definition::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 {
692 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
694 let source_file_id = source.file_id.original_file(ctx.db());
695 have_same_parent = source_file_id == curr_file_id;
698 if have_same_parent {
699 source_exists_outside_sel_in_same_mod =
700 !selection_range.contains_range(source.value.syntax().text_range());
704 Definition::Static(x) => {
705 if let Some(source) = x.source(ctx.db()) {
706 let have_same_parent;
707 if let Some(ast_module) = &curr_parent_module {
709 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
711 let source_file_id = source.file_id.original_file(ctx.db());
712 have_same_parent = source_file_id == curr_file_id;
715 if have_same_parent {
716 source_exists_outside_sel_in_same_mod =
717 !selection_range.contains_range(source.value.syntax().text_range());
721 Definition::Trait(x) => {
722 if let Some(source) = x.source(ctx.db()) {
723 let have_same_parent;
724 if let Some(ast_module) = &curr_parent_module {
726 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
728 let source_file_id = source.file_id.original_file(ctx.db());
729 have_same_parent = source_file_id == curr_file_id;
732 if have_same_parent {
733 source_exists_outside_sel_in_same_mod =
734 !selection_range.contains_range(source.value.syntax().text_range());
738 Definition::TypeAlias(x) => {
739 if let Some(source) = x.source(ctx.db()) {
740 let have_same_parent;
741 if let Some(ast_module) = &curr_parent_module {
743 compare_hir_and_ast_module(&ast_module, x.module(ctx.db()), ctx).is_some();
745 let source_file_id = source.file_id.original_file(ctx.db());
746 have_same_parent = source_file_id == curr_file_id;
749 if have_same_parent {
750 source_exists_outside_sel_in_same_mod =
751 !selection_range.contains_range(source.value.syntax().text_range());
758 return source_exists_outside_sel_in_same_mod;
761 fn get_replacements_for_visibilty_change(
762 items: Vec<ast::Item>,
763 is_clone_for_updated: bool,
766 Vec<(Option<ast::Visibility>, SyntaxNode)>,
767 Vec<(Option<ast::Visibility>, SyntaxNode)>,
770 let mut replacements = Vec::new();
771 let mut record_field_parents = Vec::new();
772 let mut impls = Vec::new();
773 let mut body_items = Vec::new();
775 items.into_iter().for_each(|item| {
777 if !is_clone_for_updated {
778 item = item.clone_for_update();
780 body_items.push(item.clone());
781 //Use stmts are ignored
783 ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())),
784 ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())),
785 ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())),
786 ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())),
787 //Associated item's visibility should not be changed
788 ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it),
789 ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())),
790 ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())),
791 ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())),
792 ast::Item::Struct(it) => {
793 replacements.push((it.visibility(), it.syntax().clone()));
794 record_field_parents.push((it.visibility(), it.syntax().clone()));
796 ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())),
797 ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())),
798 ast::Item::Union(it) => {
799 replacements.push((it.visibility(), it.syntax().clone()));
800 record_field_parents.push((it.visibility(), it.syntax().clone()));
806 return (body_items, replacements, record_field_parents, impls);
809 fn get_use_tree_paths_from_path(
811 use_tree_str: &mut Vec<ast::Path>,
812 ) -> Option<&mut Vec<ast::Path>> {
813 path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
814 if let Some(use_tree) = ast::UseTree::cast(x) {
815 if let Some(upper_tree_path) = use_tree.path() {
816 if upper_tree_path.to_string() != path.to_string() {
817 use_tree_str.push(upper_tree_path.clone());
818 get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
819 return Some(use_tree);
830 vis: Option<ast::Visibility>,
831 node_or_token_opt: Option<syntax::SyntaxElement>,
834 if let Some(node_or_token) = node_or_token_opt {
835 let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
836 if let Some(node) = node_or_token.as_node() {
837 ted::insert(ted::Position::before(node), pub_crate_vis.syntax());
839 if let Some(token) = node_or_token.as_token() {
840 ted::insert(ted::Position::before(token), pub_crate_vis.syntax());
848 fn compare_hir_and_ast_module(
849 ast_module: &ast::Module,
850 hir_module: hir::Module,
853 let hir_mod_name = hir_module.name(ctx.db())?;
854 let ast_mod_name = ast_module.name()?;
855 if hir_mod_name.to_string() != ast_mod_name.to_string() {
864 use crate::tests::{check_assist, check_assist_not_applicable};
869 fn test_not_applicable_without_selection() {
870 check_assist_not_applicable(
873 $0pub struct PublicStruct {
881 fn test_extract_module() {
885 mod thirdpartycrate {
888 pub struct SomeType2;
890 pub struct SomeType1;
894 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
896 pub struct PublicStruct {
897 field: PrivateStruct,
902 pub fn new() -> Self {
903 Self { field: PrivateStruct::new(), field1: SomeType1 }
908 let _s = PrivateStruct::new();
912 $0struct PrivateStruct {
916 pub struct PrivateStruct1 {
922 PrivateStruct { inner: SomeType }
932 mod thirdpartycrate {
935 pub struct SomeType2;
937 pub struct SomeType1;
941 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
943 pub struct PublicStruct {
944 field: modname::PrivateStruct,
949 pub fn new() -> Self {
950 Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
955 let _s = modname::PrivateStruct::new();
956 let _a = modname::bar();
960 use crate::thirdpartycrate::nest::SomeType;
962 pub(crate) struct PrivateStruct {
963 pub(crate) inner: SomeType,
966 pub struct PrivateStruct1 {
971 pub(crate) fn new() -> Self {
972 PrivateStruct { inner: SomeType }
976 pub(crate) fn bar() -> i32 {
986 fn test_extract_module_for_function_only() {
990 $0fn foo(name: i32) -> i32 {
994 fn bar(name: i32) -> i32 {
1000 pub(crate) fn foo(name: i32) -> i32 {
1005 fn bar(name: i32) -> i32 {
1013 fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1021 pub fn new_a() -> i32 {
1027 let _a = A::new_a();
1034 pub(crate) struct A {}
1037 pub fn new_a() -> i32 {
1044 let _a = modname::A::new_a();
1052 fn test_import_resolve_when_its_only_inside_selection() {
1057 pub struct PrivateStruct;
1058 pub struct PrivateStruct1;
1062 use super::foo::{PrivateStruct, PrivateStruct1};
1065 field: PrivateStruct,
1069 field: PrivateStruct1,
1075 pub struct PrivateStruct;
1076 pub struct PrivateStruct1;
1080 use super::foo::{PrivateStruct1};
1083 use super::super::foo::PrivateStruct;
1085 pub(crate) struct Strukt {
1086 pub(crate) field: PrivateStruct,
1091 field: PrivateStruct1,
1099 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1104 pub struct PrivateStruct;
1108 use super::foo::PrivateStruct;
1111 field: PrivateStruct,
1115 field: PrivateStruct,
1121 pub struct PrivateStruct;
1125 use super::foo::PrivateStruct;
1128 use super::super::foo::PrivateStruct;
1130 pub(crate) struct Strukt {
1131 pub(crate) field: PrivateStruct,
1136 field: PrivateStruct,
1144 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1149 pub struct PrivateStruct;
1152 field: PrivateStruct,
1156 field: PrivateStruct,
1162 pub struct PrivateStruct;
1165 use super::PrivateStruct;
1167 pub(crate) struct Strukt {
1168 pub(crate) field: PrivateStruct,
1173 field: PrivateStruct,
1181 fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
1189 pub fn new_a() -> i32 {
1195 let _a = A::new_a();
1207 pub fn new_a() -> i32 {
1214 let _a = A::new_a();
1222 fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1234 pub fn new_a() -> i32 {
1240 let _a = A::new_a();
1252 use super::super::foo::A;
1255 pub fn new_a() -> i32 {
1262 let _a = A::new_a();
1270 fn test_import_resolve_for_trait_bounds_on_function() {
1279 fn foo<T: JustATrait>(arg: T) -> T {
1283 impl JustATrait for A {}
1296 use super::JustATrait;
1298 pub(crate) struct A {}
1300 pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1304 impl JustATrait for A {}
1306 pub(crate) fn bar() {
1317 fn test_extract_module_for_module() {
1330 pub(crate) mod impl_play {
1340 fn test_extract_module_with_multiple_files() {
1347 use foo::PrivateStruct;
1350 field: PrivateStruct,
1359 pub struct PrivateStruct;
1364 use foo::PrivateStruct;
1367 field: PrivateStruct,
1374 pub(crate) struct Strukt1 {
1375 pub(crate) field: Strukt,
1384 fn test_extract_module_macro_rules() {
1405 fn test_do_not_apply_visibility_modifier_to_trait_impl_items() {
1415 $0impl ATrait for A {