aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/references/rename.rs187
1 files changed, 139 insertions, 48 deletions
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index c4f07f905..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::{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,49 +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 = if mod_path.file_stem() == Some("mod") { 114 let dst = if mod_path.file_stem() == Some("mod") {
96 format!("../{}/mod.rs", new_name) 115 format!("../{}/mod.rs", new_name)
97 } else { 116 } else {
98 format!("{}.rs", new_name) 117 format!("{}.rs", new_name)
99 }; 118 };
100 let move_file = 119 let move_file =
101 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst }; 120 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
102 file_system_edits.push(move_file); 121 file_system_edits.push(move_file);
103 }
104 ModuleSource::Module(..) => {}
105 } 122 }
123 ModuleSource::Module(..) => {}
106 } 124 }
107 125
108 let edit = SourceFileEdit { 126 if let Some(src) = module.declaration_source(db) {
109 file_id: position.file_id, 127 let file_id = src.file_id.original_file(db);
110 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 128 let name = src.value.name()?;
111 }; 129 let edit = SourceFileEdit {
112 source_file_edits.push(edit); 130 file_id: file_id,
113 131 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) { 132 };
115 let ref_edits = refs 133 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 } 134 }
121 135
122 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)))
123} 144}
124 145
125fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 146fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -667,6 +688,76 @@ mod foo<|>;
667 } 688 }
668 689
669 #[test] 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",
751 },
752 ],
753 is_snippet: false,
754 },
755 },
756 )
757 "###);
758 }
759
760 #[test]
670 fn test_rename_mod_in_dir() { 761 fn test_rename_mod_in_dir() {
671 let (analysis, position) = analysis_and_position( 762 let (analysis, position) = analysis_and_position(
672 r#" 763 r#"