]> git.lizzy.rs Git - rust.git/commitdiff
Fix renaming mod in use tree
authorunexge <unexge@gmail.com>
Thu, 11 Jun 2020 21:40:59 +0000 (00:40 +0300)
committerunexge <unexge@gmail.com>
Thu, 11 Jun 2020 21:40:59 +0000 (00:40 +0300)
crates/ra_ide/src/references/rename.rs

index 915d4f4d3b56da932cd6393ce63e3336f1019413..546224b50fa1a88b136e7be16b7065574b3d6e0f 100644 (file)
@@ -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<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 {
@@ -77,58 +97,59 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
 }
 
 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>> {
@@ -675,6 +696,76 @@ fn test_rename_mod() {
         "###);
     }
 
+    #[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(