Fix renaming mod in use tree
This commit is contained in:
parent
1ce8c2b5a0
commit
ce29d172a5
@ -1,11 +1,14 @@
|
|||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir::{ModuleSource, Semantics};
|
use hir::{Module, ModuleDef, ModuleSource, Semantics};
|
||||||
use ra_db::{RelativePathBuf, SourceDatabaseExt};
|
use ra_db::{RelativePathBuf, SourceDatabaseExt};
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::{
|
||||||
|
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
|
||||||
|
RootDatabase,
|
||||||
|
};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind,
|
algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
|
||||||
AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
|
lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
@ -30,10 +33,8 @@ pub(crate) fn rename(
|
|||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let source_file = sema.parse(position.file_id);
|
let source_file = sema.parse(position.file_id);
|
||||||
let syntax = source_file.syntax();
|
let syntax = source_file.syntax();
|
||||||
if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) {
|
if let Some(module) = find_module_at_offset(&sema, position, syntax) {
|
||||||
let range = ast_name.syntax().text_range();
|
rename_mod(db, position, module, new_name)
|
||||||
rename_mod(&sema, &ast_name, &ast_module, position, new_name)
|
|
||||||
.map(|info| RangeInfo::new(range, info))
|
|
||||||
} else if let Some(self_token) =
|
} else if let Some(self_token) =
|
||||||
syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
|
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(
|
fn find_module_at_offset(
|
||||||
syntax: &SyntaxNode,
|
sema: &Semantics<RootDatabase>,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<(ast::Name, ast::Module)> {
|
syntax: &SyntaxNode,
|
||||||
let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
|
) -> Option<Module> {
|
||||||
let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
|
let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
|
||||||
Some((ast_name, ast_module))
|
|
||||||
|
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 source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
|
||||||
@ -77,49 +97,50 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rename_mod(
|
fn rename_mod(
|
||||||
sema: &Semantics<RootDatabase>,
|
db: &RootDatabase,
|
||||||
ast_name: &ast::Name,
|
|
||||||
ast_module: &ast::Module,
|
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
|
module: Module,
|
||||||
new_name: &str,
|
new_name: &str,
|
||||||
) -> Option<SourceChange> {
|
) -> Option<RangeInfo<SourceChange>> {
|
||||||
let mut source_file_edits = Vec::new();
|
let mut source_file_edits = Vec::new();
|
||||||
let mut file_system_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 src = module.definition_source(db);
|
||||||
let file_id = src.file_id.original_file(sema.db);
|
let file_id = src.file_id.original_file(db);
|
||||||
match src.value {
|
match src.value {
|
||||||
ModuleSource::SourceFile(..) => {
|
ModuleSource::SourceFile(..) => {
|
||||||
let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id);
|
let mod_path: RelativePathBuf = db.file_relative_path(file_id);
|
||||||
// mod is defined in path/to/dir/mod.rs
|
// mod is defined in path/to/dir/mod.rs
|
||||||
let dst = if mod_path.file_stem() == Some("mod") {
|
let dst = if mod_path.file_stem() == Some("mod") {
|
||||||
format!("../{}/mod.rs", new_name)
|
format!("../{}/mod.rs", new_name)
|
||||||
} else {
|
} else {
|
||||||
format!("{}.rs", new_name)
|
format!("{}.rs", new_name)
|
||||||
};
|
};
|
||||||
let move_file =
|
let move_file =
|
||||||
FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
|
FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
|
||||||
file_system_edits.push(move_file);
|
file_system_edits.push(move_file);
|
||||||
}
|
|
||||||
ModuleSource::Module(..) => {}
|
|
||||||
}
|
}
|
||||||
|
ModuleSource::Module(..) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let edit = SourceFileEdit {
|
if let Some(src) = module.declaration_source(db) {
|
||||||
file_id: position.file_id,
|
let file_id = src.file_id.original_file(db);
|
||||||
edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()),
|
let name = src.value.name()?;
|
||||||
};
|
let edit = SourceFileEdit {
|
||||||
source_file_edits.push(edit);
|
file_id: file_id,
|
||||||
|
edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
|
||||||
if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) {
|
};
|
||||||
let ref_edits = refs
|
source_file_edits.push(edit);
|
||||||
.references
|
|
||||||
.into_iter()
|
|
||||||
.map(|reference| source_edit_from_reference(reference, new_name));
|
|
||||||
source_file_edits.extend(ref_edits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>> {
|
fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> {
|
||||||
@ -666,6 +687,76 @@ fn test_rename_mod() {
|
|||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_mod_in_use_tree() {
|
||||||
|
let (analysis, position) = analysis_and_position(
|
||||||
|
r#"
|
||||||
|
//- /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,
|
||||||
|
),
|
||||||
|
anchor: FileId(
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
dst: "qux.rs",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
is_snippet: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rename_mod_in_dir() {
|
fn test_rename_mod_in_dir() {
|
||||||
let (analysis, position) = analysis_and_position(
|
let (analysis, position) = analysis_and_position(
|
||||||
|
Loading…
Reference in New Issue
Block a user