Skip to content

Commit 3f38619

Browse files
UserIsntAvailableVeykril
authored andcommitted
feat: append as <name> when renaming inside an "UseTree".
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.
1 parent 68c506f commit 3f38619

File tree

1 file changed

+106
-14
lines changed

1 file changed

+106
-14
lines changed

crates/ide/src/rename.rs

+106-14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use ide_db::{
99
base_db::{FileId, FileRange},
1010
defs::{Definition, NameClass, NameRefClass},
1111
rename::{bail, format_err, source_edit_from_references, IdentifierKind},
12+
source_change::SourceChangeBuilder,
1213
RootDatabase,
1314
};
1415
use itertools::Itertools;
@@ -90,24 +91,60 @@ pub(crate) fn rename(
9091
let syntax = source_file.syntax();
9192

9293
let defs = find_definitions(&sema, syntax, position)?;
94+
let alias_fallback = alias_fallback(syntax, position, new_name);
95+
96+
let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
97+
Some(_) => defs
98+
// FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
99+
// properly find "direct" usages/references.
100+
.map(|(.., def)| {
101+
match IdentifierKind::classify(new_name)? {
102+
IdentifierKind::Ident => (),
103+
IdentifierKind::Lifetime => {
104+
bail!("Cannot alias reference to a lifetime identifier")
105+
}
106+
IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
107+
};
93108

94-
let ops: RenameResult<Vec<SourceChange>> = defs
95-
.map(|(.., def)| {
96-
if let Definition::Local(local) = def {
97-
if let Some(self_param) = local.as_self_param(sema.db) {
98-
cov_mark::hit!(rename_self_to_param);
99-
return rename_self_to_param(&sema, local, self_param, new_name);
100-
}
101-
if new_name == "self" {
102-
cov_mark::hit!(rename_to_self);
103-
return rename_to_self(&sema, local);
109+
let mut usages = def.usages(&sema).all();
110+
111+
// FIXME: hack - removes the usage that triggered this rename operation.
112+
match usages.references.get_mut(&position.file_id).and_then(|refs| {
113+
refs.iter()
114+
.position(|ref_| ref_.range.contains_inclusive(position.offset))
115+
.map(|idx| refs.remove(idx))
116+
}) {
117+
Some(_) => (),
118+
None => never!(),
119+
};
120+
121+
let mut source_change = SourceChange::default();
122+
source_change.extend(usages.iter().map(|(&file_id, refs)| {
123+
(file_id, source_edit_from_references(refs, def, new_name))
124+
}));
125+
126+
Ok(source_change)
127+
})
128+
.collect(),
129+
None => defs
130+
.map(|(.., def)| {
131+
if let Definition::Local(local) = def {
132+
if let Some(self_param) = local.as_self_param(sema.db) {
133+
cov_mark::hit!(rename_self_to_param);
134+
return rename_self_to_param(&sema, local, self_param, new_name);
135+
}
136+
if new_name == "self" {
137+
cov_mark::hit!(rename_to_self);
138+
return rename_to_self(&sema, local);
139+
}
104140
}
105-
}
106-
def.rename(&sema, new_name)
107-
})
108-
.collect();
141+
def.rename(&sema, new_name, rename_external)
142+
})
143+
.collect(),
144+
};
109145

110146
ops?.into_iter()
147+
.chain(alias_fallback)
111148
.reduce(|acc, elem| acc.merge(elem))
112149
.ok_or_else(|| format_err!("No references found at position"))
113150
}
@@ -130,6 +167,38 @@ pub(crate) fn will_rename_file(
130167
Some(change)
131168
}
132169

170+
// FIXME: Should support `extern crate`.
171+
fn alias_fallback(
172+
syntax: &SyntaxNode,
173+
FilePosition { file_id, offset }: FilePosition,
174+
new_name: &str,
175+
) -> Option<SourceChange> {
176+
let use_tree = syntax
177+
.token_at_offset(offset)
178+
.flat_map(|syntax| syntax.parent_ancestors())
179+
.find_map(ast::UseTree::cast)?;
180+
181+
let last_path_segment = use_tree.path()?.segments().last()?.name_ref()?;
182+
if !last_path_segment.syntax().text_range().contains_inclusive(offset) {
183+
return None;
184+
};
185+
186+
let mut builder = SourceChangeBuilder::new(file_id);
187+
188+
match use_tree.rename() {
189+
Some(rename) => {
190+
let offset = rename.syntax().text_range();
191+
builder.replace(offset, format!("as {new_name}"));
192+
}
193+
None => {
194+
let offset = use_tree.syntax().text_range().end();
195+
builder.insert(offset, format!(" as {new_name}"));
196+
}
197+
}
198+
199+
Some(builder.finish())
200+
}
201+
133202
fn find_definitions(
134203
sema: &Semantics<'_, RootDatabase>,
135204
syntax: &SyntaxNode,
@@ -2686,4 +2755,27 @@ fn test() {
26862755
"#,
26872756
);
26882757
}
2758+
2759+
#[test]
2760+
fn rename_path_inside_use_tree() {
2761+
check(
2762+
"Baz",
2763+
r#"
2764+
mod foo { pub struct Foo; }
2765+
mod bar { use super::Foo; }
2766+
2767+
use foo::Foo$0;
2768+
2769+
fn main() { let _: Foo; }
2770+
"#,
2771+
r#"
2772+
mod foo { pub struct Foo; }
2773+
mod bar { use super::Baz; }
2774+
2775+
use foo::Foo as Baz;
2776+
2777+
fn main() { let _: Baz; }
2778+
"#,
2779+
)
2780+
}
26892781
}

0 commit comments

Comments
 (0)