From 18591ae5c8f1f8f649cab46193a226e740545c68 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 11 Dec 2023 18:37:12 +0100 Subject: [PATCH 1/3] Always render the path to be imported in the completion detail --- crates/ide-completion/src/item.rs | 12 +++--- crates/ide-completion/src/snippet.rs | 2 +- crates/ide-completion/src/tests/flyimport.rs | 26 ++++++------ crates/ide-db/src/imports/import_assets.rs | 42 ++++++-------------- 4 files changed, 31 insertions(+), 51 deletions(-) diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index b982322a7349..de41a5bd70c4 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -458,13 +458,11 @@ impl Builder { } if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one - if let Some(original_path) = import_edit.original_path.as_ref() { - label_detail.replace(SmolStr::from(format!( - "{} (use {})", - label_detail.as_deref().unwrap_or_default(), - original_path.display(db) - ))); - } + label_detail.replace(SmolStr::from(format!( + "{} (use {})", + label_detail.as_deref().unwrap_or_default(), + import_edit.import_path.display(db) + ))); } else if let Some(trait_name) = self.trait_name { label_detail.replace(SmolStr::from(format!( "{} (as {trait_name})", diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index 50618296ee43..e667e2e01683 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -181,7 +181,7 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option 1).then(|| LocatedImport::new(path.clone(), item, item, None))) + Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item))) }; let mut res = Vec::with_capacity(requires.len()); for import in requires { diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 9a4a94a24566..c58374f2e833 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -597,8 +597,8 @@ fn main() { } "#, expect![[r#" - ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED "#]], ); } @@ -717,7 +717,7 @@ fn main() { check( fixture, expect![[r#" - st Item (use foo::bar::baz::Item) Item + st Item (use foo::bar) Item "#]], ); @@ -725,19 +725,19 @@ fn main() { "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 +}"#, ); } @@ -803,7 +803,7 @@ fn main() { check( fixture, expect![[r#" - ct TEST_ASSOC (use foo::bar::Item) usize + ct TEST_ASSOC (use foo::bar) usize "#]], ); diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 04263d15d0a5..e92af1298003 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -195,18 +195,11 @@ pub struct LocatedImport { /// the original item is the associated constant, but the import has to be a trait that /// defines this constant. pub original_item: ItemInNs, - /// A path of the original item. - pub original_path: Option, } impl LocatedImport { - pub fn new( - import_path: ModPath, - item_to_import: ItemInNs, - original_item: ItemInNs, - original_path: Option, - ) -> Self { - Self { import_path, item_to_import, original_item, original_path } + pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self { + Self { import_path, item_to_import, original_item } } } @@ -351,7 +344,7 @@ fn path_applicable_imports( ) .filter_map(|item| { let mod_path = mod_path(item)?; - Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) + Some(LocatedImport::new(mod_path, item, item)) }) .collect() } @@ -416,24 +409,15 @@ fn import_for_item( // especially in case of lazy completion edit resolutions. return None; } - (false, Some(trait_to_import)) => LocatedImport::new( - mod_path(trait_to_import)?, - trait_to_import, - original_item, - mod_path(original_item), - ), - (true, None) => LocatedImport::new( - import_path_candidate, - original_item_candidate, - original_item, - mod_path(original_item), - ), - (false, None) => LocatedImport::new( - mod_path(segment_import)?, - segment_import, - original_item, - mod_path(original_item), - ), + (false, Some(trait_to_import)) => { + LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item) + } + (true, None) => { + LocatedImport::new(import_path_candidate, original_item_candidate, original_item) + } + (false, None) => { + LocatedImport::new(mod_path(segment_import)?, segment_import, original_item) + } }) } @@ -550,7 +534,6 @@ fn trait_applicable_items( mod_path(trait_item)?, trait_item, original_item, - mod_path(original_item), )); } None::<()> @@ -573,7 +556,6 @@ fn trait_applicable_items( mod_path(trait_item)?, trait_item, original_item, - mod_path(original_item), )); } None::<()> From 34ec665ba1d39be2dd01775b96b63344cb38a57d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Dec 2023 11:35:34 +0100 Subject: [PATCH 2/3] Simplify and improve perf of import_assets::import_for_item --- crates/ide-db/src/imports/import_assets.rs | 160 +++++++++++---------- crates/syntax/src/ast/node_ext.rs | 13 ++ crates/syntax/src/utils.rs | 42 +----- 3 files changed, 99 insertions(+), 116 deletions(-) diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index e92af1298003..a4f0a6df7813 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -1,14 +1,14 @@ //! Look up accessible paths for items. + use hir::{ - AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, + AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type, }; -use itertools::Itertools; -use rustc_hash::FxHashSet; +use itertools::{EitherOrBoth, Itertools}; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ ast::{self, make, HasName}, - utils::path_to_string_stripping_turbo_fish, - AstNode, SyntaxNode, + AstNode, SmolStr, SyntaxNode, }; use crate::{ @@ -51,18 +51,11 @@ pub struct TraitImportCandidate { #[derive(Debug)] pub struct PathImportCandidate { /// Optional qualifier before name. - pub qualifier: Option, + pub qualifier: Option>, /// The name the item (struct, trait, enum, etc.) should have. pub name: NameToImport, } -/// A qualifier that has a first segment and it's unresolved. -#[derive(Debug)] -pub struct FirstSegmentUnresolved { - fist_segment: ast::NameRef, - full_qualifier: ast::Path, -} - /// A name that will be used during item lookups. #[derive(Debug, Clone)] pub enum NameToImport { @@ -348,60 +341,71 @@ fn path_applicable_imports( }) .collect() } - Some(first_segment_unresolved) => { - let unresolved_qualifier = - path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier); - let unresolved_first_segment = first_segment_unresolved.fist_segment.text(); - items_locator::items_with_name( - sema, - current_crate, - path_candidate.name.clone(), - AssocSearchMode::Include, - Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), - ) - .filter_map(|item| { - import_for_item( - sema.db, - mod_path, - &unresolved_first_segment, - &unresolved_qualifier, - item, - ) - }) - .collect() - } + Some(qualifier) => items_locator::items_with_name( + sema, + current_crate, + path_candidate.name.clone(), + AssocSearchMode::Include, + Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()), + ) + .filter_map(|item| import_for_item(sema.db, mod_path, &qualifier, item)) + .collect(), } } fn import_for_item( db: &RootDatabase, mod_path: impl Fn(ItemInNs) -> Option, - unresolved_first_segment: &str, - unresolved_qualifier: &str, + unresolved_qualifier: &[SmolStr], original_item: ItemInNs, ) -> Option { let _p = profile::span("import_assets::import_for_item"); + let [first_segment, ..] = unresolved_qualifier else { return None }; - let original_item_candidate = item_for_path_search(db, original_item)?; + let item_as_assoc = item_as_assoc(db, original_item); + + let (original_item_candidate, trait_item_to_import) = match item_as_assoc { + Some(assoc_item) => match assoc_item.container(db) { + AssocItemContainer::Trait(trait_) => { + let trait_ = ItemInNs::from(ModuleDef::from(trait_)); + (trait_, Some(trait_)) + } + AssocItemContainer::Impl(impl_) => { + (ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), None) + } + }, + None => (original_item, None), + }; let import_path_candidate = mod_path(original_item_candidate)?; - let import_path_string = import_path_candidate.display(db).to_string(); - let expected_import_end = if item_as_assoc(db, original_item).is_some() { - unresolved_qualifier.to_string() - } else { - format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db)) + let mut import_path_candidate_segments = import_path_candidate.segments().iter().rev(); + let predicate = |it: EitherOrBoth<&SmolStr, &Name>| match it { + // segments match, check next one + EitherOrBoth::Both(a, b) if b.as_str() == Some(&**a) => None, + // segments mismatch / qualifier is longer than the path, bail out + EitherOrBoth::Both(..) | EitherOrBoth::Left(_) => Some(false), + // all segments match and we have exhausted the qualifier, proceed + EitherOrBoth::Right(_) => Some(true), }; - if !import_path_string.contains(unresolved_first_segment) - || !import_path_string.ends_with(&expected_import_end) - { + if item_as_assoc.is_none() { + let item_name = item_name(db, original_item)?.as_text()?; + let last_segment = import_path_candidate_segments.next()?; + if last_segment.as_str() != Some(&*item_name) { + return None; + } + } + let ends_with = unresolved_qualifier + .iter() + .rev() + .zip_longest(import_path_candidate_segments) + .find_map(predicate) + .unwrap_or(true); + if !ends_with { return None; } - let segment_import = - find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?; - let trait_item_to_import = item_as_assoc(db, original_item) - .and_then(|assoc| assoc.containing_trait(db)) - .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); + let segment_import = find_import_for_segment(db, original_item_candidate, first_segment)?; + Some(match (segment_import == original_item_candidate, trait_item_to_import) { (true, Some(_)) => { // FIXME we should be able to import both the trait and the segment, @@ -424,18 +428,22 @@ fn import_for_item( pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { Some(match item { ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { - Some(assoc_item) => match assoc_item.container(db) { - AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), - AssocItemContainer::Impl(impl_) => { - ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)) - } - }, + Some(assoc_item) => item_for_path_search_assoc(db, assoc_item)?, None => item, }, ItemInNs::Macros(_) => item, }) } +fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Option { + Some(match assoc_item.container(db) { + AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), + AssocItemContainer::Impl(impl_) => { + ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)) + } + }) +} + fn find_import_for_segment( db: &RootDatabase, original_item: ItemInNs, @@ -512,6 +520,7 @@ fn trait_applicable_items( .collect(); let mut located_imports = FxHashSet::default(); + let mut trait_import_paths = FxHashMap::default(); if trait_assoc_item { trait_candidate.receiver_ty.iterate_path_candidates( @@ -529,11 +538,14 @@ fn trait_applicable_items( } let located_trait = assoc.containing_trait(db)?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); - let original_item = assoc_to_item(assoc); + let import_path = trait_import_paths + .entry(trait_item) + .or_insert_with(|| mod_path(trait_item)) + .clone()?; located_imports.insert(LocatedImport::new( - mod_path(trait_item)?, + import_path, trait_item, - original_item, + assoc_to_item(assoc), )); } None::<()> @@ -551,11 +563,14 @@ fn trait_applicable_items( if required_assoc_items.contains(&assoc) { let located_trait = assoc.containing_trait(db)?; let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); - let original_item = assoc_to_item(assoc); + let import_path = trait_import_paths + .entry(trait_item) + .or_insert_with(|| mod_path(trait_item)) + .clone()?; located_imports.insert(LocatedImport::new( - mod_path(trait_item)?, + import_path, trait_item, - original_item, + assoc_to_item(assoc), )); } None::<()> @@ -653,18 +668,13 @@ fn path_import_candidate( Some(match qualifier { Some(qualifier) => match sema.resolve_path(&qualifier) { None => { - let qualifier_start = - qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; - let qualifier_start_path = - qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; - if sema.resolve_path(&qualifier_start_path).is_none() { - ImportCandidate::Path(PathImportCandidate { - qualifier: Some(FirstSegmentUnresolved { - fist_segment: qualifier_start, - full_qualifier: qualifier, - }), - name, - }) + if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) { + let mut qualifier = qualifier + .segments_of_this_path_only_rev() + .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text()))) + .collect::>>()?; + qualifier.reverse(); + ImportCandidate::Path(PathImportCandidate { qualifier: Some(qualifier), name }) } else { return None; } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index be5b954ad345..f81dff8840cc 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -275,10 +275,19 @@ impl ast::Path { successors(Some(self.clone()), ast::Path::qualifier).last().unwrap() } + pub fn first_qualifier(&self) -> Option { + successors(self.qualifier(), ast::Path::qualifier).last() + } + pub fn first_segment(&self) -> Option { self.first_qualifier_or_self().segment() } + // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function + pub fn segments_of_this_path_only_rev(&self) -> impl Iterator + Clone { + self.qualifiers_and_self().filter_map(|it| it.segment()) + } + pub fn segments(&self) -> impl Iterator + Clone { successors(self.first_segment(), |p| { p.parent_path().parent_path().and_then(|p| p.segment()) @@ -289,6 +298,10 @@ impl ast::Path { successors(self.qualifier(), |p| p.qualifier()) } + pub fn qualifiers_and_self(&self) -> impl Iterator + Clone { + successors(Some(self.clone()), |p| p.qualifier()) + } + pub fn top_path(&self) -> ast::Path { let mut this = self.clone(); while let Some(path) = this.parent_path() { diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs index 25f34ea9d397..a38f8b2b55d4 100644 --- a/crates/syntax/src/utils.rs +++ b/crates/syntax/src/utils.rs @@ -1,48 +1,8 @@ //! A set of utils methods to reuse on other abstraction levels -use itertools::Itertools; - -use crate::{ast, match_ast, AstNode, SyntaxKind}; - -pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String { - path.syntax() - .children() - .filter_map(|node| { - match_ast! { - match node { - ast::PathSegment(it) => { - Some(it.name_ref()?.to_string()) - }, - ast::Path(it) => { - Some(path_to_string_stripping_turbo_fish(&it)) - }, - _ => None, - } - } - }) - .join("::") -} +use crate::SyntaxKind; pub fn is_raw_identifier(name: &str) -> bool { let is_keyword = SyntaxKind::from_keyword(name).is_some(); is_keyword && !matches!(name, "self" | "crate" | "super" | "Self") } - -#[cfg(test)] -mod tests { - use super::path_to_string_stripping_turbo_fish; - use crate::ast::make; - - #[test] - fn turbofishes_are_stripped() { - assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::")),); - assert_eq!( - "Vec::new", - path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::::new")), - ); - assert_eq!( - "Vec::new", - path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")), - ); - } -} From 6dee64c9f4c3f3c48e1e34e029ef8e41fd7d04a9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Dec 2023 12:03:37 +0100 Subject: [PATCH 3/3] Bump DEFAULT_QUERY_SEARCH_LIMIT from 40 to 100 --- crates/hir/src/symbols.rs | 44 +++++++++++++++++------------- crates/ide-db/src/items_locator.rs | 8 +++--- crates/ide-db/src/symbol_index.rs | 2 +- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 03112f6de5af..a2a30edeb039 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -23,6 +23,7 @@ pub struct FileSymbol { pub loc: DeclarationLocation, pub container_name: Option, pub is_alias: bool, + pub is_assoc: bool, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -121,34 +122,34 @@ impl<'a> SymbolCollector<'a> { match module_def_id { ModuleDefId::ModuleId(id) => self.push_module(id), ModuleDefId::FunctionId(id) => { - self.push_decl(id); + self.push_decl(id, false); self.collect_from_body(id); } - ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id), - ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id), - ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id), + ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false), + ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false), + ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false), ModuleDefId::ConstId(id) => { - self.push_decl(id); + self.push_decl(id, false); self.collect_from_body(id); } ModuleDefId::StaticId(id) => { - self.push_decl(id); + self.push_decl(id, false); self.collect_from_body(id); } ModuleDefId::TraitId(id) => { - self.push_decl(id); + self.push_decl(id, false); self.collect_from_trait(id); } ModuleDefId::TraitAliasId(id) => { - self.push_decl(id); + self.push_decl(id, false); } ModuleDefId::TypeAliasId(id) => { - self.push_decl(id); + self.push_decl(id, false); } ModuleDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => self.push_decl(id), - MacroId::MacroRulesId(id) => self.push_decl(id), - MacroId::ProcMacroId(id) => self.push_decl(id), + MacroId::Macro2Id(id) => self.push_decl(id, false), + MacroId::MacroRulesId(id) => self.push_decl(id, false), + MacroId::ProcMacroId(id) => self.push_decl(id, false), }, // Don't index these. ModuleDefId::BuiltinType(_) => {} @@ -190,6 +191,7 @@ impl<'a> SymbolCollector<'a> { container_name: self.current_container_name.clone(), loc: dec_loc, is_alias: false, + is_assoc: false, }); }); } @@ -202,9 +204,9 @@ impl<'a> SymbolCollector<'a> { for &id in id { if id.module(self.db.upcast()) == module_id { match id { - MacroId::Macro2Id(id) => self.push_decl(id), - MacroId::MacroRulesId(id) => self.push_decl(id), - MacroId::ProcMacroId(id) => self.push_decl(id), + MacroId::Macro2Id(id) => self.push_decl(id, false), + MacroId::MacroRulesId(id) => self.push_decl(id, false), + MacroId::ProcMacroId(id) => self.push_decl(id, false), } } } @@ -266,13 +268,13 @@ impl<'a> SymbolCollector<'a> { fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl(id), - AssocItemId::ConstId(id) => self.push_decl(id), - AssocItemId::TypeAliasId(id) => self.push_decl(id), + AssocItemId::FunctionId(id) => self.push_decl(id, true), + AssocItemId::ConstId(id) => self.push_decl(id, true), + AssocItemId::TypeAliasId(id) => self.push_decl(id, true), } } - fn push_decl(&mut self, id: L) + fn push_decl(&mut self, id: L, is_assoc: bool) where L: Lookup + Into, ::Data: HasSource, @@ -296,6 +298,7 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc.clone(), container_name: self.current_container_name.clone(), is_alias: true, + is_assoc, }); } } @@ -306,6 +309,7 @@ impl<'a> SymbolCollector<'a> { container_name: self.current_container_name.clone(), loc: dec_loc, is_alias: false, + is_assoc, }); } @@ -331,6 +335,7 @@ impl<'a> SymbolCollector<'a> { loc: dec_loc.clone(), container_name: self.current_container_name.clone(), is_alias: true, + is_assoc: false, }); } } @@ -341,6 +346,7 @@ impl<'a> SymbolCollector<'a> { container_name: self.current_container_name.clone(), loc: dec_loc, is_alias: false, + is_assoc: false, }); } } diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs index 67ed44f08b7f..022b167d492e 100644 --- a/crates/ide-db/src/items_locator.rs +++ b/crates/ide-db/src/items_locator.rs @@ -3,13 +3,13 @@ //! The main reason for this module to exist is the fact that project's items and dependencies' items //! are located in different caches, with different APIs. use either::Either; -use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics}; +use hir::{import_map, Crate, ItemInNs, Semantics}; use limit::Limit; use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; /// A value to use, when uncertain which limit to pick. -pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40); +pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100); pub use import_map::AssocSearchMode; @@ -101,8 +101,8 @@ fn find_items<'a>( .into_iter() .filter(move |candidate| match assoc_item_search { AssocSearchMode::Include => true, - AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(), - AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(), + AssocSearchMode::Exclude => !candidate.is_assoc, + AssocSearchMode::AssocItemsOnly => candidate.is_assoc, }) .map(|local_candidate| match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index 3e89159c2c6e..be8566b759cf 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -50,7 +50,7 @@ enum SearchMode { Prefix, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Query { query: String, lowercased: String,