Auto merge of #12387 - 00nktk:fix-mod-rename, r=Veykril
fix(ide-db): correct single-file module rename Fixes a bug where rust-analyzer would emit `WorkspaceEdit`s with paths to dirs instead of files for the following project layout. lib.rs ```rust mod foo; ``` foo.rs ```rust mod bar { struct Bar; } ``` Also fixes emitted paths for modules with mod.rs. The bug resulted in panic in helix editor when attempting to rename a module.
This commit is contained in:
commit
bd0c2344f2
@ -39,6 +39,11 @@ pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_inline(self, db: &dyn HirDatabase) -> bool {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].origin.is_inline()
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
|
||||
|
@ -180,15 +180,33 @@ fn rename_mod(
|
||||
let InFile { file_id, value: def_source } = module.definition_source(sema.db);
|
||||
if let ModuleSource::SourceFile(..) = def_source {
|
||||
let anchor = file_id.original_file(sema.db);
|
||||
// not mod.rs and doesn't has children, rename file only
|
||||
if !module.is_mod_rs(sema.db) && module.children(sema.db).next().is_none() {
|
||||
|
||||
let is_mod_rs = module.is_mod_rs(sema.db);
|
||||
let has_detached_child = module.children(sema.db).any(|child| !child.is_inline(sema.db));
|
||||
|
||||
// Module exists in a named file
|
||||
if !is_mod_rs {
|
||||
let path = format!("{}.rs", new_name);
|
||||
let dst = AnchoredPathBuf { anchor, path };
|
||||
source_change.push_file_system_edit(FileSystemEdit::MoveFile { src: anchor, dst })
|
||||
} else if let Some(mod_name) = module.name(sema.db) {
|
||||
// is mod.rs or has children, rename dir
|
||||
let src = AnchoredPathBuf { anchor, path: mod_name.to_string() };
|
||||
let dst = AnchoredPathBuf { anchor, path: new_name.to_string() };
|
||||
}
|
||||
|
||||
// Rename the dir if:
|
||||
// - Module source is in mod.rs
|
||||
// - Module has submodules defined in separate files
|
||||
let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
|
||||
// Go up one level since the anchor is inside the dir we're trying to rename
|
||||
(true, _, Some(mod_name)) => {
|
||||
Some((format!("../{}", mod_name), format!("../{}", new_name)))
|
||||
}
|
||||
// The anchor is on the same level as target dir
|
||||
(false, true, Some(mod_name)) => Some((mod_name.to_string(), new_name.to_string())),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((src, dst)) = dir_paths {
|
||||
let src = AnchoredPathBuf { anchor, path: src };
|
||||
let dst = AnchoredPathBuf { anchor, path: dst };
|
||||
source_change.push_file_system_edit(FileSystemEdit::MoveDir {
|
||||
src,
|
||||
src_id: anchor,
|
||||
|
@ -973,7 +973,7 @@ fn test_rename_mod_in_dir() {
|
||||
anchor: FileId(
|
||||
1,
|
||||
),
|
||||
path: "foo",
|
||||
path: "../foo",
|
||||
},
|
||||
src_id: FileId(
|
||||
1,
|
||||
@ -982,7 +982,7 @@ fn test_rename_mod_in_dir() {
|
||||
anchor: FileId(
|
||||
1,
|
||||
),
|
||||
path: "foo2",
|
||||
path: "../foo2",
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -1158,6 +1158,17 @@ fn test_rename_mod_recursive() {
|
||||
},
|
||||
},
|
||||
file_system_edits: [
|
||||
MoveFile {
|
||||
src: FileId(
|
||||
1,
|
||||
),
|
||||
dst: AnchoredPathBuf {
|
||||
anchor: FileId(
|
||||
1,
|
||||
),
|
||||
path: "foo2.rs",
|
||||
},
|
||||
},
|
||||
MoveDir {
|
||||
src: AnchoredPathBuf {
|
||||
anchor: FileId(
|
||||
|
Loading…
Reference in New Issue
Block a user