Skip to content

Commit

Permalink
feat: append as <name> when renaming inside an "UseTree".
Browse files Browse the repository at this point in the history
test: include `rename_path_inside_use_tree`.

Keeps tracks the progress of the changes. 3 other tests broke with the changes
of this.

feat: rename all other usages within the current file.

feat: fix most of the implementation problems.

test: `rename_path_inside_use_tree` tests a more complicated scenario.
  • Loading branch information
UserIsntAvailable authored and Veykril committed Feb 19, 2024
1 parent a016555 commit 50e4937
Showing 1 changed file with 106 additions and 14 deletions.
120 changes: 106 additions & 14 deletions crates/ide/src/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ide_db::{
base_db::{FileId, FileRange},
defs::{Definition, NameClass, NameRefClass},
rename::{bail, format_err, source_edit_from_references, IdentifierKind},
source_change::SourceChangeBuilder,
RootDatabase,
};
use itertools::Itertools;
Expand Down Expand Up @@ -91,24 +92,60 @@ pub(crate) fn rename(
let syntax = source_file.syntax();

let defs = find_definitions(&sema, syntax, position)?;
let alias_fallback = alias_fallback(syntax, position, new_name);

let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
Some(_) => defs
// FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
// properly find "direct" usages/references.
.map(|(.., def)| {
match IdentifierKind::classify(new_name)? {
IdentifierKind::Ident => (),
IdentifierKind::Lifetime => {
bail!("Cannot alias reference to a lifetime identifier")
}
IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
};

let ops: RenameResult<Vec<SourceChange>> = defs
.map(|(.., def)| {
if let Definition::Local(local) = def {
if let Some(self_param) = local.as_self_param(sema.db) {
cov_mark::hit!(rename_self_to_param);
return rename_self_to_param(&sema, local, self_param, new_name);
}
if new_name == "self" {
cov_mark::hit!(rename_to_self);
return rename_to_self(&sema, local);
let mut usages = def.usages(&sema).all();

// FIXME: hack - removes the usage that triggered this rename operation.
match usages.references.get_mut(&position.file_id).and_then(|refs| {
refs.iter()
.position(|ref_| ref_.range.contains_inclusive(position.offset))
.map(|idx| refs.remove(idx))
}) {
Some(_) => (),
None => never!(),
};

let mut source_change = SourceChange::default();
source_change.extend(usages.iter().map(|(&file_id, refs)| {
(file_id, source_edit_from_references(refs, def, new_name))
}));

Ok(source_change)
})
.collect(),
None => defs
.map(|(.., def)| {
if let Definition::Local(local) = def {
if let Some(self_param) = local.as_self_param(sema.db) {
cov_mark::hit!(rename_self_to_param);
return rename_self_to_param(&sema, local, self_param, new_name);
}
if new_name == "self" {
cov_mark::hit!(rename_to_self);
return rename_to_self(&sema, local);
}
}
}
def.rename(&sema, new_name, rename_external)
})
.collect();
def.rename(&sema, new_name, rename_external)
})
.collect(),
};

ops?.into_iter()
.chain(alias_fallback)
.reduce(|acc, elem| acc.merge(elem))
.ok_or_else(|| format_err!("No references found at position"))
}
Expand All @@ -131,6 +168,38 @@ pub(crate) fn will_rename_file(
Some(change)
}

// FIXME: Should support `extern crate`.
fn alias_fallback(
syntax: &SyntaxNode,
FilePosition { file_id, offset }: FilePosition,
new_name: &str,
) -> Option<SourceChange> {
let use_tree = syntax
.token_at_offset(offset)
.flat_map(|syntax| syntax.parent_ancestors())
.find_map(ast::UseTree::cast)?;

let last_path_segment = use_tree.path()?.segments().last()?.name_ref()?;
if !last_path_segment.syntax().text_range().contains_inclusive(offset) {
return None;
};

let mut builder = SourceChangeBuilder::new(file_id);

match use_tree.rename() {
Some(rename) => {
let offset = rename.syntax().text_range();
builder.replace(offset, format!("as {new_name}"));
}
None => {
let offset = use_tree.syntax().text_range().end();
builder.insert(offset, format!(" as {new_name}"));
}
}

Some(builder.finish())
}

fn find_definitions(
sema: &Semantics<'_, RootDatabase>,
syntax: &SyntaxNode,
Expand Down Expand Up @@ -2707,4 +2776,27 @@ fn test() {
"#,
);
}

#[test]
fn rename_path_inside_use_tree() {
check(
"Baz",
r#"
mod foo { pub struct Foo; }
mod bar { use super::Foo; }
use foo::Foo$0;
fn main() { let _: Foo; }
"#,
r#"
mod foo { pub struct Foo; }
mod bar { use super::Baz; }
use foo::Foo as Baz;
fn main() { let _: Baz; }
"#,
)
}
}

0 comments on commit 50e4937

Please sign in to comment.