Skip to content

Commit 51b2c86

Browse files
committed
Fix renaming mod in use tree
1 parent 36353bb commit 51b2c86

File tree

1 file changed

+146
-55
lines changed

1 file changed

+146
-55
lines changed

crates/ra_ide/src/references/rename.rs

Lines changed: 146 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
//! FIXME: write short doc here
22
3-
use hir::{ModuleSource, Semantics};
3+
use hir::{Module, ModuleDef, ModuleSource, Semantics};
44
use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt};
5-
use ra_ide_db::RootDatabase;
5+
use ra_ide_db::{
6+
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
7+
RootDatabase,
8+
};
69
use ra_syntax::{
7-
algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind,
8-
AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
10+
algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
11+
lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
912
};
1013
use ra_text_edit::TextEdit;
1114
use std::convert::TryInto;
@@ -30,10 +33,8 @@ pub(crate) fn rename(
3033
let sema = Semantics::new(db);
3134
let source_file = sema.parse(position.file_id);
3235
let syntax = source_file.syntax();
33-
if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) {
34-
let range = ast_name.syntax().text_range();
35-
rename_mod(&sema, &ast_name, &ast_module, position, new_name)
36-
.map(|info| RangeInfo::new(range, info))
36+
if let Some(module) = find_module_at_offset(&sema, position, syntax) {
37+
rename_mod(db, position, module, new_name)
3738
} else if let Some(self_token) =
3839
syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
3940
{
@@ -43,13 +44,32 @@ pub(crate) fn rename(
4344
}
4445
}
4546

46-
fn find_name_and_module_at_offset(
47-
syntax: &SyntaxNode,
47+
fn find_module_at_offset(
48+
sema: &Semantics<RootDatabase>,
4849
position: FilePosition,
49-
) -> Option<(ast::Name, ast::Module)> {
50-
let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
51-
let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
52-
Some((ast_name, ast_module))
50+
syntax: &SyntaxNode,
51+
) -> Option<Module> {
52+
let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
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)
5373
}
5474

5575
fn 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
7797
}
7898

7999
fn rename_mod(
80-
sema: &Semantics<RootDatabase>,
81-
ast_name: &ast::Name,
82-
ast_module: &ast::Module,
100+
db: &RootDatabase,
83101
position: FilePosition,
102+
module: Module,
84103
new_name: &str,
85-
) -> Option<SourceChange> {
104+
) -> Option<RangeInfo<SourceChange>> {
86105
let mut source_file_edits = Vec::new();
87106
let mut file_system_edits = Vec::new();
88-
if let Some(module) = sema.to_def(ast_module) {
89-
let src = module.definition_source(sema.db);
90-
let file_id = src.file_id.original_file(sema.db);
91-
match src.value {
92-
ModuleSource::SourceFile(..) => {
93-
let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id);
94-
// mod is defined in path/to/dir/mod.rs
95-
let dst_path = if mod_path.file_stem() == Some("mod") {
96-
mod_path
97-
.parent()
98-
.and_then(|p| p.parent())
99-
.or_else(|| Some(RelativePath::new("")))
100-
.map(|p| p.join(new_name).join("mod.rs"))
101-
} else {
102-
Some(mod_path.with_file_name(new_name).with_extension("rs"))
107+
108+
let src = module.definition_source(db);
109+
let file_id = src.file_id.original_file(db);
110+
match src.value {
111+
ModuleSource::SourceFile(..) => {
112+
let mod_path: RelativePathBuf = db.file_relative_path(file_id);
113+
// mod is defined in path/to/dir/mod.rs
114+
let dst_path = if mod_path.file_stem() == Some("mod") {
115+
mod_path
116+
.parent()
117+
.and_then(|p| p.parent())
118+
.or_else(|| Some(RelativePath::new("")))
119+
.map(|p| p.join(new_name).join("mod.rs"))
120+
} else {
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,
103128
};
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-
}
129+
file_system_edits.push(move_file);
112130
}
113-
ModuleSource::Module(..) => {}
114131
}
132+
ModuleSource::Module(..) => {}
115133
}
116134

117-
let edit = SourceFileEdit {
118-
file_id: position.file_id,
119-
edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()),
120-
};
121-
source_file_edits.push(edit);
122-
123-
if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) {
124-
let ref_edits = refs
125-
.references
126-
.into_iter()
127-
.map(|reference| source_edit_from_reference(reference, new_name));
128-
source_file_edits.extend(ref_edits);
135+
if let Some(src) = module.declaration_source(db) {
136+
let file_id = src.file_id.original_file(db);
137+
let name = src.value.name()?;
138+
let edit = SourceFileEdit {
139+
file_id: file_id,
140+
edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
141+
};
142+
source_file_edits.push(edit);
129143
}
130144

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)))
132153
}
133154

134155
fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
@@ -675,6 +696,76 @@ mod tests {
675696
"###);
676697
}
677698

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+
678769
#[test]
679770
fn test_rename_mod_in_dir() {
680771
let (analysis, position) = analysis_and_position(

0 commit comments

Comments
 (0)