aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/references/rename.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/references/rename.rs')
-rw-r--r--crates/ra_ide/src/references/rename.rs202
1 files changed, 146 insertions, 56 deletions
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index c4f07f905..7ebc0adcf 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::{RelativePathBuf, SourceDatabaseExt}; 4use ra_db::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,49 +97,49 @@ 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 // mod is defined in path/to/dir/mod.rs
94 // mod is defined in path/to/dir/mod.rs 113 let dst = if module.is_mod_rs(db) {
95 let dst = if mod_path.file_stem() == Some("mod") { 114 format!("../{}/mod.rs", new_name)
96 format!("../{}/mod.rs", new_name) 115 } else {
97 } else { 116 format!("{}.rs", new_name)
98 format!("{}.rs", new_name) 117 };
99 }; 118 let move_file =
100 let move_file = 119 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
101 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst }; 120 file_system_edits.push(move_file);
102 file_system_edits.push(move_file);
103 }
104 ModuleSource::Module(..) => {}
105 } 121 }
122 ModuleSource::Module(..) => {}
106 } 123 }
107 124
108 let edit = SourceFileEdit { 125 if let Some(src) = module.declaration_source(db) {
109 file_id: position.file_id, 126 let file_id = src.file_id.original_file(db);
110 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 127 let name = src.value.name()?;
111 }; 128 let edit = SourceFileEdit {
112 source_file_edits.push(edit); 129 file_id: file_id,
113 130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
114 if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { 131 };
115 let ref_edits = refs 132 source_file_edits.push(edit);
116 .references
117 .into_iter()
118 .map(|reference| source_edit_from_reference(reference, new_name));
119 source_file_edits.extend(ref_edits);
120 } 133 }
121 134
122 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 135 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?;
136 let ref_edits = refs
137 .references
138 .into_iter()
139 .map(|reference| source_edit_from_reference(reference, new_name));
140 source_file_edits.extend(ref_edits);
141
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
123} 143}
124 144
125fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 145fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -251,11 +271,10 @@ fn rename_reference(
251mod tests { 271mod tests {
252 use insta::assert_debug_snapshot; 272 use insta::assert_debug_snapshot;
253 use ra_text_edit::TextEditBuilder; 273 use ra_text_edit::TextEditBuilder;
274 use stdx::trim_indent;
254 use test_utils::{assert_eq_text, mark}; 275 use test_utils::{assert_eq_text, mark};
255 276
256 use crate::{ 277 use crate::{mock_analysis::analysis_and_position, FileId};
257 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
258 };
259 278
260 #[test] 279 #[test]
261 fn test_rename_to_underscore() { 280 fn test_rename_to_underscore() {
@@ -289,7 +308,7 @@ mod tests {
289 308
290 #[test] 309 #[test]
291 fn test_rename_to_invalid_identifier() { 310 fn test_rename_to_invalid_identifier() {
292 let (analysis, position) = single_file_with_position( 311 let (analysis, position) = analysis_and_position(
293 " 312 "
294 fn main() { 313 fn main() {
295 let i<|> = 1; 314 let i<|> = 1;
@@ -667,6 +686,76 @@ mod foo<|>;
667 } 686 }
668 687
669 #[test] 688 #[test]
689 fn test_rename_mod_in_use_tree() {
690 let (analysis, position) = analysis_and_position(
691 r#"
692//- /main.rs
693pub mod foo;
694pub mod bar;
695fn main() {}
696
697//- /foo.rs
698pub struct FooContent;
699
700//- /bar.rs
701use crate::foo<|>::FooContent;
702 "#,
703 );
704 let new_name = "qux";
705 let source_change = analysis.rename(position, new_name).unwrap();
706 assert_debug_snapshot!(&source_change,
707@r###"
708 Some(
709 RangeInfo {
710 range: 11..14,
711 info: SourceChange {
712 source_file_edits: [
713 SourceFileEdit {
714 file_id: FileId(
715 1,
716 ),
717 edit: TextEdit {
718 indels: [
719 Indel {
720 insert: "qux",
721 delete: 8..11,
722 },
723 ],
724 },
725 },
726 SourceFileEdit {
727 file_id: FileId(
728 3,
729 ),
730 edit: TextEdit {
731 indels: [
732 Indel {
733 insert: "qux",
734 delete: 11..14,
735 },
736 ],
737 },
738 },
739 ],
740 file_system_edits: [
741 MoveFile {
742 src: FileId(
743 2,
744 ),
745 anchor: FileId(
746 3,
747 ),
748 dst: "qux.rs",
749 },
750 ],
751 is_snippet: false,
752 },
753 },
754 )
755 "###);
756 }
757
758 #[test]
670 fn test_rename_mod_in_dir() { 759 fn test_rename_mod_in_dir() {
671 let (analysis, position) = analysis_and_position( 760 let (analysis, position) = analysis_and_position(
672 r#" 761 r#"
@@ -963,8 +1052,9 @@ pub mod foo<|>;
963 ); 1052 );
964 } 1053 }
965 1054
966 fn test_rename(text: &str, new_name: &str, expected: &str) { 1055 fn test_rename(ra_fixture_before: &str, new_name: &str, ra_fixture_after: &str) {
967 let (analysis, position) = single_file_with_position(text); 1056 let ra_fixture_after = &trim_indent(ra_fixture_after);
1057 let (analysis, position) = analysis_and_position(ra_fixture_before);
968 let source_change = analysis.rename(position, new_name).unwrap(); 1058 let source_change = analysis.rename(position, new_name).unwrap();
969 let mut text_edit_builder = TextEditBuilder::default(); 1059 let mut text_edit_builder = TextEditBuilder::default();
970 let mut file_id: Option<FileId> = None; 1060 let mut file_id: Option<FileId> = None;
@@ -978,6 +1068,6 @@ pub mod foo<|>;
978 } 1068 }
979 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); 1069 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
980 text_edit_builder.finish().apply(&mut result); 1070 text_edit_builder.finish().apply(&mut result);
981 assert_eq_text!(expected, &*result); 1071 assert_eq_text!(ra_fixture_after, &*result);
982 } 1072 }
983} 1073}