From 4d0e58afef1722d5f5bf5970bed44594c27ecf34 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 Feb 2019 13:51:22 +0300 Subject: rename rename to references --- crates/ra_ide_api/src/lib.rs | 4 +- crates/ra_ide_api/src/references.rs | 273 ++++++++++++++++++++++++++++++++++++ crates/ra_ide_api/src/rename.rs | 273 ------------------------------------ 3 files changed, 275 insertions(+), 275 deletions(-) create mode 100644 crates/ra_ide_api/src/references.rs delete mode 100644 crates/ra_ide_api/src/rename.rs (limited to 'crates') diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index bb60e8d40..0bb21245d 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -29,7 +29,7 @@ mod hover; mod call_info; mod syntax_highlighting; mod parent_module; -mod rename; +mod references; mod impls; mod assists; @@ -388,7 +388,7 @@ impl Analysis { position: FilePosition, new_name: &str, ) -> Cancelable> { - self.with_db(|db| rename::rename(db, position, new_name)) + self.with_db(|db| references::rename(db, position, new_name)) } fn with_db T + std::panic::UnwindSafe, T>( diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs new file mode 100644 index 000000000..1c9491a0a --- /dev/null +++ b/crates/ra_ide_api/src/references.rs @@ -0,0 +1,273 @@ +use relative_path::RelativePathBuf; + +use hir::{ + self, ModuleSource, source_binder::module_from_declaration, +}; +use ra_syntax::{ + algo::find_node_at_offset, + ast, + AstNode, + SyntaxNode +}; + +use crate::{ + db::RootDatabase, + FilePosition, + FileSystemEdit, + SourceChange, + SourceFileEdit, +}; +use ra_db::SourceDatabase; +use relative_path::RelativePath; + +pub(crate) fn rename( + db: &RootDatabase, + position: FilePosition, + new_name: &str, +) -> Option { + let source_file = db.parse(position.file_id); + let syntax = source_file.syntax(); + + if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { + rename_mod(db, ast_name, ast_module, position, new_name) + } else { + rename_reference(db, position, new_name) + } +} + +fn find_name_and_module_at_offset( + syntax: &SyntaxNode, + position: FilePosition, +) -> Option<(&ast::Name, &ast::Module)> { + let ast_name = find_node_at_offset::(syntax, position.offset); + let ast_name_parent = ast::Module::cast(ast_name?.syntax().parent()?); + + if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) { + return Some((name, ast_module)); + } + None +} + +fn rename_mod( + db: &RootDatabase, + ast_name: &ast::Name, + ast_module: &ast::Module, + position: FilePosition, + new_name: &str, +) -> Option { + let mut source_file_edits = Vec::new(); + let mut file_system_edits = Vec::new(); + if let Some(module) = module_from_declaration(db, position.file_id, &ast_module) { + let (file_id, module_source) = module.definition_source(db); + match module_source { + 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, + }; + file_system_edits.push(move_file); + } + } + ModuleSource::Module(..) => {} + } + } + + let edit = SourceFileEdit { + file_id: position.file_id, + edit: { + let mut builder = ra_text_edit::TextEditBuilder::default(); + builder.replace(ast_name.syntax().range(), new_name.into()); + builder.finish() + }, + }; + source_file_edits.push(edit); + + Some(SourceChange { + label: "rename".to_string(), + source_file_edits, + file_system_edits, + cursor_position: None, + }) +} + +fn rename_reference( + db: &RootDatabase, + position: FilePosition, + new_name: &str, +) -> Option { + let edit = db + .find_all_refs(position) + .iter() + .map(|(file_id, text_range)| SourceFileEdit { + file_id: *file_id, + edit: { + let mut builder = ra_text_edit::TextEditBuilder::default(); + builder.replace(*text_range, new_name.into()); + builder.finish() + }, + }) + .collect::>(); + if edit.is_empty() { + return None; + } + + Some(SourceChange { + label: "rename".to_string(), + source_file_edits: edit, + file_system_edits: Vec::new(), + cursor_position: None, + }) +} + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot_matches; + use test_utils::assert_eq_text; + use crate::{ + mock_analysis::single_file_with_position, + mock_analysis::analysis_and_position, + FileId +}; + + #[test] + fn test_rename_for_local() { + test_rename( + r#" + fn main() { + let mut i = 1; + let j = 1; + i = i<|> + j; + + { + i = 0; + } + + i = 5; + }"#, + "k", + r#" + fn main() { + let mut k = 1; + let j = 1; + k = k + j; + + { + k = 0; + } + + k = 5; + }"#, + ); + } + + #[test] + fn test_rename_for_param_inside() { + test_rename( + r#" + fn foo(i : u32) -> u32 { + i<|> + }"#, + "j", + r#" + fn foo(j : u32) -> u32 { + j + }"#, + ); + } + + #[test] + fn test_rename_refs_for_fn_param() { + test_rename( + r#" + fn foo(i<|> : u32) -> u32 { + i + }"#, + "new_name", + r#" + fn foo(new_name : u32) -> u32 { + new_name + }"#, + ); + } + + #[test] + fn test_rename_for_mut_param() { + test_rename( + r#" + fn foo(mut i<|> : u32) -> u32 { + i + }"#, + "new_name", + r#" + fn foo(mut new_name : u32) -> u32 { + new_name + }"#, + ); + } + + #[test] + fn test_rename_mod() { + let (analysis, position) = analysis_and_position( + " + //- /lib.rs + mod bar; + + //- /bar.rs + mod foo<|>; + + //- /bar/foo.rs + // emtpy + ", + ); + let new_name = "foo2"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert_debug_snapshot_matches!("rename_mod", &source_change); + } + + #[test] + fn test_rename_mod_in_dir() { + let (analysis, position) = analysis_and_position( + " + //- /lib.rs + mod fo<|>o; + //- /foo/mod.rs + // emtpy + ", + ); + let new_name = "foo2"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change); + } + + fn test_rename(text: &str, new_name: &str, expected: &str) { + let (analysis, position) = single_file_with_position(text); + let source_change = analysis.rename(position, new_name).unwrap(); + let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default(); + let mut file_id: Option = None; + if let Some(change) = source_change { + for edit in change.source_file_edits { + file_id = Some(edit.file_id); + for atom in edit.edit.as_atoms() { + text_edit_bulder.replace(atom.delete, atom.insert.clone()); + } + } + } + let result = text_edit_bulder + .finish() + .apply(&*analysis.file_text(file_id.unwrap())); + assert_eq_text!(expected, &*result); + } +} diff --git a/crates/ra_ide_api/src/rename.rs b/crates/ra_ide_api/src/rename.rs deleted file mode 100644 index 1c9491a0a..000000000 --- a/crates/ra_ide_api/src/rename.rs +++ /dev/null @@ -1,273 +0,0 @@ -use relative_path::RelativePathBuf; - -use hir::{ - self, ModuleSource, source_binder::module_from_declaration, -}; -use ra_syntax::{ - algo::find_node_at_offset, - ast, - AstNode, - SyntaxNode -}; - -use crate::{ - db::RootDatabase, - FilePosition, - FileSystemEdit, - SourceChange, - SourceFileEdit, -}; -use ra_db::SourceDatabase; -use relative_path::RelativePath; - -pub(crate) fn rename( - db: &RootDatabase, - position: FilePosition, - new_name: &str, -) -> Option { - let source_file = db.parse(position.file_id); - let syntax = source_file.syntax(); - - if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { - rename_mod(db, ast_name, ast_module, position, new_name) - } else { - rename_reference(db, position, new_name) - } -} - -fn find_name_and_module_at_offset( - syntax: &SyntaxNode, - position: FilePosition, -) -> Option<(&ast::Name, &ast::Module)> { - let ast_name = find_node_at_offset::(syntax, position.offset); - let ast_name_parent = ast::Module::cast(ast_name?.syntax().parent()?); - - if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) { - return Some((name, ast_module)); - } - None -} - -fn rename_mod( - db: &RootDatabase, - ast_name: &ast::Name, - ast_module: &ast::Module, - position: FilePosition, - new_name: &str, -) -> Option { - let mut source_file_edits = Vec::new(); - let mut file_system_edits = Vec::new(); - if let Some(module) = module_from_declaration(db, position.file_id, &ast_module) { - let (file_id, module_source) = module.definition_source(db); - match module_source { - 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, - }; - file_system_edits.push(move_file); - } - } - ModuleSource::Module(..) => {} - } - } - - let edit = SourceFileEdit { - file_id: position.file_id, - edit: { - let mut builder = ra_text_edit::TextEditBuilder::default(); - builder.replace(ast_name.syntax().range(), new_name.into()); - builder.finish() - }, - }; - source_file_edits.push(edit); - - Some(SourceChange { - label: "rename".to_string(), - source_file_edits, - file_system_edits, - cursor_position: None, - }) -} - -fn rename_reference( - db: &RootDatabase, - position: FilePosition, - new_name: &str, -) -> Option { - let edit = db - .find_all_refs(position) - .iter() - .map(|(file_id, text_range)| SourceFileEdit { - file_id: *file_id, - edit: { - let mut builder = ra_text_edit::TextEditBuilder::default(); - builder.replace(*text_range, new_name.into()); - builder.finish() - }, - }) - .collect::>(); - if edit.is_empty() { - return None; - } - - Some(SourceChange { - label: "rename".to_string(), - source_file_edits: edit, - file_system_edits: Vec::new(), - cursor_position: None, - }) -} - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot_matches; - use test_utils::assert_eq_text; - use crate::{ - mock_analysis::single_file_with_position, - mock_analysis::analysis_and_position, - FileId -}; - - #[test] - fn test_rename_for_local() { - test_rename( - r#" - fn main() { - let mut i = 1; - let j = 1; - i = i<|> + j; - - { - i = 0; - } - - i = 5; - }"#, - "k", - r#" - fn main() { - let mut k = 1; - let j = 1; - k = k + j; - - { - k = 0; - } - - k = 5; - }"#, - ); - } - - #[test] - fn test_rename_for_param_inside() { - test_rename( - r#" - fn foo(i : u32) -> u32 { - i<|> - }"#, - "j", - r#" - fn foo(j : u32) -> u32 { - j - }"#, - ); - } - - #[test] - fn test_rename_refs_for_fn_param() { - test_rename( - r#" - fn foo(i<|> : u32) -> u32 { - i - }"#, - "new_name", - r#" - fn foo(new_name : u32) -> u32 { - new_name - }"#, - ); - } - - #[test] - fn test_rename_for_mut_param() { - test_rename( - r#" - fn foo(mut i<|> : u32) -> u32 { - i - }"#, - "new_name", - r#" - fn foo(mut new_name : u32) -> u32 { - new_name - }"#, - ); - } - - #[test] - fn test_rename_mod() { - let (analysis, position) = analysis_and_position( - " - //- /lib.rs - mod bar; - - //- /bar.rs - mod foo<|>; - - //- /bar/foo.rs - // emtpy - ", - ); - let new_name = "foo2"; - let source_change = analysis.rename(position, new_name).unwrap(); - assert_debug_snapshot_matches!("rename_mod", &source_change); - } - - #[test] - fn test_rename_mod_in_dir() { - let (analysis, position) = analysis_and_position( - " - //- /lib.rs - mod fo<|>o; - //- /foo/mod.rs - // emtpy - ", - ); - let new_name = "foo2"; - let source_change = analysis.rename(position, new_name).unwrap(); - assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change); - } - - fn test_rename(text: &str, new_name: &str, expected: &str) { - let (analysis, position) = single_file_with_position(text); - let source_change = analysis.rename(position, new_name).unwrap(); - let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default(); - let mut file_id: Option = None; - if let Some(change) = source_change { - for edit in change.source_file_edits { - file_id = Some(edit.file_id); - for atom in edit.edit.as_atoms() { - text_edit_bulder.replace(atom.delete, atom.insert.clone()); - } - } - } - let result = text_edit_bulder - .finish() - .apply(&*analysis.file_text(file_id.unwrap())); - assert_eq_text!(expected, &*result); - } -} -- cgit v1.2.3