diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 4f1c9faa08..3bd7769052 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -81,32 +81,34 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() let potential_import_name = ctx.token.to_string(); - let possible_imports = - imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400) - .filter_map(|import_candidate| match import_candidate { - // when completing outside the use declaration, modules are pretty useless - // and tend to bloat the completion suggestions a lot - Either::Left(ModuleDef::Module(_)) => None, - Either::Left(module_def) => Some(( - current_module.find_use_path(ctx.db, module_def)?, - ScopeDef::ModuleDef(module_def), - )), - Either::Right(macro_def) => Some(( - current_module.find_use_path(ctx.db, macro_def)?, - ScopeDef::MacroDef(macro_def), - )), - }) - .filter(|(mod_path, _)| mod_path.len() > 1) - .filter_map(|(import_path, definition)| { - render_resolution_with_import( - RenderContext::new(ctx), - import_path.clone(), - import_scope.clone(), - ctx.config.merge, - &definition, - ) - }) - .take(20); + let possible_imports = imports_locator::find_similar_imports( + &ctx.sema, + ctx.krate?, + &potential_import_name, + 50, + true, + ) + .filter_map(|import_candidate| { + Some(match import_candidate { + Either::Left(module_def) => { + (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) + } + Either::Right(macro_def) => { + (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) + } + }) + }) + .filter(|(mod_path, _)| mod_path.len() > 1) + .take(20) + .filter_map(|(import_path, definition)| { + render_resolution_with_import( + RenderContext::new(ctx), + import_path.clone(), + import_scope.clone(), + ctx.config.merge, + &definition, + ) + }); acc.add_all(possible_imports); Some(()) diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index e892d4de85..bce02f5773 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -150,6 +150,7 @@ impl<'a> Render<'a> { import_data: Option<(ModPath, ImportScope, Option)>, resolution: &ScopeDef, ) -> Option { + let _p = profile::span("render_resolution"); use hir::ModuleDef::*; let completion_kind = match resolution { diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 44499bc794..3d9436d690 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] crossbeam-channel = "0.5.0" log = "0.4.8" -cargo_metadata = "0.12.0" +cargo_metadata = "=0.12.0" serde_json = "1.0.48" jod-thread = "0.1.1" diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 1e24f29a81..c0f1088480 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs @@ -7,7 +7,7 @@ use fst::{self, Streamer}; use hir_expand::name::Name; use indexmap::{map::Entry, IndexMap}; use itertools::Itertools; -use rustc_hash::{FxHashMap, FxHasher}; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use smallvec::SmallVec; use syntax::SmolStr; @@ -225,6 +225,19 @@ fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) lhs_str.cmp(&rhs_str) } +#[derive(Debug, Eq, PartialEq, Hash)] +pub enum ImportKind { + Module, + Function, + Adt, + EnumVariant, + Const, + Static, + Trait, + TypeAlias, + BuiltinType, +} + #[derive(Debug)] pub struct Query { query: String, @@ -232,6 +245,7 @@ pub struct Query { anchor_end: bool, case_sensitive: bool, limit: usize, + exclude_import_kinds: FxHashSet, } impl Query { @@ -242,6 +256,7 @@ impl Query { anchor_end: false, case_sensitive: false, limit: usize::max_value(), + exclude_import_kinds: FxHashSet::default(), } } @@ -260,6 +275,12 @@ impl Query { pub fn case_sensitive(self) -> Self { Self { case_sensitive: true, ..self } } + + /// Do not include imports of the specified kind in the search results. + pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self { + self.exclude_import_kinds.insert(import_kind); + self + } } /// Searches dependencies of `krate` for an importable path matching `query`. @@ -303,10 +324,17 @@ pub fn search_dependencies<'a>( // Add the items from this `ModPath` group. Those are all subsequent items in // `importables` whose paths match `path`. - let iter = importables.iter().copied().take_while(|item| { - let item_path = &import_map.map[item].path; - fst_path(item_path) == fst_path(path) - }); + let iter = importables + .iter() + .copied() + .take_while(|item| { + let item_path = &import_map.map[item].path; + fst_path(item_path) == fst_path(path) + }) + .filter(|&item| match item_import_kind(item) { + Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), + None => true, + }); if query.case_sensitive { // FIXME: This does not do a subsequence match. @@ -341,6 +369,20 @@ pub fn search_dependencies<'a>( res } +fn item_import_kind(item: ItemInNs) -> Option { + Some(match item.as_module_def_id()? { + ModuleDefId::ModuleId(_) => ImportKind::Module, + ModuleDefId::FunctionId(_) => ImportKind::Function, + ModuleDefId::AdtId(_) => ImportKind::Adt, + ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant, + ModuleDefId::ConstId(_) => ImportKind::Const, + ModuleDefId::StaticId(_) => ImportKind::Static, + ModuleDefId::TraitId(_) => ImportKind::Trait, + ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias, + ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType, + }) +} + #[cfg(test)] mod tests { use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; @@ -758,4 +800,34 @@ mod tests { "#]], ); } + + #[test] + fn search_exclusions() { + let ra_fixture = r#" + //- /main.rs crate:main deps:dep + //- /dep.rs crate:dep + + pub struct fmt; + pub struct FMT; + "#; + + check_search( + ra_fixture, + "main", + Query::new("FMT"), + expect![[r#" + dep::fmt (t) + dep::fmt (v) + dep::FMT (t) + dep::FMT (v) + "#]], + ); + + check_search( + ra_fixture, + "main", + Query::new("FMT").exclude_import_kind(ImportKind::Adt), + expect![[r#""#]], + ); + } } diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index 9d8ea7368d..09046d3c36 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs @@ -36,8 +36,15 @@ pub fn find_similar_imports<'a>( krate: Crate, name_to_import: &str, limit: usize, + ignore_modules: bool, ) -> impl Iterator> { let _p = profile::span("find_similar_imports"); + + let mut external_query = import_map::Query::new(name_to_import).limit(limit); + if ignore_modules { + external_query = external_query.exclude_import_kind(import_map::ImportKind::Module); + } + find_imports( sema, krate, @@ -46,7 +53,7 @@ pub fn find_similar_imports<'a>( local_query.limit(limit); local_query }, - import_map::Query::new(name_to_import).limit(limit), + external_query, ) } diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index 048b321869..7293729688 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml @@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } test_utils = { path = "../test_utils", version = "0.0.0" } [dev-dependencies] -cargo_metadata = "0.12.0" +cargo_metadata = "=0.12.0" difference = "2.0.0" # used as proc macro test targets diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index 2d53bcbcca..e0c591603e 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] log = "0.4.8" rustc-hash = "1.1.0" -cargo_metadata = "0.12.0" +cargo_metadata = "=0.12.0" serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.48" anyhow = "1.0.26"