//! 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;
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)
{
}
}
-fn find_name_and_module_at_offset(
- syntax: &SyntaxNode,
+fn find_module_at_offset(
+ sema: &Semantics<RootDatabase>,
position: FilePosition,
-) -> Option<(ast::Name, ast::Module)> {
- let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
- let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
- Some((ast_name, ast_module))
+ syntax: &SyntaxNode,
+) -> Option<Module> {
+ 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 {
}
fn rename_mod(
- sema: &Semantics<RootDatabase>,
- ast_name: &ast::Name,
- ast_module: &ast::Module,
+ db: &RootDatabase,
position: FilePosition,
+ module: Module,
new_name: &str,
-) -> Option<SourceChange> {
+) -> Option<RangeInfo<SourceChange>> {
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<RangeInfo<SourceChange>> {
"###);
}
+ #[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(