aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorunexge <[email protected]>2020-06-11 22:40:59 +0100
committerunexge <[email protected]>2020-06-11 22:40:59 +0100
commit51b2c86b358a69b88bddf92cd1078500c233ba66 (patch)
treee15fbde01989534e448a15a2214e5b3a73021ccd /crates/ra_ide
parent36353bb1827dbd2efcde2d18c8598c4cc5e2e296 (diff)
Fix renaming mod in use tree
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/references/rename.rs201
1 files changed, 146 insertions, 55 deletions
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 915d4f4d3..546224b50 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::{RelativePath, 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,59 @@ 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_path = if mod_path.file_stem() == Some("mod") {
96 mod_path 115 mod_path
97 .parent() 116 .parent()
98 .and_then(|p| p.parent()) 117 .and_then(|p| p.parent())
99 .or_else(|| Some(RelativePath::new(""))) 118 .or_else(|| Some(RelativePath::new("")))
100 .map(|p| p.join(new_name).join("mod.rs")) 119 .map(|p| p.join(new_name).join("mod.rs"))
101 } else { 120 } else {
102 Some(mod_path.with_file_name(new_name).with_extension("rs")) 121 Some(mod_path.with_file_name(new_name).with_extension("rs"))
122 };
123 if let Some(path) = dst_path {
124 let move_file = FileSystemEdit::MoveFile {
125 src: file_id,
126 dst_source_root: db.file_source_root(position.file_id),
127 dst_path: path,
103 }; 128 };
104 if let Some(path) = dst_path { 129 file_system_edits.push(move_file);
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 } 130 }
113 ModuleSource::Module(..) => {}
114 } 131 }
132 ModuleSource::Module(..) => {}
115 } 133 }
116 134
117 let edit = SourceFileEdit { 135 if let Some(src) = module.declaration_source(db) {
118 file_id: position.file_id, 136 let file_id = src.file_id.original_file(db);
119 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 137 let name = src.value.name()?;
120 }; 138 let edit = SourceFileEdit {
121 source_file_edits.push(edit); 139 file_id: file_id,
122 140 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) { 141 };
124 let ref_edits = refs 142 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 } 143 }
130 144
131 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 145 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?;
146 let ref_edits = refs
147 .references
148 .into_iter()
149 .map(|reference| source_edit_from_reference(reference, new_name));
150 source_file_edits.extend(ref_edits);
151
152 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
132} 153}
133 154
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 155fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -676,6 +697,76 @@ mod tests {
676 } 697 }
677 698
678 #[test] 699 #[test]
700 fn test_rename_mod_in_use_tree() {
701 let (analysis, position) = analysis_and_position(
702 "
703 //- /main.rs
704 pub mod foo;
705 pub mod bar;
706 fn main() {}
707
708 //- /foo.rs
709 pub struct FooContent;
710
711 //- /bar.rs
712 use crate::foo<|>::FooContent;
713 ",
714 );
715 let new_name = "qux";
716 let source_change = analysis.rename(position, new_name).unwrap();
717 assert_debug_snapshot!(&source_change,
718@r###"
719 Some(
720 RangeInfo {
721 range: 11..14,
722 info: SourceChange {
723 source_file_edits: [
724 SourceFileEdit {
725 file_id: FileId(
726 1,
727 ),
728 edit: TextEdit {
729 indels: [
730 Indel {
731 insert: "qux",
732 delete: 8..11,
733 },
734 ],
735 },
736 },
737 SourceFileEdit {
738 file_id: FileId(
739 3,
740 ),
741 edit: TextEdit {
742 indels: [
743 Indel {
744 insert: "qux",
745 delete: 11..14,
746 },
747 ],
748 },
749 },
750 ],
751 file_system_edits: [
752 MoveFile {
753 src: FileId(
754 2,
755 ),
756 dst_source_root: SourceRootId(
757 0,
758 ),
759 dst_path: "qux.rs",
760 },
761 ],
762 is_snippet: false,
763 },
764 },
765 )
766 "###);
767 }
768
769 #[test]
679 fn test_rename_mod_in_dir() { 770 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 771 let (analysis, position) = analysis_and_position(
681 " 772 "