aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/items_locator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/items_locator.rs')
-rw-r--r--crates/ide_db/src/items_locator.rs178
1 files changed, 92 insertions, 86 deletions
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
index 8a7f02935..ef796b6f7 100644
--- a/crates/ide_db/src/items_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,6 +1,7 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module has the functionality to search the project and its dependencies for a certain item,
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! by its name and a few criteria.
3 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.
4use either::Either; 5use either::Either;
5use hir::{ 6use hir::{
6 import_map::{self, ImportKind}, 7 import_map::{self, ImportKind},
@@ -10,122 +11,121 @@ use syntax::{ast, AstNode, SyntaxKind::NAME};
10 11
11use crate::{ 12use crate::{
12 defs::{Definition, NameClass}, 13 defs::{Definition, NameClass},
14 helpers::import_assets::NameToImport,
13 symbol_index::{self, FileSymbol}, 15 symbol_index::{self, FileSymbol},
14 RootDatabase, 16 RootDatabase,
15}; 17};
16use rustc_hash::FxHashSet;
17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 18
20pub fn with_exact_name( 19/// A value to use, when uncertain which limit to pick.
21 sema: &Semantics<'_, RootDatabase>, 20pub const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
22 krate: Crate,
23 exact_name: String,
24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports");
26 find_items(
27 sema,
28 krate,
29 {
30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query
34 },
35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only()
38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(),
40 )
41}
42 21
43#[derive(Debug)] 22/// Three possible ways to search for the name in associated and/or other items.
23#[derive(Debug, Clone, Copy)]
44pub enum AssocItemSearch { 24pub enum AssocItemSearch {
25 /// Search for the name in both associated and other items.
45 Include, 26 Include,
27 /// Search for the name in other items only.
46 Exclude, 28 Exclude,
29 /// Search for the name in the associated items only.
47 AssocItemsOnly, 30 AssocItemsOnly,
48} 31}
49 32
50pub fn with_similar_name( 33/// Searches for importable items with the given name in the crate and its dependencies.
51 sema: &Semantics<'_, RootDatabase>, 34pub fn items_with_name<'a>(
35 sema: &'a Semantics<'_, RootDatabase>,
52 krate: Crate, 36 krate: Crate,
53 fuzzy_search_string: String, 37 name: NameToImport,
54 assoc_item_search: AssocItemSearch, 38 assoc_item_search: AssocItemSearch,
55 limit: Option<usize>, 39 limit: Option<usize>,
56) -> FxHashSet<ItemInNs> { 40) -> impl Iterator<Item = ItemInNs> + 'a {
57 let _p = profile::span("find_similar_imports"); 41 let _p = profile::span("items_with_name").detail(|| {
42 format!(
43 "Name: {}, crate: {:?}, assoc items: {:?}, limit: {:?}",
44 name.text(),
45 assoc_item_search,
46 krate.display_name(sema.db).map(|name| name.to_string()),
47 limit,
48 )
49 });
50
51 let (mut local_query, mut external_query) = match name {
52 NameToImport::Exact(exact_name) => {
53 let mut local_query = symbol_index::Query::new(exact_name.clone());
54 local_query.exact();
58 55
59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 56 let external_query = import_map::Query::new(exact_name)
60 .search_mode(import_map::SearchMode::Fuzzy) 57 .name_only()
61 .name_only(); 58 .search_mode(import_map::SearchMode::Equals)
59 .case_sensitive();
62 60
63 match assoc_item_search { 61 (local_query, external_query)
64 AssocItemSearch::Include => {}
65 AssocItemSearch::Exclude => {
66 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
67 } 62 }
68 AssocItemSearch::AssocItemsOnly => { 63 NameToImport::Fuzzy(fuzzy_search_string) => {
69 external_query = external_query.assoc_items_only(); 64 let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
65
66 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
67 .search_mode(import_map::SearchMode::Fuzzy)
68 .name_only();
69 match assoc_item_search {
70 AssocItemSearch::Include => {}
71 AssocItemSearch::Exclude => {
72 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
73 }
74 AssocItemSearch::AssocItemsOnly => {
75 external_query = external_query.assoc_items_only();
76 }
77 }
78
79 if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
80 local_query.case_sensitive();
81 external_query = external_query.case_sensitive();
82 }
83
84 (local_query, external_query)
70 } 85 }
71 } 86 };
72
73 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
74 87
75 if let Some(limit) = limit { 88 if let Some(limit) = limit {
76 external_query = external_query.limit(limit); 89 external_query = external_query.limit(limit);
77 local_query.limit(limit); 90 local_query.limit(limit);
78 } 91 }
79 92
80 find_items(sema, krate, local_query, external_query) 93 find_items(sema, krate, assoc_item_search, local_query, external_query)
81 .into_iter()
82 .filter(move |&item| match assoc_item_search {
83 AssocItemSearch::Include => true,
84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
86 })
87 .collect()
88} 94}
89 95
90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { 96fn find_items<'a>(
91 item.as_module_def_id() 97 sema: &'a Semantics<'_, RootDatabase>,
92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
93 .is_some()
94}
95
96fn find_items(
97 sema: &Semantics<'_, RootDatabase>,
98 krate: Crate, 98 krate: Crate,
99 assoc_item_search: AssocItemSearch,
99 local_query: symbol_index::Query, 100 local_query: symbol_index::Query,
100 external_query: import_map::Query, 101 external_query: import_map::Query,
101) -> FxHashSet<ItemInNs> { 102) -> impl Iterator<Item = ItemInNs> + 'a {
102 let _p = profile::span("find_similar_imports"); 103 let _p = profile::span("find_items");
103 let db = sema.db; 104 let db = sema.db;
104 105
105 // Query dependencies first. 106 let external_importables =
106 let mut candidates = krate 107 krate.query_external_importables(db, external_query).map(|external_importable| {
107 .query_external_importables(db, external_query) 108 match external_importable {
108 .map(|external_importable| match external_importable { 109 Either::Left(module_def) => ItemInNs::from(module_def),
109 Either::Left(module_def) => ItemInNs::from(module_def), 110 Either::Right(macro_def) => ItemInNs::from(macro_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def), 111 }
111 }) 112 });
112 .collect::<FxHashSet<_>>();
113 113
114 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query)
116 116 .into_iter()
117 candidates.extend( 117 .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
118 local_results 118 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 .into_iter() 119 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) 120 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 _ => None,
122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), 122 });
123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), 123
124 _ => None, 124 external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
125 }), 125 AssocItemSearch::Include => true,
126 ); 126 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
127 127 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
128 candidates 128 })
129} 129}
130 130
131fn get_name_definition( 131fn get_name_definition(
@@ -144,3 +144,9 @@ fn get_name_definition(
144 let name = ast::Name::cast(candidate_name_node)?; 144 let name = ast::Name::cast(candidate_name_node)?;
145 NameClass::classify(sema, &name)?.defined(sema.db) 145 NameClass::classify(sema, &name)?.defined(sema.db)
146} 146}
147
148fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
149 item.as_module_def_id()
150 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
151 .is_some()
152}