Skip to content

Commit af72fd3

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 af72fd3

File tree

1 file changed

+128
-14
lines changed

1 file changed

+128
-14
lines changed

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

Lines changed: 128 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,22 @@ 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+
let on_first = original_path.qualifier().is_none();
95+
96+
if on_first {
97+
original_path = original_path.top_path();
98+
}
99+
100+
match ctx.sema.resolve_path(&original_path)? {
101+
PathResolution::Def(ModuleDef::Variant(_)) if on_first => original_path.qualifier(),
102+
PathResolution::Def(def) if def.as_assoc_item(ctx.db()).is_some() => {
103+
on_first.then_some(original_path.qualifier()?)
104+
}
105+
_ => Some(original_path),
106+
}
107+
}
108+
100109
fn drop_generic_args(path: &ast::Path) -> ast::Path {
101110
let path = path.clone_for_update();
102111
if let Some(segment) = path.segment()
@@ -270,12 +279,117 @@ fn main() {
270279
}
271280
",
272281
r"
273-
use std::fmt;
282+
use std::fmt::Debug;
274283
275284
mod std { pub mod fmt { pub trait Debug {} } }
276285
fn main() {
277-
fmt::Debug;
278-
let x: fmt::Debug = fmt::Debug;
286+
Debug;
287+
let x: Debug = Debug;
288+
}
289+
",
290+
);
291+
}
292+
293+
#[test]
294+
fn assist_runs_on_first_segment_for_enum() {
295+
check_assist(
296+
replace_qualified_name_with_use,
297+
r"
298+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
299+
fn main() {
300+
$0std::option::Option;
301+
let x: std::option::Option<()> = std::option::Option::Some(());
302+
}
303+
",
304+
r"
305+
use std::option::Option;
306+
307+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
308+
fn main() {
309+
Option;
310+
let x: Option<()> = Option::Some(());
311+
}
312+
",
313+
);
314+
315+
check_assist(
316+
replace_qualified_name_with_use,
317+
r"
318+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
319+
fn main() {
320+
std::option::Option;
321+
let x: std::option::Option<()> = $0std::option::Option::Some(());
322+
}
323+
",
324+
r"
325+
use std::option::Option;
326+
327+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
328+
fn main() {
329+
Option;
330+
let x: Option<()> = Option::Some(());
331+
}
332+
",
333+
);
334+
}
335+
336+
#[test]
337+
fn assist_runs_on_first_segment_for_assoc_type() {
338+
check_assist(
339+
replace_qualified_name_with_use,
340+
r"
341+
mod foo { pub struct Foo; impl Foo { pub fn foo() {} } }
342+
fn main() {
343+
$0foo::Foo::foo();
344+
}
345+
",
346+
r"
347+
use foo::Foo;
348+
349+
mod foo { pub struct Foo; impl Foo { pub fn foo() {} } }
350+
fn main() {
351+
Foo::foo();
352+
}
353+
",
354+
);
355+
}
356+
357+
#[test]
358+
fn assist_runs_on_enum_variant() {
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+
let x = std::option::Option::Some$0(());
365+
}
366+
",
367+
r"
368+
use std::option::Option::Some;
369+
370+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
371+
fn main() {
372+
let x = Some(());
373+
}
374+
",
375+
);
376+
377+
check_assist(
378+
replace_qualified_name_with_use,
379+
r"
380+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
381+
fn main() {
382+
std::option::Option;
383+
let x: std::option::Option<()> = $0std::option::Option::Some(());
384+
}
385+
",
386+
r"
387+
use std::option::Option;
388+
389+
mod std { pub mod option { pub enum Option<T> { Some(T), None } } }
390+
fn main() {
391+
Option;
392+
let x: Option<()> = Option::Some(());
279393
}
280394
",
281395
);

0 commit comments

Comments
 (0)