Merge #6172
6172: Add qualify path assist r=matklad a=Veykril This implements a `qualify_path` assist which works quite similar to the `auto_import` assist but instead of adding imports to the file it well, qualifies the path. This PR also moves out the `AutoImportAssets` struct and functions from `auto_import` into a utils submodule as most of this is now shared between `auto_import` and `qualify_path`. Changes made to `AutoImportAssets` are solely in its `search_for_imports` function which now takes a prefixed parameter to discern between using `find_use_path_prefixed` and `find_use_path` as the former is the required behavior by `auto_import` and the latter by this assist. For missing imported traits instead of importing this will qualify the path with a trait cast as in: ```rust test_mod::TestStruct::TEST_CONST<|> ``` becomes ```rust <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST ``` and for trait methods ideally it would do the following: ```rust let test_struct = test_mod::TestStruct {}; test_struct.test_meth<|>od() ``` becomes ```rust let test_struct = test_mod::TestStruct {}; test_mod::TestTrait::test_method(&test_struct) ``` Fixes #4124. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
989de9e309
@ -100,7 +100,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
let group = import_group_message(import_assets.import_candidate());
|
||||
let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?;
|
||||
let syntax = scope.as_syntax_node();
|
||||
for import in proposed_imports {
|
||||
for (import, _) in proposed_imports {
|
||||
acc.add_group(
|
||||
&group,
|
||||
AssistId("auto_import", AssistKind::QuickFix),
|
||||
|
1048
crates/assists/src/handlers/qualify_path.rs
Normal file
1048
crates/assists/src/handlers/qualify_path.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -150,6 +150,7 @@ mod handlers {
|
||||
mod merge_match_arms;
|
||||
mod move_bounds;
|
||||
mod move_guard;
|
||||
mod qualify_path;
|
||||
mod raw_string;
|
||||
mod remove_dbg;
|
||||
mod remove_mut;
|
||||
@ -196,6 +197,7 @@ mod handlers {
|
||||
move_bounds::move_bounds_to_where_clause,
|
||||
move_guard::move_arm_cond_to_match_guard,
|
||||
move_guard::move_guard_to_arm_body,
|
||||
qualify_path::qualify_path,
|
||||
raw_string::add_hash,
|
||||
raw_string::make_raw_string,
|
||||
raw_string::make_usual_string,
|
||||
|
@ -712,6 +712,25 @@ fn handle(action: Action) {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_qualify_path() {
|
||||
check_doc_test(
|
||||
"qualify_path",
|
||||
r#####"
|
||||
fn main() {
|
||||
let map = HashMap<|>::new();
|
||||
}
|
||||
pub mod std { pub mod collections { pub struct HashMap { } } }
|
||||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let map = std::collections::HashMap::new();
|
||||
}
|
||||
pub mod std { pub mod collections { pub struct HashMap { } } }
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_remove_dbg() {
|
||||
check_doc_test(
|
||||
|
@ -1,6 +1,4 @@
|
||||
//! Look up accessible paths for items.
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use either::Either;
|
||||
use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
|
||||
use ide_db::{imports_locator, RootDatabase};
|
||||
@ -29,12 +27,12 @@ pub(crate) enum ImportCandidate {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TraitImportCandidate {
|
||||
pub ty: hir::Type,
|
||||
pub name: String,
|
||||
pub name: ast::NameRef,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PathImportCandidate {
|
||||
pub name: String,
|
||||
pub name: ast::NameRef,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -86,9 +84,9 @@ impl ImportAssets {
|
||||
fn get_search_query(&self) -> &str {
|
||||
match &self.import_candidate {
|
||||
ImportCandidate::UnqualifiedName(candidate)
|
||||
| ImportCandidate::QualifierStart(candidate) => &candidate.name,
|
||||
| ImportCandidate::QualifierStart(candidate) => candidate.name.text(),
|
||||
ImportCandidate::TraitAssocItem(candidate)
|
||||
| ImportCandidate::TraitMethod(candidate) => &candidate.name,
|
||||
| ImportCandidate::TraitMethod(candidate) => candidate.name.text(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +94,7 @@ impl ImportAssets {
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
config: &InsertUseConfig,
|
||||
) -> BTreeSet<hir::ModPath> {
|
||||
) -> Vec<(hir::ModPath, hir::ItemInNs)> {
|
||||
let _p = profile::span("import_assists::search_for_imports");
|
||||
self.search_for(sema, Some(config.prefix_kind))
|
||||
}
|
||||
@ -106,7 +104,7 @@ impl ImportAssets {
|
||||
pub(crate) fn search_for_relative_paths(
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
) -> BTreeSet<hir::ModPath> {
|
||||
) -> Vec<(hir::ModPath, hir::ItemInNs)> {
|
||||
let _p = profile::span("import_assists::search_for_relative_paths");
|
||||
self.search_for(sema, None)
|
||||
}
|
||||
@ -115,7 +113,7 @@ impl ImportAssets {
|
||||
&self,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
prefixed: Option<hir::PrefixKind>,
|
||||
) -> BTreeSet<hir::ModPath> {
|
||||
) -> Vec<(hir::ModPath, hir::ItemInNs)> {
|
||||
let db = sema.db;
|
||||
let mut trait_candidates = FxHashSet::default();
|
||||
let current_crate = self.module_with_name_to_import.krate();
|
||||
@ -181,7 +179,7 @@ impl ImportAssets {
|
||||
}
|
||||
};
|
||||
|
||||
imports_locator::find_imports(sema, current_crate, &self.get_search_query())
|
||||
let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query())
|
||||
.into_iter()
|
||||
.filter_map(filter)
|
||||
.filter_map(|candidate| {
|
||||
@ -191,10 +189,13 @@ impl ImportAssets {
|
||||
} else {
|
||||
self.module_with_name_to_import.find_use_path(db, item)
|
||||
}
|
||||
.map(|path| (path, item))
|
||||
})
|
||||
.filter(|use_path| !use_path.segments.is_empty())
|
||||
.filter(|(use_path, _)| !use_path.segments.is_empty())
|
||||
.take(20)
|
||||
.collect::<BTreeSet<_>>()
|
||||
.collect::<Vec<_>>();
|
||||
res.sort_by_key(|(path, _)| path.clone());
|
||||
res
|
||||
}
|
||||
|
||||
fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> {
|
||||
@ -215,7 +216,7 @@ impl ImportCandidate {
|
||||
Some(_) => None,
|
||||
None => Some(Self::TraitMethod(TraitImportCandidate {
|
||||
ty: sema.type_of_expr(&method_call.receiver()?)?,
|
||||
name: method_call.name_ref()?.syntax().to_string(),
|
||||
name: method_call.name_ref()?,
|
||||
})),
|
||||
}
|
||||
}
|
||||
@ -243,24 +244,17 @@ impl ImportCandidate {
|
||||
hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
|
||||
ImportCandidate::TraitAssocItem(TraitImportCandidate {
|
||||
ty: assoc_item_path.ty(sema.db),
|
||||
name: segment.syntax().to_string(),
|
||||
name: segment.name_ref()?,
|
||||
})
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
ImportCandidate::QualifierStart(PathImportCandidate {
|
||||
name: qualifier_start.syntax().to_string(),
|
||||
})
|
||||
ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start })
|
||||
}
|
||||
} else {
|
||||
ImportCandidate::UnqualifiedName(PathImportCandidate {
|
||||
name: segment
|
||||
.syntax()
|
||||
.descendants()
|
||||
.find_map(ast::NameRef::cast)?
|
||||
.syntax()
|
||||
.to_string(),
|
||||
name: segment.syntax().descendants().find_map(ast::NameRef::cast)?,
|
||||
})
|
||||
};
|
||||
Some(candidate)
|
||||
|
@ -172,6 +172,9 @@ pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
|
||||
pub fn expr_method_call(receiver: ast::Expr, method: &str, arg_list: ast::ArgList) -> ast::Expr {
|
||||
expr_from_text(&format!("{}.{}{}", receiver, method, arg_list))
|
||||
}
|
||||
pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
|
||||
expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) })
|
||||
}
|
||||
fn expr_from_text(text: &str) -> ast::Expr {
|
||||
ast_from_text(&format!("const C: () = {};", text))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user