]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/imports_locator.rs
Share import_assets and related entities
[rust.git] / crates / ide_db / src / imports_locator.rs
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
4 use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
5 use syntax::{ast, AstNode, SyntaxKind::NAME};
6
7 use crate::{
8     defs::{Definition, NameClass},
9     symbol_index::{self, FileSymbol},
10     RootDatabase,
11 };
12 use either::Either;
13 use rustc_hash::FxHashSet;
14
15 const QUERY_SEARCH_LIMIT: usize = 40;
16
17 pub fn find_exact_imports<'a>(
18     sema: &Semantics<'a, RootDatabase>,
19     krate: Crate,
20     name_to_import: String,
21 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
22     let _p = profile::span("find_exact_imports");
23     find_imports(
24         sema,
25         krate,
26         {
27             let mut local_query = symbol_index::Query::new(name_to_import.clone());
28             local_query.exact();
29             local_query.limit(QUERY_SEARCH_LIMIT);
30             local_query
31         },
32         import_map::Query::new(name_to_import)
33             .limit(QUERY_SEARCH_LIMIT)
34             .name_only()
35             .search_mode(import_map::SearchMode::Equals)
36             .case_sensitive(),
37     )
38 }
39
40 pub fn find_similar_imports<'a>(
41     sema: &Semantics<'a, RootDatabase>,
42     krate: Crate,
43     limit: Option<usize>,
44     fuzzy_search_string: String,
45     ignore_assoc_items: bool,
46     name_only: bool,
47 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a {
48     let _p = profile::span("find_similar_imports");
49
50     let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
51         .search_mode(import_map::SearchMode::Fuzzy);
52     if name_only {
53         external_query = external_query.name_only();
54     }
55
56     let mut local_query = symbol_index::Query::new(fuzzy_search_string);
57
58     if let Some(limit) = limit {
59         local_query.limit(limit);
60         external_query = external_query.limit(limit);
61     }
62
63     let db = sema.db;
64     find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| {
65         if ignore_assoc_items {
66             match import_candidate {
67                 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(),
68                 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(),
69                 Either::Left(ModuleDef::TypeAlias(type_alias)) => {
70                     type_alias.as_assoc_item(db).is_none()
71                 }
72                 _ => true,
73             }
74         } else {
75             true
76         }
77     })
78 }
79
80 fn find_imports<'a>(
81     sema: &Semantics<'a, RootDatabase>,
82     krate: Crate,
83     local_query: symbol_index::Query,
84     external_query: import_map::Query,
85 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
86     let _p = profile::span("find_similar_imports");
87     let db = sema.db;
88
89     // Query dependencies first.
90     let mut candidates: FxHashSet<_> =
91         krate.query_external_importables(db, external_query).collect();
92
93     // Query the local crate using the symbol index.
94     let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
95
96     candidates.extend(
97         local_results
98             .into_iter()
99             .filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
100             .filter_map(|name_definition_to_import| match name_definition_to_import {
101                 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
102                 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
103                 _ => None,
104             }),
105     );
106
107     candidates.into_iter()
108 }
109
110 fn get_name_definition<'a>(
111     sema: &Semantics<'a, RootDatabase>,
112     import_candidate: &FileSymbol,
113 ) -> Option<Definition> {
114     let _p = profile::span("get_name_definition");
115     let file_id = import_candidate.file_id;
116
117     let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax());
118     let candidate_name_node = if candidate_node.kind() != NAME {
119         candidate_node.children().find(|it| it.kind() == NAME)?
120     } else {
121         candidate_node
122     };
123     let name = ast::Name::cast(candidate_name_node)?;
124     NameClass::classify(sema, &name)?.defined(sema.db)
125 }