aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/references
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/references')
-rw-r--r--crates/ra_ide/src/references/rename.rs266
1 files changed, 174 insertions, 92 deletions
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 915d4f4d3..99c2581b7 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,11 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::{RelativePathBuf, SourceDatabaseExt};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{
6 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
7 RootDatabase,
8};
6use ra_syntax::{ 9use ra_syntax::{
7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, 10 algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
8 AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 11 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
9}; 12};
10use ra_text_edit::TextEdit; 13use ra_text_edit::TextEdit;
11use std::convert::TryInto; 14use std::convert::TryInto;
@@ -30,10 +33,8 @@ pub(crate) fn rename(
30 let sema = Semantics::new(db); 33 let sema = Semantics::new(db);
31 let source_file = sema.parse(position.file_id); 34 let source_file = sema.parse(position.file_id);
32 let syntax = source_file.syntax(); 35 let syntax = source_file.syntax();
33 if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { 36 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
34 let range = ast_name.syntax().text_range(); 37 rename_mod(db, position, module, new_name)
35 rename_mod(&sema, &ast_name, &ast_module, position, new_name)
36 .map(|info| RangeInfo::new(range, info))
37 } else if let Some(self_token) = 38 } else if let Some(self_token) =
38 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 39 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
39 { 40 {
@@ -43,13 +44,32 @@ pub(crate) fn rename(
43 } 44 }
44} 45}
45 46
46fn find_name_and_module_at_offset( 47fn find_module_at_offset(
47 syntax: &SyntaxNode, 48 sema: &Semantics<RootDatabase>,
48 position: FilePosition, 49 position: FilePosition,
49) -> Option<(ast::Name, ast::Module)> { 50 syntax: &SyntaxNode,
50 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; 51) -> Option<Module> {
51 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; 52 let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
52 Some((ast_name, ast_module)) 53
54 let module = match_ast! {
55 match (ident.parent()) {
56 ast::NameRef(name_ref) => {
57 match classify_name_ref(sema, &name_ref)? {
58 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
59 _ => return None,
60 }
61 },
62 ast::Name(name) => {
63 match classify_name(&sema, &name)? {
64 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
65 _ => return None,
66 }
67 },
68 _ => return None,
69 }
70 };
71
72 Some(module)
53} 73}
54 74
55fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 75fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
@@ -77,58 +97,50 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
77} 97}
78 98
79fn rename_mod( 99fn rename_mod(
80 sema: &Semantics<RootDatabase>, 100 db: &RootDatabase,
81 ast_name: &ast::Name,
82 ast_module: &ast::Module,
83 position: FilePosition, 101 position: FilePosition,
102 module: Module,
84 new_name: &str, 103 new_name: &str,
85) -> Option<SourceChange> { 104) -> Option<RangeInfo<SourceChange>> {
86 let mut source_file_edits = Vec::new(); 105 let mut source_file_edits = Vec::new();
87 let mut file_system_edits = Vec::new(); 106 let mut file_system_edits = Vec::new();
88 if let Some(module) = sema.to_def(ast_module) { 107
89 let src = module.definition_source(sema.db); 108 let src = module.definition_source(db);
90 let file_id = src.file_id.original_file(sema.db); 109 let file_id = src.file_id.original_file(db);
91 match src.value { 110 match src.value {
92 ModuleSource::SourceFile(..) => { 111 ModuleSource::SourceFile(..) => {
93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); 112 let mod_path: RelativePathBuf = db.file_relative_path(file_id);
94 // mod is defined in path/to/dir/mod.rs 113 // mod is defined in path/to/dir/mod.rs
95 let dst_path = if mod_path.file_stem() == Some("mod") { 114 let dst = if mod_path.file_stem() == Some("mod") {
96 mod_path 115 format!("../{}/mod.rs", new_name)
97 .parent() 116 } else {
98 .and_then(|p| p.parent()) 117 format!("{}.rs", new_name)
99 .or_else(|| Some(RelativePath::new(""))) 118 };
100 .map(|p| p.join(new_name).join("mod.rs")) 119 let move_file =
101 } else { 120 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
102 Some(mod_path.with_file_name(new_name).with_extension("rs")) 121 file_system_edits.push(move_file);
103 };
104 if let Some(path) = dst_path {
105 let move_file = FileSystemEdit::MoveFile {
106 src: file_id,
107 dst_source_root: sema.db.file_source_root(position.file_id),
108 dst_path: path,
109 };
110 file_system_edits.push(move_file);
111 }
112 }
113 ModuleSource::Module(..) => {}
114 } 122 }
123 ModuleSource::Module(..) => {}
115 } 124 }
116 125
117 let edit = SourceFileEdit { 126 if let Some(src) = module.declaration_source(db) {
118 file_id: position.file_id, 127 let file_id = src.file_id.original_file(db);
119 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 128 let name = src.value.name()?;
120 }; 129 let edit = SourceFileEdit {
121 source_file_edits.push(edit); 130 file_id: file_id,
122 131 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
123 if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { 132 };
124 let ref_edits = refs 133 source_file_edits.push(edit);
125 .references
126 .into_iter()
127 .map(|reference| source_edit_from_reference(reference, new_name));
128 source_file_edits.extend(ref_edits);
129 } 134 }
130 135
131 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 136 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?;
137 let ref_edits = refs
138 .references
139 .into_iter()
140 .map(|reference| source_edit_from_reference(reference, new_name));
141 source_file_edits.extend(ref_edits);
142
143 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
132} 144}
133 145
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 146fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -623,16 +635,16 @@ mod tests {
623 #[test] 635 #[test]
624 fn test_rename_mod() { 636 fn test_rename_mod() {
625 let (analysis, position) = analysis_and_position( 637 let (analysis, position) = analysis_and_position(
626 " 638 r#"
627 //- /lib.rs 639//- /lib.rs
628 mod bar; 640mod bar;
629 641
630 //- /bar.rs 642//- /bar.rs
631 mod foo<|>; 643mod foo<|>;
632 644
633 //- /bar/foo.rs 645//- /bar/foo.rs
634 // emtpy 646// emtpy
635 ", 647 "#,
636 ); 648 );
637 let new_name = "foo2"; 649 let new_name = "foo2";
638 let source_change = analysis.rename(position, new_name).unwrap(); 650 let source_change = analysis.rename(position, new_name).unwrap();
@@ -662,10 +674,80 @@ mod tests {
662 src: FileId( 674 src: FileId(
663 3, 675 3,
664 ), 676 ),
665 dst_source_root: SourceRootId( 677 anchor: FileId(
666 0, 678 2,
667 ), 679 ),
668 dst_path: "bar/foo2.rs", 680 dst: "foo2.rs",
681 },
682 ],
683 is_snippet: false,
684 },
685 },
686 )
687 "###);
688 }
689
690 #[test]
691 fn test_rename_mod_in_use_tree() {
692 let (analysis, position) = analysis_and_position(
693 r#"
694//- /main.rs
695pub mod foo;
696pub mod bar;
697fn main() {}
698
699//- /foo.rs
700pub struct FooContent;
701
702//- /bar.rs
703use crate::foo<|>::FooContent;
704 "#,
705 );
706 let new_name = "qux";
707 let source_change = analysis.rename(position, new_name).unwrap();
708 assert_debug_snapshot!(&source_change,
709@r###"
710 Some(
711 RangeInfo {
712 range: 11..14,
713 info: SourceChange {
714 source_file_edits: [
715 SourceFileEdit {
716 file_id: FileId(
717 1,
718 ),
719 edit: TextEdit {
720 indels: [
721 Indel {
722 insert: "qux",
723 delete: 8..11,
724 },
725 ],
726 },
727 },
728 SourceFileEdit {
729 file_id: FileId(
730 3,
731 ),
732 edit: TextEdit {
733 indels: [
734 Indel {
735 insert: "qux",
736 delete: 11..14,
737 },
738 ],
739 },
740 },
741 ],
742 file_system_edits: [
743 MoveFile {
744 src: FileId(
745 2,
746 ),
747 anchor: FileId(
748 3,
749 ),
750 dst: "qux.rs",
669 }, 751 },
670 ], 752 ],
671 is_snippet: false, 753 is_snippet: false,
@@ -678,12 +760,12 @@ mod tests {
678 #[test] 760 #[test]
679 fn test_rename_mod_in_dir() { 761 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 762 let (analysis, position) = analysis_and_position(
681 " 763 r#"
682 //- /lib.rs 764//- /lib.rs
683 mod fo<|>o; 765mod fo<|>o;
684 //- /foo/mod.rs 766//- /foo/mod.rs
685 // emtpy 767// emtpy
686 ", 768 "#,
687 ); 769 );
688 let new_name = "foo2"; 770 let new_name = "foo2";
689 let source_change = analysis.rename(position, new_name).unwrap(); 771 let source_change = analysis.rename(position, new_name).unwrap();
@@ -713,10 +795,10 @@ mod tests {
713 src: FileId( 795 src: FileId(
714 2, 796 2,
715 ), 797 ),
716 dst_source_root: SourceRootId( 798 anchor: FileId(
717 0, 799 1,
718 ), 800 ),
719 dst_path: "foo2/mod.rs", 801 dst: "../foo2/mod.rs",
720 }, 802 },
721 ], 803 ],
722 is_snippet: false, 804 is_snippet: false,
@@ -753,19 +835,19 @@ mod tests {
753 #[test] 835 #[test]
754 fn test_rename_mod_filename_and_path() { 836 fn test_rename_mod_filename_and_path() {
755 let (analysis, position) = analysis_and_position( 837 let (analysis, position) = analysis_and_position(
756 " 838 r#"
757 //- /lib.rs 839//- /lib.rs
758 mod bar; 840mod bar;
759 fn f() { 841fn f() {
760 bar::foo::fun() 842 bar::foo::fun()
761 } 843}
762 844
763 //- /bar.rs 845//- /bar.rs
764 pub mod foo<|>; 846pub mod foo<|>;
765 847
766 //- /bar/foo.rs 848//- /bar/foo.rs
767 // pub fn fun() {} 849// pub fn fun() {}
768 ", 850 "#,
769 ); 851 );
770 let new_name = "foo2"; 852 let new_name = "foo2";
771 let source_change = analysis.rename(position, new_name).unwrap(); 853 let source_change = analysis.rename(position, new_name).unwrap();
@@ -808,10 +890,10 @@ mod tests {
808 src: FileId( 890 src: FileId(
809 3, 891 3,
810 ), 892 ),
811 dst_source_root: SourceRootId( 893 anchor: FileId(
812 0, 894 2,
813 ), 895 ),
814 dst_path: "bar/foo2.rs", 896 dst: "foo2.rs",
815 }, 897 },
816 ], 898 ],
817 is_snippet: false, 899 is_snippet: false,