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.rs146
1 files changed, 146 insertions, 0 deletions
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
new file mode 100644
index 000000000..b81c14618
--- /dev/null
+++ b/crates/ide_db/src/items_locator.rs
@@ -0,0 +1,146 @@
1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3
4use either::Either;
5use hir::{
6 import_map::{self, ImportKind},
7 AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
8};
9use syntax::{ast, AstNode, SyntaxKind::NAME};
10
11use crate::{
12 defs::{Definition, NameClass},
13 symbol_index::{self, FileSymbol},
14 RootDatabase,
15};
16use rustc_hash::FxHashSet;
17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19
20pub fn with_for_exact_name(
21 sema: &Semantics<'_, RootDatabase>,
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
43#[derive(Debug)]
44pub enum AssocItemSearch {
45 Include,
46 Exclude,
47 AssocItemsOnly,
48}
49
50pub fn with_similar_name(
51 sema: &Semantics<'_, RootDatabase>,
52 krate: Crate,
53 fuzzy_search_string: String,
54 assoc_item_search: AssocItemSearch,
55 limit: Option<usize>,
56) -> FxHashSet<ItemInNs> {
57 let _p = profile::span("find_similar_imports");
58
59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
60 .search_mode(import_map::SearchMode::Fuzzy)
61 .name_only();
62
63 match assoc_item_search {
64 AssocItemSearch::Include => {}
65 AssocItemSearch::Exclude => {
66 external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
67 }
68 AssocItemSearch::AssocItemsOnly => {
69 external_query = external_query.assoc_items_only();
70 }
71 }
72
73 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
74
75 if let Some(limit) = limit {
76 external_query = external_query.limit(limit);
77 local_query.limit(limit);
78 }
79
80 find_items(sema, krate, 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}
89
90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
91 item.as_module_def_id()
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,
99 local_query: symbol_index::Query,
100 external_query: import_map::Query,
101) -> FxHashSet<ItemInNs> {
102 let _p = profile::span("find_similar_imports");
103 let db = sema.db;
104
105 // Query dependencies first.
106 let mut candidates = krate
107 .query_external_importables(db, external_query)
108 .map(|external_importable| match external_importable {
109 Either::Left(module_def) => ItemInNs::from(module_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def),
111 })
112 .collect::<FxHashSet<_>>();
113
114 // Query the local crate using the symbol index.
115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
116
117 candidates.extend(
118 local_results
119 .into_iter()
120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
121 .filter_map(|name_definition_to_import| match name_definition_to_import {
122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
124 _ => None,
125 }),
126 );
127
128 candidates
129}
130
131fn get_name_definition(
132 sema: &Semantics<'_, RootDatabase>,
133 import_candidate: &FileSymbol,
134) -> Option<Definition> {
135 let _p = profile::span("get_name_definition");
136 let file_id = import_candidate.file_id;
137
138 let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax());
139 let candidate_name_node = if candidate_node.kind() != NAME {
140 candidate_node.children().find(|it| it.kind() == NAME)?
141 } else {
142 candidate_node
143 };
144 let name = ast::Name::cast(candidate_name_node)?;
145 NameClass::classify(sema, &name)?.defined(sema.db)
146}