1 //! This module has the functionality to search the project and its dependencies for a certain item,
2 //! by its name and a few criteria.
3 //! The main reason for this module to exist is the fact that project's items and dependencies' items
4 //! are located in different caches, with different APIs.
7 import_map::{self, ImportKind},
8 AsAssocItem, Crate, ItemInNs, Semantics,
11 use syntax::{ast, AstNode, SyntaxKind::NAME};
14 defs::{Definition, NameClass},
15 helpers::import_assets::NameToImport,
16 symbol_index::{self, FileSymbol},
20 /// A value to use, when uncertain which limit to pick.
21 pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
23 /// Three possible ways to search for the name in associated and/or other items.
24 #[derive(Debug, Clone, Copy)]
25 pub enum AssocItemSearch {
26 /// Search for the name in both associated and other items.
28 /// Search for the name in other items only.
30 /// Search for the name in the associated items only.
34 /// Searches for importable items with the given name in the crate and its dependencies.
35 pub fn items_with_name<'a>(
36 sema: &'a Semantics<'_, RootDatabase>,
39 assoc_item_search: AssocItemSearch,
41 ) -> impl Iterator<Item = ItemInNs> + 'a {
42 let _p = profile::span("items_with_name").detail(|| {
44 "Name: {}, crate: {:?}, assoc items: {:?}, limit: {:?}",
47 krate.display_name(sema.db).map(|name| name.to_string()),
52 let (mut local_query, mut external_query) = match name {
53 NameToImport::Exact(exact_name, case_sensitive) => {
54 let mut local_query = symbol_index::Query::new(exact_name.clone());
57 let external_query = import_map::Query::new(exact_name)
59 .search_mode(import_map::SearchMode::Equals);
63 if case_sensitive { external_query.case_sensitive() } else { external_query },
66 NameToImport::Fuzzy(fuzzy_search_string) => {
67 let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
69 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
70 .search_mode(import_map::SearchMode::Fuzzy)
72 match assoc_item_search {
73 AssocItemSearch::Include => {}
74 AssocItemSearch::Exclude => {
75 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
77 AssocItemSearch::AssocItemsOnly => {
78 external_query = external_query.assoc_items_only();
82 if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
83 local_query.case_sensitive();
84 external_query = external_query.case_sensitive();
87 (local_query, external_query)
91 if let Some(limit) = limit {
92 external_query = external_query.limit(limit);
93 local_query.limit(limit);
96 find_items(sema, krate, assoc_item_search, local_query, external_query)
100 sema: &'a Semantics<'_, RootDatabase>,
102 assoc_item_search: AssocItemSearch,
103 local_query: symbol_index::Query,
104 external_query: import_map::Query,
105 ) -> impl Iterator<Item = ItemInNs> + 'a {
106 let _p = profile::span("find_items");
109 let external_importables =
110 krate.query_external_importables(db, external_query).map(|external_importable| {
111 match external_importable {
112 Either::Left(module_def) => ItemInNs::from(module_def),
113 Either::Right(macro_def) => ItemInNs::from(macro_def),
117 // Query the local crate using the symbol index.
118 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query)
120 .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
121 .filter_map(|name_definition_to_import| match name_definition_to_import {
122 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
123 def => <Option<_>>::from(def),
126 external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
127 AssocItemSearch::Include => true,
128 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
129 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
133 fn get_name_definition(
134 sema: &Semantics<'_, RootDatabase>,
135 import_candidate: &FileSymbol,
136 ) -> Option<Definition> {
137 let _p = profile::span("get_name_definition");
139 let candidate_node = import_candidate.loc.syntax(sema)?;
140 let candidate_name_node = if candidate_node.kind() != NAME {
141 candidate_node.children().find(|it| it.kind() == NAME)?
145 let name = ast::Name::cast(candidate_name_node)?;
146 NameClass::classify(sema, &name)?.defined()
149 fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
150 item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some()