From 51b2c86b358a69b88bddf92cd1078500c233ba66 Mon Sep 17 00:00:00 2001 From: unexge Date: Fri, 12 Jun 2020 00:40:59 +0300 Subject: Fix renaming mod in use tree --- crates/ra_ide/src/references/rename.rs | 201 ++++++++++++++++++++++++--------- 1 file changed, 146 insertions(+), 55 deletions(-) (limited to 'crates/ra_ide/src/references') 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 @@ //! FIXME: write short doc here -use hir::{ModuleSource, Semantics}; +use hir::{Module, ModuleDef, ModuleSource, Semantics}; use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; -use ra_ide_db::RootDatabase; +use ra_ide_db::{ + defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, + RootDatabase, +}; use ra_syntax::{ - algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, - AstNode, SyntaxKind, SyntaxNode, SyntaxToken, + algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner, + lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, }; use ra_text_edit::TextEdit; use std::convert::TryInto; @@ -30,10 +33,8 @@ pub(crate) fn rename( let sema = Semantics::new(db); let source_file = sema.parse(position.file_id); let syntax = source_file.syntax(); - if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { - let range = ast_name.syntax().text_range(); - rename_mod(&sema, &ast_name, &ast_module, position, new_name) - .map(|info| RangeInfo::new(range, info)) + if let Some(module) = find_module_at_offset(&sema, position, syntax) { + rename_mod(db, position, module, new_name) } else if let Some(self_token) = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) { @@ -43,13 +44,32 @@ pub(crate) fn rename( } } -fn find_name_and_module_at_offset( - syntax: &SyntaxNode, +fn find_module_at_offset( + sema: &Semantics, position: FilePosition, -) -> Option<(ast::Name, ast::Module)> { - let ast_name = find_node_at_offset::(syntax, position.offset)?; - let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; - Some((ast_name, ast_module)) + syntax: &SyntaxNode, +) -> Option { + let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?; + + let module = match_ast! { + match (ident.parent()) { + ast::NameRef(name_ref) => { + match classify_name_ref(sema, &name_ref)? { + NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, + _ => return None, + } + }, + ast::Name(name) => { + match classify_name(&sema, &name)? { + NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module, + _ => return None, + } + }, + _ => return None, + } + }; + + Some(module) } 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 } fn rename_mod( - sema: &Semantics, - ast_name: &ast::Name, - ast_module: &ast::Module, + db: &RootDatabase, position: FilePosition, + module: Module, new_name: &str, -) -> Option { +) -> Option> { let mut source_file_edits = Vec::new(); let mut file_system_edits = Vec::new(); - if let Some(module) = sema.to_def(ast_module) { - let src = module.definition_source(sema.db); - let file_id = src.file_id.original_file(sema.db); - match src.value { - ModuleSource::SourceFile(..) => { - let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); - // mod is defined in path/to/dir/mod.rs - let dst_path = if mod_path.file_stem() == Some("mod") { - mod_path - .parent() - .and_then(|p| p.parent()) - .or_else(|| Some(RelativePath::new(""))) - .map(|p| p.join(new_name).join("mod.rs")) - } else { - Some(mod_path.with_file_name(new_name).with_extension("rs")) + + let src = module.definition_source(db); + let file_id = src.file_id.original_file(db); + match src.value { + ModuleSource::SourceFile(..) => { + let mod_path: RelativePathBuf = db.file_relative_path(file_id); + // mod is defined in path/to/dir/mod.rs + let dst_path = if mod_path.file_stem() == Some("mod") { + mod_path + .parent() + .and_then(|p| p.parent()) + .or_else(|| Some(RelativePath::new(""))) + .map(|p| p.join(new_name).join("mod.rs")) + } else { + Some(mod_path.with_file_name(new_name).with_extension("rs")) + }; + if let Some(path) = dst_path { + let move_file = FileSystemEdit::MoveFile { + src: file_id, + dst_source_root: db.file_source_root(position.file_id), + dst_path: path, }; - if let Some(path) = dst_path { - let move_file = FileSystemEdit::MoveFile { - src: file_id, - dst_source_root: sema.db.file_source_root(position.file_id), - dst_path: path, - }; - file_system_edits.push(move_file); - } + file_system_edits.push(move_file); } - ModuleSource::Module(..) => {} } + ModuleSource::Module(..) => {} } - let edit = SourceFileEdit { - file_id: position.file_id, - edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), - }; - source_file_edits.push(edit); - - if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { - let ref_edits = refs - .references - .into_iter() - .map(|reference| source_edit_from_reference(reference, new_name)); - source_file_edits.extend(ref_edits); + if let Some(src) = module.declaration_source(db) { + let file_id = src.file_id.original_file(db); + let name = src.value.name()?; + let edit = SourceFileEdit { + file_id: file_id, + edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), + }; + source_file_edits.push(edit); } - Some(SourceChange::from_edits(source_file_edits, file_system_edits)) + let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; + let ref_edits = refs + .references + .into_iter() + .map(|reference| source_edit_from_reference(reference, new_name)); + source_file_edits.extend(ref_edits); + + Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) } fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option> { @@ -675,6 +696,76 @@ mod tests { "###); } + #[test] + fn test_rename_mod_in_use_tree() { + let (analysis, position) = analysis_and_position( + " + //- /main.rs + pub mod foo; + pub mod bar; + fn main() {} + + //- /foo.rs + pub struct FooContent; + + //- /bar.rs + use crate::foo<|>::FooContent; + ", + ); + let new_name = "qux"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert_debug_snapshot!(&source_change, +@r###" + Some( + RangeInfo { + range: 11..14, + info: SourceChange { + source_file_edits: [ + SourceFileEdit { + file_id: FileId( + 1, + ), + edit: TextEdit { + indels: [ + Indel { + insert: "qux", + delete: 8..11, + }, + ], + }, + }, + SourceFileEdit { + file_id: FileId( + 3, + ), + edit: TextEdit { + indels: [ + Indel { + insert: "qux", + delete: 11..14, + }, + ], + }, + }, + ], + file_system_edits: [ + MoveFile { + src: FileId( + 2, + ), + dst_source_root: SourceRootId( + 0, + ), + dst_path: "qux.rs", + }, + ], + is_snippet: false, + }, + }, + ) + "###); + } + #[test] fn test_rename_mod_in_dir() { let (analysis, position) = analysis_and_position( -- cgit v1.2.3