Work towards better import labels

This commit is contained in:
Kirill Bulatov 2021-03-03 01:26:53 +02:00
parent 4d4ac1d4fa
commit 33c83e72b9
13 changed files with 242 additions and 238 deletions

View File

@ -1094,4 +1094,27 @@ mod tests {
expect![[r#""#]],
);
}
#[test]
fn search_with_path() {
check_search(
r#"
//- /main.rs crate:main deps:dep
//- /dep.rs crate:dep
pub mod foo {
pub mod bar {
pub mod baz {
pub trait Display {
fn fmt();
}
}
}
}"#,
"main",
Query::new("baz::fmt".to_string()).search_mode(SearchMode::Fuzzy),
expect![[r#"
dep::foo::bar::baz::Display::fmt (a)
"#]],
);
}
}

View File

@ -478,7 +478,6 @@ impl Analysis {
position: FilePosition,
full_import_path: &str,
imported_name: String,
import_for_trait_assoc_item: bool,
) -> Cancelable<Vec<TextEdit>> {
Ok(self
.with_db(|db| {
@ -488,7 +487,6 @@ impl Analysis {
position,
full_import_path,
imported_name,
import_for_trait_assoc_item,
)
})?
.unwrap_or_default())

View File

@ -93,17 +93,18 @@ 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(&syntax_under_caret, &ctx.sema)?;
for import in proposed_imports {
let name = match import.original_item_name(ctx.db()) {
Some(name) => name,
None => continue,
};
acc.add_group(
&group,
AssistId("auto_import", AssistKind::QuickFix),
format!("Import `{}`", import.display_path()),
format!("Import `{}`", name),
range,
|builder| {
let rewriter = insert_use(
&scope,
mod_path_to_ast(import.import_path()),
ctx.config.insert_use,
);
let rewriter =
insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
builder.rewrite(rewriter);
},
);

View File

@ -2,7 +2,7 @@ use std::iter;
use hir::AsAssocItem;
use ide_db::helpers::{
import_assets::{ImportCandidate, Qualifier},
import_assets::{ImportCandidate, LocatedImport, Qualifier},
mod_path_to_ast,
};
use ide_db::RootDatabase;
@ -78,13 +78,13 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
acc.add_group(
&group_label,
AssistId("qualify_path", AssistKind::QuickFix),
label(candidate, import.display_path()),
label(ctx.db(), candidate, &import),
range,
|builder| {
qualify_candidate.qualify(
|replace_with: String| builder.replace(range, replace_with),
import.import_path(),
import.item_to_import(),
&import.import_path,
import.item_to_import,
)
},
);
@ -197,17 +197,21 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
GroupLabel(format!("Qualify {}", name))
}
fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
let display_path = match import.original_item_name(db) {
Some(display_path) => display_path.to_string(),
None => "{unknown}".to_string(),
};
match candidate {
ImportCandidate::Path(candidate) => {
if !matches!(candidate.qualifier, Qualifier::Absent) {
format!("Qualify with `{}`", &import)
format!("Qualify with `{}`", display_path)
} else {
format!("Qualify as `{}`", &import)
format!("Qualify as `{}`", display_path)
}
}
ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
}
}

View File

@ -1,5 +1,6 @@
use hir::ModuleDef;
use ide_db::helpers::mod_path_to_ast;
use ide_db::imports_locator;
use ide_db::items_locator;
use itertools::Itertools;
use syntax::{
ast::{self, make, AstNode, NameOwner},
@ -64,13 +65,14 @@ pub(crate) fn replace_derive_with_manual_impl(
let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
let current_crate = current_module.krate();
let found_traits = imports_locator::find_exact_imports(
let found_traits = items_locator::with_for_exact_name(
&ctx.sema,
current_crate,
trait_token.text().to_string(),
)
.filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
.into_iter()
.filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
ModuleDef::Trait(trait_) => Some(trait_),
_ => None,
})
.flat_map(|trait_| {

View File

@ -87,11 +87,12 @@
//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
//! capability enabled.
use hir::{AsAssocItem, ModPath, ModuleDef, ScopeDef};
use hir::ModPath;
use ide_db::helpers::{
import_assets::{ImportAssets, ImportCandidate},
insert_use::ImportScope,
};
use itertools::Itertools;
use syntax::{AstNode, SyntaxNode, T};
use crate::{
@ -130,27 +131,23 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
&ctx.sema,
)?;
let mut all_imports =
import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
all_imports.sort_by_cached_key(|import| {
compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
});
acc.add_all(all_imports.into_iter().filter_map(|import| {
let import_for_trait_assoc_item = import
.item_to_display()
.as_module_def_id()
.and_then(|module_def_id| {
ModuleDef::from(module_def_id).as_assoc_item(ctx.db)?.containing_trait(ctx.db)
acc.add_all(
import_assets
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
.into_iter()
.sorted_by_key(|located_import| {
compute_fuzzy_completion_order_key(
&located_import.import_path,
&user_input_lowercased,
)
})
.is_some();
let def_to_display = ScopeDef::from(import.item_to_display());
render_resolution_with_import(
RenderContext::new(ctx),
ImportEdit { import, import_scope: import_scope.clone(), import_for_trait_assoc_item },
&def_to_display,
)
}));
.filter_map(|import| {
render_resolution_with_import(
RenderContext::new(ctx),
ImportEdit { import, import_scope: import_scope.clone() },
)
}),
);
Some(())
}
@ -190,6 +187,7 @@ fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<I
ctx.scope.clone(),
)?;
// TODO kb bad: with the path prefix, the "min 3 symbols" limit applies. Fix in a separate PR on the symbol_index level
if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
&& fuzzy_name_length < 2
{
@ -796,9 +794,7 @@ fn main() {
#[test]
fn unresolved_qualifier() {
check_edit(
"Item",
r#"
let fixture = r#"
mod foo {
pub mod bar {
pub mod baz {
@ -809,31 +805,34 @@ mod foo {
fn main() {
bar::baz::Ite$0
}
"#,
}"#;
check(fixture, expect![["st Item (foo::bar::baz::Item)"]]);
check_edit(
"Item",
fixture,
r#"
use foo::bar;
use foo::bar;
mod foo {
pub mod bar {
pub mod baz {
pub struct Item;
mod foo {
pub mod bar {
pub mod baz {
pub struct Item;
}
}
}
}
}
fn main() {
bar::baz::Item
}
"#,
fn main() {
bar::baz::Item
}
"#,
);
}
#[test]
fn unresolved_assoc_item_container() {
check_edit(
"TEST_ASSOC",
r#"
let fixture = r#"
mod foo {
pub struct Item;
@ -844,8 +843,13 @@ mod foo {
fn main() {
Item::TEST_A$0
}
"#,
}"#;
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
check_edit(
"TEST_ASSOC",
fixture,
r#"
use foo::Item;
@ -866,9 +870,7 @@ fn main() {
#[test]
fn unresolved_assoc_item_container_with_path() {
check_edit(
"TEST_ASSOC",
r#"
let fixture = r#"
mod foo {
pub mod bar {
pub struct Item;
@ -881,8 +883,13 @@ mod foo {
fn main() {
bar::Item::TEST_A$0
}
"#,
}"#;
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
check_edit(
"TEST_ASSOC",
fixture,
r#"
use foo::bar;

View File

@ -275,7 +275,6 @@ impl CompletionItem {
pub struct ImportEdit {
pub import: LocatedImport,
pub import_scope: ImportScope,
pub import_for_trait_assoc_item: bool,
}
impl ImportEdit {
@ -286,7 +285,7 @@ impl ImportEdit {
let rewriter = insert_use::insert_use(
&self.import_scope,
mod_path_to_ast(self.import.import_path()),
mod_path_to_ast(&self.import.import_path),
cfg,
);
let old_ast = rewriter.rewrite_root()?;
@ -303,6 +302,7 @@ impl ImportEdit {
pub(crate) struct Builder {
source_range: TextRange,
completion_kind: CompletionKind,
// TODO kb also add a db here, to resolve the completion label?
import_to_add: Option<ImportEdit>,
label: String,
insert_text: Option<String>,
@ -322,19 +322,22 @@ impl Builder {
pub(crate) fn build(self) -> CompletionItem {
let _p = profile::span("item::Builder::build");
let mut label = self.label;
let mut lookup = self.lookup;
let mut insert_text = self.insert_text;
let label = self.label;
let lookup = self.lookup;
let insert_text = self.insert_text;
if let Some(import_to_add) = self.import_to_add.as_ref() {
lookup = lookup.or_else(|| Some(label.clone()));
insert_text = insert_text.or_else(|| Some(label.clone()));
let display_path = import_to_add.import.display_path();
if import_to_add.import_for_trait_assoc_item {
label = format!("{} ({})", label, display_path);
} else {
label = display_path.to_string();
}
if let Some(_import_to_add) = self.import_to_add.as_ref() {
todo!("todo kb")
// let import = &import_to_add.import;
// let item_to_import = import.item_to_import();
// lookup = lookup.or_else(|| Some(label.clone()));
// insert_text = insert_text.or_else(|| Some(label.clone()));
// let display_path = import_to_add.import.display_path();
// if import_to_add.import {
// label = format!("{} ({})", label, display_path);
// } else {
// label = display_path.to_string();
// }
}
let text_edit = match self.text_edit {
@ -438,8 +441,8 @@ impl Builder {
}
}
impl<'a> Into<CompletionItem> for Builder {
fn into(self) -> CompletionItem {
self.build()
}
}
// impl<'a> Into<CompletionItem> for Builder {
// fn into(self) -> CompletionItem {
// self.build()
// }
// }

View File

@ -15,7 +15,7 @@ use completions::flyimport::position_for_import;
use ide_db::{
base_db::FilePosition,
helpers::{import_assets::LocatedImport, insert_use::ImportScope},
imports_locator, RootDatabase,
items_locator, RootDatabase,
};
use text_edit::TextEdit;
@ -141,7 +141,6 @@ pub fn resolve_completion_edits(
position: FilePosition,
full_import_path: &str,
imported_name: String,
import_for_trait_assoc_item: bool,
) -> Option<Vec<TextEdit>> {
let ctx = CompletionContext::new(db, position, config)?;
let position_for_import = position_for_import(&ctx, None)?;
@ -151,19 +150,17 @@ pub fn resolve_completion_edits(
let current_crate = current_module.krate();
let (import_path, item_to_import) =
imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
items_locator::with_for_exact_name(&ctx.sema, current_crate, imported_name)
.into_iter()
.filter_map(|candidate| {
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
current_module
.find_use_path_prefixed(db, item, config.insert_use.prefix_kind)
.zip(Some(item))
.find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
.zip(Some(candidate))
})
.find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
let import = LocatedImport::new(import_path, item_to_import, None);
let import = LocatedImport::new(import_path, item_to_import, item_to_import);
ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
.to_text_edit(config.insert_use)
.map(|edit| vec![edit])
ImportEdit { import, import_scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
}
#[cfg(test)]

View File

@ -53,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
pub(crate) fn render_resolution_with_import<'a>(
ctx: RenderContext<'a>,
import_edit: ImportEdit,
resolution: &ScopeDef,
) -> Option<CompletionItem> {
let resolution = ScopeDef::from(import_edit.import.original_item);
let local_name = match resolution {
ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
_ => item_name(ctx.db(), import_edit.import.item_to_display())?.to_string(),
_ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
};
Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| {
item.completion_kind = CompletionKind::Magic;
item
})
Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
|mut item| {
item.completion_kind = CompletionKind::Magic;
item
},
)
}
/// Interface for data and methods required for items rendering.

View File

@ -1,14 +1,13 @@
//! Look up accessible paths for items.
use either::Either;
use hir::{
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
};
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode};
use crate::{
imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
RootDatabase,
};
@ -130,34 +129,23 @@ impl<'a> ImportAssets<'a> {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LocatedImport {
import_path: ModPath,
item_to_import: ItemInNs,
data_to_display: Option<(ModPath, ItemInNs)>,
pub import_path: ModPath,
pub item_to_import: ItemInNs,
pub original_item: ItemInNs,
}
impl LocatedImport {
pub fn new(
import_path: ModPath,
item_to_import: ItemInNs,
data_to_display: Option<(ModPath, ItemInNs)>,
) -> Self {
Self { import_path, item_to_import, data_to_display }
pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self {
Self { import_path, item_to_import, original_item }
}
pub fn display_path(&self) -> &ModPath {
self.data_to_display.as_ref().map(|(mod_path, _)| mod_path).unwrap_or(&self.import_path)
}
pub fn import_path(&self) -> &ModPath {
&self.import_path
}
pub fn item_to_display(&self) -> ItemInNs {
self.data_to_display.as_ref().map(|&(_, item)| item).unwrap_or(self.item_to_import)
}
pub fn item_to_import(&self) -> ItemInNs {
self.item_to_import
pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> {
match self.original_item {
ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => {
ModuleDef::from(module_def_id).name(db)
}
ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
}
}
}
@ -166,25 +154,20 @@ impl<'a> ImportAssets<'a> {
&self.import_candidate
}
fn name_to_import(&self) -> &NameToImport {
match &self.import_candidate {
ImportCandidate::Path(candidate) => &candidate.name,
ImportCandidate::TraitAssocItem(candidate)
| ImportCandidate::TraitMethod(candidate) => &candidate.name,
}
}
pub fn search_for_imports(
&self,
sema: &Semantics<RootDatabase>,
prefix_kind: PrefixKind,
) -> Vec<LocatedImport> {
) -> FxHashSet<LocatedImport> {
let _p = profile::span("import_assets::search_for_imports");
self.search_for(sema, Some(prefix_kind))
}
/// This may return non-absolute paths if a part of the returned path is already imported into scope.
pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
pub fn search_for_relative_paths(
&self,
sema: &Semantics<RootDatabase>,
) -> FxHashSet<LocatedImport> {
let _p = profile::span("import_assets::search_for_relative_paths");
self.search_for(sema, None)
}
@ -193,14 +176,13 @@ impl<'a> ImportAssets<'a> {
&self,
sema: &Semantics<RootDatabase>,
prefixed: Option<PrefixKind>,
) -> Vec<LocatedImport> {
let current_crate = self.module_with_candidate.krate();
let scope_definitions = self.scope_definitions();
let defs_for_candidate_name = match self.name_to_import() {
NameToImport::Exact(exact_name) => {
imports_locator::find_exact_imports(sema, current_crate, exact_name.clone())
}
) -> FxHashSet<LocatedImport> {
let items_with_candidate_name = match self.name_to_import() {
NameToImport::Exact(exact_name) => items_locator::with_for_exact_name(
sema,
self.module_with_candidate.krate(),
exact_name.clone(),
),
// FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
// instead, we need to look up all trait impls for a certain struct and search through them only
// see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
@ -213,9 +195,9 @@ impl<'a> ImportAssets<'a> {
(AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
};
imports_locator::find_similar_imports(
items_locator::with_similar_name(
sema,
current_crate,
self.module_with_candidate.krate(),
fuzzy_name.clone(),
assoc_item_search,
limit,
@ -223,10 +205,11 @@ impl<'a> ImportAssets<'a> {
}
};
self.applicable_defs(sema.db, prefixed, defs_for_candidate_name)
let scope_definitions = self.scope_definitions();
self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
.into_iter()
.filter(|import| import.import_path().len() > 1)
.filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import())))
.filter(|import| import.import_path.len() > 1)
.filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
.collect()
}
@ -238,11 +221,19 @@ impl<'a> ImportAssets<'a> {
scope_definitions
}
fn name_to_import(&self) -> &NameToImport {
match &self.import_candidate {
ImportCandidate::Path(candidate) => &candidate.name,
ImportCandidate::TraitAssocItem(candidate)
| ImportCandidate::TraitMethod(candidate) => &candidate.name,
}
}
fn applicable_defs(
&self,
db: &RootDatabase,
prefixed: Option<PrefixKind>,
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
items_with_candidate_name: FxHashSet<ItemInNs>,
) -> FxHashSet<LocatedImport> {
let _p = profile::span("import_assets::applicable_defs");
let current_crate = self.module_with_candidate.krate();
@ -251,7 +242,7 @@ impl<'a> ImportAssets<'a> {
match &self.import_candidate {
ImportCandidate::Path(path_candidate) => {
path_applicable_imports(db, path_candidate, mod_path, defs_for_candidate_name)
path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
}
ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
db,
@ -259,7 +250,7 @@ impl<'a> ImportAssets<'a> {
trait_candidate,
true,
mod_path,
defs_for_candidate_name,
items_with_candidate_name,
),
ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
db,
@ -267,7 +258,7 @@ impl<'a> ImportAssets<'a> {
trait_candidate,
false,
mod_path,
defs_for_candidate_name,
items_with_candidate_name,
),
}
}
@ -277,17 +268,15 @@ fn path_applicable_imports(
db: &RootDatabase,
path_candidate: &PathImportCandidate,
mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
items_with_candidate_name: FxHashSet<ItemInNs>,
) -> FxHashSet<LocatedImport> {
let _p = profile::span("import_assets::path_applicable_imports");
let items_for_candidate_name =
defs_for_candidate_name.map(|def| def.either(ItemInNs::from, ItemInNs::from));
let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
Qualifier::Absent => {
return items_for_candidate_name
.filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, None)))
return items_with_candidate_name
.into_iter()
.filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, item)))
.collect();
}
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => {
@ -295,7 +284,8 @@ fn path_applicable_imports(
}
};
items_for_candidate_name
items_with_candidate_name
.into_iter()
.filter_map(|item| {
import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
})
@ -336,7 +326,6 @@ fn import_for_item(
}
let segment_import = find_import_for_segment(db, item_candidate, &unresolved_first_segment)?;
let data_to_display = Some((import_path_candidate.clone(), original_item));
Some(match (segment_import == item_candidate, trait_to_import) {
(true, Some(_)) => {
// FIXME we should be able to import both the trait and the segment,
@ -345,11 +334,11 @@ fn import_for_item(
return None;
}
(false, Some(trait_to_import)) => {
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, data_to_display)
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
}
(true, None) => LocatedImport::new(import_path_candidate, item_candidate, data_to_display),
(true, None) => LocatedImport::new(import_path_candidate, item_candidate, original_item),
(false, None) => {
LocatedImport::new(mod_path(segment_import)?, segment_import, data_to_display)
LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
}
})
}
@ -399,16 +388,14 @@ fn trait_applicable_items(
trait_candidate: &TraitImportCandidate,
trait_assoc_item: bool,
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
defs_for_candidate_name: impl Iterator<Item = Either<ModuleDef, MacroDef>>,
items_with_candidate_name: FxHashSet<ItemInNs>,
) -> FxHashSet<LocatedImport> {
let _p = profile::span("import_assets::trait_applicable_items");
let mut required_assoc_items = FxHashSet::default();
let trait_candidates = defs_for_candidate_name
.filter_map(|input| match input {
Either::Left(module_def) => module_def.as_assoc_item(db),
_ => None,
})
let trait_candidates = items_with_candidate_name
.into_iter()
.filter_map(|input| ModuleDef::from(input.as_module_def_id()?).as_assoc_item(db))
.filter_map(|assoc| {
let assoc_item_trait = assoc.containing_trait(db)?;
required_assoc_items.insert(assoc);
@ -433,20 +420,10 @@ fn trait_applicable_items(
}
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
let item_path = mod_path(item)?;
let assoc_item = assoc_to_item(assoc);
let assoc_item_path = match assoc.container(db) {
AssocItemContainer::Trait(_) => item_path.clone(),
AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from(
ModuleDef::from(impl_.target_ty(db).as_adt()?),
))?,
};
located_imports.insert(LocatedImport::new(
item_path,
mod_path(item)?,
item,
Some((assoc_item_path, assoc_item)),
assoc_to_item(assoc),
));
}
None::<()>
@ -462,20 +439,10 @@ fn trait_applicable_items(
let assoc = function.as_assoc_item(db)?;
if required_assoc_items.contains(&assoc) {
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
let item_path = mod_path(item)?;
let assoc_item = assoc_to_item(assoc);
let assoc_item_path = match assoc.container(db) {
AssocItemContainer::Trait(_) => item_path.clone(),
AssocItemContainer::Impl(impl_) => mod_path(ItemInNs::from(
ModuleDef::from(impl_.target_ty(db).as_adt()?),
))?,
};
located_imports.insert(LocatedImport::new(
item_path,
mod_path(item)?,
item,
Some((assoc_item_path, assoc_item)),
assoc_to_item(assoc),
));
}
None::<()>

View File

@ -1,9 +1,10 @@
//! This module contains an import search functionality that is provided to the assists module.
//! Later, this should be moved away to a separate crate that is accessible from the assists module.
use either::Either;
use hir::{
import_map::{self, ImportKind},
AsAssocItem, Crate, MacroDef, ModuleDef, Semantics,
AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
};
use syntax::{ast, AstNode, SyntaxKind::NAME};
@ -12,32 +13,31 @@ use crate::{
symbol_index::{self, FileSymbol},
RootDatabase,
};
use either::Either;
use rustc_hash::FxHashSet;
pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
pub fn find_exact_imports(
pub fn with_for_exact_name(
sema: &Semantics<'_, RootDatabase>,
krate: Crate,
name_to_import: String,
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> {
exact_name: String,
) -> FxHashSet<ItemInNs> {
let _p = profile::span("find_exact_imports");
Box::new(find_imports(
find_items(
sema,
krate,
{
let mut local_query = symbol_index::Query::new(name_to_import.clone());
let mut local_query = symbol_index::Query::new(exact_name.clone());
local_query.exact();
local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
local_query
},
import_map::Query::new(name_to_import)
import_map::Query::new(exact_name)
.limit(DEFAULT_QUERY_SEARCH_LIMIT)
.name_only()
.search_mode(import_map::SearchMode::Equals)
.case_sensitive(),
))
)
}
#[derive(Debug)]
@ -47,13 +47,13 @@ pub enum AssocItemSearch {
AssocItemsOnly,
}
pub fn find_similar_imports<'a>(
sema: &'a Semantics<'a, RootDatabase>,
pub fn with_similar_name(
sema: &Semantics<'_, RootDatabase>,
krate: Crate,
fuzzy_search_string: String,
assoc_item_search: AssocItemSearch,
limit: Option<usize>,
) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> {
) -> FxHashSet<ItemInNs> {
let _p = profile::span("find_similar_imports");
let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@ -77,36 +77,39 @@ pub fn find_similar_imports<'a>(
local_query.limit(limit);
}
Box::new(find_imports(sema, krate, local_query, external_query).filter(
move |import_candidate| match assoc_item_search {
find_items(sema, krate, local_query, external_query)
.into_iter()
.filter(move |&item| match assoc_item_search {
AssocItemSearch::Include => true,
AssocItemSearch::Exclude => !is_assoc_item(import_candidate, sema.db),
AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, sema.db),
},
))
AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
})
.collect()
}
fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool {
match import_candidate {
Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(),
Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(),
Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
_ => false,
}
fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
item.as_module_def_id()
.and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
.is_some()
}
fn find_imports<'a>(
sema: &Semantics<'a, RootDatabase>,
fn find_items(
sema: &Semantics<'_, RootDatabase>,
krate: Crate,
local_query: symbol_index::Query,
external_query: import_map::Query,
) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
) -> FxHashSet<ItemInNs> {
let _p = profile::span("find_similar_imports");
let db = sema.db;
// Query dependencies first.
let mut candidates: FxHashSet<_> =
krate.query_external_importables(db, external_query).collect();
let mut candidates = krate
.query_external_importables(db, external_query)
.map(|external_importable| match external_importable {
Either::Left(module_def) => ItemInNs::from(module_def),
Either::Right(macro_def) => ItemInNs::from(macro_def),
})
.collect::<FxHashSet<_>>();
// Query the local crate using the symbol index.
let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
@ -114,19 +117,19 @@ fn find_imports<'a>(
candidates.extend(
local_results
.into_iter()
.filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
.filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
.filter_map(|name_definition_to_import| match name_definition_to_import {
Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
_ => None,
}),
);
candidates.into_iter()
candidates
}
fn get_name_definition<'a>(
sema: &Semantics<'a, RootDatabase>,
fn get_name_definition(
sema: &Semantics<'_, RootDatabase>,
import_candidate: &FileSymbol,
) -> Option<Definition> {
let _p = profile::span("get_name_definition");

View File

@ -8,7 +8,7 @@ pub mod line_index;
pub mod symbol_index;
pub mod defs;
pub mod search;
pub mod imports_locator;
pub mod items_locator;
pub mod source_change;
pub mod ty_filter;
pub mod traits;

View File

@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
FilePosition { file_id, offset },
&resolve_data.full_import_path,
resolve_data.imported_name,
resolve_data.import_for_trait_assoc_item,
)?
.into_iter()
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@ -1525,7 +1524,6 @@ struct CompletionResolveData {
position: lsp_types::TextDocumentPositionParams,
full_import_path: String,
imported_name: String,
import_for_trait_assoc_item: bool,
}
fn fill_resolve_data(
@ -1534,14 +1532,13 @@ fn fill_resolve_data(
position: &TextDocumentPositionParams,
) -> Option<()> {
let import_edit = item.import_to_add()?;
let import_path = import_edit.import.import_path();
let import_path = &import_edit.import.import_path;
*resolve_data = Some(
to_value(CompletionResolveData {
position: position.to_owned(),
full_import_path: import_path.to_string(),
imported_name: import_path.segments().last()?.to_string(),
import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
})
.unwrap(),
);