Skip to content

Commit 6044bb4

Browse files
committed
Improve assist qualified to top when on first segment
Example --- ```rust mod std { pub mod fmt { pub trait Debug {} } } fn main() { $0std::fmt::Debug; let x: std::fmt::Debug = std::fmt::Debug; } ``` **Before this PR** ```rust use std::fmt; mod std { pub mod fmt { pub trait Debug {} } } fn main() { fmt::Debug; let x: fmt::Debug = fmt::Debug; } ``` **After this PR** ```rust use std::fmt::Debug; mod std { pub mod fmt { pub trait Debug {} } } fn main() { Debug; let x: Debug = Debug; } ```
1 parent 4bf516e commit 6044bb4

File tree

1 file changed

+110
-14
lines changed

1 file changed

+110
-14
lines changed

crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::AsAssocItem;
1+
use hir::{AsAssocItem, ModuleDef, PathResolution};
22
use ide_db::{
33
helpers::mod_path_to_ast,
44
imports::insert_use::{ImportScope, insert_use},
@@ -30,26 +30,19 @@ pub(crate) fn replace_qualified_name_with_use(
3030
acc: &mut Assists,
3131
ctx: &AssistContext<'_>,
3232
) -> Option<()> {
33-
let mut original_path: ast::Path = ctx.find_node_at_offset()?;
33+
let original_path: ast::Path = ctx.find_node_at_offset()?;
3434
// We don't want to mess with use statements
3535
if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() {
3636
cov_mark::hit!(not_applicable_in_use);
3737
return None;
3838
}
3939

40-
if original_path.qualifier().is_none() {
41-
original_path = original_path.parent_path()?;
42-
}
40+
let original_path = target_path(ctx, original_path)?;
4341

44-
// only offer replacement for non assoc items
45-
match ctx.sema.resolve_path(&original_path)? {
46-
hir::PathResolution::Def(def) if def.as_assoc_item(ctx.sema.db).is_none() => (),
47-
_ => return None,
48-
}
4942
// then search for an import for the first path segment of what we want to replace
5043
// that way it is less likely that we import the item from a different location due re-exports
5144
let module = match ctx.sema.resolve_path(&original_path.first_qualifier_or_self())? {
52-
hir::PathResolution::Def(module @ hir::ModuleDef::Module(_)) => module,
45+
PathResolution::Def(module @ ModuleDef::Module(_)) => module,
5346
_ => return None,
5447
};
5548

@@ -97,6 +90,25 @@ pub(crate) fn replace_qualified_name_with_use(
9790
)
9891
}
9992

93+
fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option<ast::Path> {
94+
if original_path.qualifier().is_none() {
95+
original_path = original_path.top_path();
96+
97+
if let Some(PathResolution::Def(ModuleDef::Variant(_))) =
98+
ctx.sema.resolve_path(&original_path)
99+
{
100+
original_path = original_path.qualifier()?
101+
}
102+
} else if let PathResolution::Def(def) = ctx.sema.resolve_path(&original_path)?
103+
&& def.as_assoc_item(ctx.sema.db).is_some()
104+
{
105+
// Do not offer replacement for assoc items
106+
return None;
107+
}
108+
109+
Some(original_path)
110+
}
111+
100112
fn drop_generic_args(path: &ast::Path) -> ast::Path {
101113
let path = path.clone_for_update();
102114
if let Some(segment) = path.segment()
@@ -270,12 +282,96 @@ fn main() {
270282
}
271283
",
272284
r"
273-
use std::fmt;
285+
use std::fmt::Debug;
274286
275287
mod std { pub mod fmt { pub trait Debug {} } }
276288
fn main() {
277-
fmt::Debug;
278-
let x: fmt::Debug = fmt::Debug;
289+
Debug;
290+
let x: Debug = Debug;
291+
}
292+
",
293+
);
294+
}
295+
296+
#[test]
297+
fn assist_runs_on_first_segment_for_enum() {
298+
check_assist(
299+
replace_qualified_name_with_use,
300+
r"
301+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
302+
fn main() {
303+
$0std::option::Option;
304+
let x: std::option::Option<()> = std::option::Option::Some(());
305+
}
306+
",
307+
r"
308+
use std::option::Option;
309+
310+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
311+
fn main() {
312+
Option;
313+
let x: Option<()> = Option::Some(());
314+
}
315+
",
316+
);
317+
318+
check_assist(
319+
replace_qualified_name_with_use,
320+
r"
321+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
322+
fn main() {
323+
std::option::Option;
324+
let x: std::option::Option<()> = $0std::option::Option::Some(());
325+
}
326+
",
327+
r"
328+
use std::option::Option;
329+
330+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
331+
fn main() {
332+
Option;
333+
let x: Option<()> = Option::Some(());
334+
}
335+
",
336+
);
337+
}
338+
339+
#[test]
340+
fn assist_runs_on_enum_variant() {
341+
check_assist(
342+
replace_qualified_name_with_use,
343+
r"
344+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
345+
fn main() {
346+
let x = std::option::Option::Some$0(());
347+
}
348+
",
349+
r"
350+
use std::option::Option::Some;
351+
352+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
353+
fn main() {
354+
let x = Some(());
355+
}
356+
",
357+
);
358+
359+
check_assist(
360+
replace_qualified_name_with_use,
361+
r"
362+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
363+
fn main() {
364+
std::option::Option;
365+
let x: std::option::Option<()> = $0std::option::Option::Some(());
366+
}
367+
",
368+
r"
369+
use std::option::Option;
370+
371+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
372+
fn main() {
373+
Option;
374+
let x: Option<()> = Option::Some(());
279375
}
280376
",
281377
);

0 commit comments

Comments
 (0)