diff options
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r-- | crates/ide_db/src/call_info.rs | 14 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 208 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 4 | ||||
-rw-r--r-- | crates/ide_db/src/items_locator.rs | 178 | ||||
-rw-r--r-- | crates/ide_db/src/symbol_index.rs | 16 |
5 files changed, 212 insertions, 208 deletions
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index 7e26c3ccf..e583a52f4 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -4,7 +4,7 @@ use either::Either; | |||
4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; | 4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; |
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, ArgListOwner}, | 7 | ast::{self, ArgListOwner, NameOwner}, |
8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, | 8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -142,7 +142,7 @@ fn call_info_impl( | |||
142 | #[derive(Debug)] | 142 | #[derive(Debug)] |
143 | pub struct ActiveParameter { | 143 | pub struct ActiveParameter { |
144 | pub ty: Type, | 144 | pub ty: Type, |
145 | pub name: String, | 145 | pub pat: Either<ast::SelfParam, ast::Pat>, |
146 | } | 146 | } |
147 | 147 | ||
148 | impl ActiveParameter { | 148 | impl ActiveParameter { |
@@ -165,8 +165,14 @@ impl ActiveParameter { | |||
165 | return None; | 165 | return None; |
166 | } | 166 | } |
167 | let (pat, ty) = params.swap_remove(idx); | 167 | let (pat, ty) = params.swap_remove(idx); |
168 | let name = pat?.to_string(); | 168 | pat.map(|pat| ActiveParameter { ty, pat }) |
169 | Some(ActiveParameter { ty, name }) | 169 | } |
170 | |||
171 | pub fn ident(&self) -> Option<ast::Name> { | ||
172 | self.pat.as_ref().right().and_then(|param| match param { | ||
173 | ast::Pat::IdentPat(ident) => ident.name(), | ||
174 | _ => None, | ||
175 | }) | ||
170 | } | 176 | } |
171 | } | 177 | } |
172 | 178 | ||
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index e03ccd351..1881c746f 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -61,7 +61,7 @@ pub struct FirstSegmentUnresolved { | |||
61 | } | 61 | } |
62 | 62 | ||
63 | /// A name that will be used during item lookups. | 63 | /// A name that will be used during item lookups. |
64 | #[derive(Debug)] | 64 | #[derive(Debug, Clone)] |
65 | pub enum NameToImport { | 65 | pub enum NameToImport { |
66 | /// Requires items with names that exactly match the given string, case-sensitive. | 66 | /// Requires items with names that exactly match the given string, case-sensitive. |
67 | Exact(String), | 67 | Exact(String), |
@@ -201,125 +201,101 @@ impl ImportAssets { | |||
201 | sema: &Semantics<RootDatabase>, | 201 | sema: &Semantics<RootDatabase>, |
202 | prefixed: Option<PrefixKind>, | 202 | prefixed: Option<PrefixKind>, |
203 | ) -> Vec<LocatedImport> { | 203 | ) -> Vec<LocatedImport> { |
204 | let items_with_candidate_name = match self.name_to_import() { | 204 | let _p = profile::span("import_assets::search_for"); |
205 | NameToImport::Exact(exact_name) => items_locator::with_exact_name( | ||
206 | sema, | ||
207 | self.module_with_candidate.krate(), | ||
208 | exact_name.clone(), | ||
209 | ), | ||
210 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: | ||
211 | // instead, we need to look up all trait impls for a certain struct and search through them only | ||
212 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 | ||
213 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | ||
214 | // for the details | ||
215 | NameToImport::Fuzzy(fuzzy_name) => { | ||
216 | let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { | ||
217 | (AssocItemSearch::AssocItemsOnly, None) | ||
218 | } else { | ||
219 | (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT)) | ||
220 | }; | ||
221 | |||
222 | items_locator::with_similar_name( | ||
223 | sema, | ||
224 | self.module_with_candidate.krate(), | ||
225 | fuzzy_name.clone(), | ||
226 | assoc_item_search, | ||
227 | limit, | ||
228 | ) | ||
229 | } | ||
230 | }; | ||
231 | 205 | ||
232 | let scope_definitions = self.scope_definitions(sema); | 206 | let scope_definitions = self.scope_definitions(sema); |
233 | self.applicable_defs(sema.db, prefixed, items_with_candidate_name) | ||
234 | .into_iter() | ||
235 | .filter(|import| import.import_path.len() > 1) | ||
236 | .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import))) | ||
237 | .sorted_by_key(|import| import.import_path.clone()) | ||
238 | .collect() | ||
239 | } | ||
240 | |||
241 | fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> { | ||
242 | let mut scope_definitions = FxHashSet::default(); | ||
243 | sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| { | ||
244 | scope_definitions.insert(scope_def); | ||
245 | }); | ||
246 | scope_definitions | ||
247 | } | ||
248 | |||
249 | fn name_to_import(&self) -> &NameToImport { | ||
250 | match &self.import_candidate { | ||
251 | ImportCandidate::Path(candidate) => &candidate.name, | ||
252 | ImportCandidate::TraitAssocItem(candidate) | ||
253 | | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name, | ||
254 | } | ||
255 | } | ||
256 | |||
257 | fn applicable_defs( | ||
258 | &self, | ||
259 | db: &RootDatabase, | ||
260 | prefixed: Option<PrefixKind>, | ||
261 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
262 | ) -> FxHashSet<LocatedImport> { | ||
263 | let _p = profile::span("import_assets::applicable_defs"); | ||
264 | let current_crate = self.module_with_candidate.krate(); | 207 | let current_crate = self.module_with_candidate.krate(); |
265 | |||
266 | let mod_path = |item| { | 208 | let mod_path = |item| { |
267 | get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) | 209 | get_mod_path( |
210 | sema.db, | ||
211 | item_for_path_search(sema.db, item)?, | ||
212 | &self.module_with_candidate, | ||
213 | prefixed, | ||
214 | ) | ||
268 | }; | 215 | }; |
269 | 216 | ||
270 | match &self.import_candidate { | 217 | match &self.import_candidate { |
271 | ImportCandidate::Path(path_candidate) => { | 218 | ImportCandidate::Path(path_candidate) => { |
272 | path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) | 219 | path_applicable_imports(sema, current_crate, path_candidate, mod_path) |
220 | } | ||
221 | ImportCandidate::TraitAssocItem(trait_candidate) => { | ||
222 | trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path) | ||
223 | } | ||
224 | ImportCandidate::TraitMethod(trait_candidate) => { | ||
225 | trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path) | ||
273 | } | 226 | } |
274 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( | ||
275 | db, | ||
276 | current_crate, | ||
277 | trait_candidate, | ||
278 | true, | ||
279 | mod_path, | ||
280 | items_with_candidate_name, | ||
281 | ), | ||
282 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | ||
283 | db, | ||
284 | current_crate, | ||
285 | trait_candidate, | ||
286 | false, | ||
287 | mod_path, | ||
288 | items_with_candidate_name, | ||
289 | ), | ||
290 | } | 227 | } |
228 | .into_iter() | ||
229 | .filter(|import| import.import_path.len() > 1) | ||
230 | .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import))) | ||
231 | .sorted_by_key(|import| import.import_path.clone()) | ||
232 | .collect() | ||
233 | } | ||
234 | |||
235 | fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> { | ||
236 | let _p = profile::span("import_assets::scope_definitions"); | ||
237 | let mut scope_definitions = FxHashSet::default(); | ||
238 | sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| { | ||
239 | scope_definitions.insert(scope_def); | ||
240 | }); | ||
241 | scope_definitions | ||
291 | } | 242 | } |
292 | } | 243 | } |
293 | 244 | ||
294 | fn path_applicable_imports( | 245 | fn path_applicable_imports( |
295 | db: &RootDatabase, | 246 | sema: &Semantics<RootDatabase>, |
247 | current_crate: Crate, | ||
296 | path_candidate: &PathImportCandidate, | 248 | path_candidate: &PathImportCandidate, |
297 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, | 249 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, |
298 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
299 | ) -> FxHashSet<LocatedImport> { | 250 | ) -> FxHashSet<LocatedImport> { |
300 | let _p = profile::span("import_assets::path_applicable_imports"); | 251 | let _p = profile::span("import_assets::path_applicable_imports"); |
301 | 252 | ||
302 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | 253 | match &path_candidate.qualifier { |
303 | None => { | 254 | None => { |
304 | return items_with_candidate_name | 255 | items_locator::items_with_name( |
305 | .into_iter() | 256 | sema, |
306 | .filter_map(|item| { | 257 | current_crate, |
307 | Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item))) | 258 | path_candidate.name.clone(), |
308 | }) | 259 | // FIXME: we could look up assoc items by the input and propose those in completion, |
309 | .collect(); | 260 | // but that requries more preparation first: |
261 | // * store non-trait assoc items in import_map to fully enable this lookup | ||
262 | // * ensure that does not degrade the performance (bencmark it) | ||
263 | // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now) | ||
264 | // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear | ||
265 | // | ||
266 | // see also an ignored test under FIXME comment in the qualify_path.rs module | ||
267 | AssocItemSearch::Exclude, | ||
268 | Some(DEFAULT_QUERY_SEARCH_LIMIT), | ||
269 | ) | ||
270 | .filter_map(|item| { | ||
271 | let mod_path = mod_path(item)?; | ||
272 | Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) | ||
273 | }) | ||
274 | .collect() | ||
310 | } | 275 | } |
311 | Some(first_segment_unresolved) => ( | 276 | Some(first_segment_unresolved) => { |
312 | first_segment_unresolved.fist_segment.to_string(), | 277 | let unresolved_qualifier = |
313 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), | 278 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier); |
314 | ), | 279 | let unresolved_first_segment = first_segment_unresolved.fist_segment.text(); |
315 | }; | 280 | items_locator::items_with_name( |
316 | 281 | sema, | |
317 | items_with_candidate_name | 282 | current_crate, |
318 | .into_iter() | 283 | path_candidate.name.clone(), |
319 | .filter_map(|item| { | 284 | AssocItemSearch::Include, |
320 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | 285 | Some(DEFAULT_QUERY_SEARCH_LIMIT), |
321 | }) | 286 | ) |
322 | .collect() | 287 | .filter_map(|item| { |
288 | import_for_item( | ||
289 | sema.db, | ||
290 | mod_path, | ||
291 | unresolved_first_segment, | ||
292 | &unresolved_qualifier, | ||
293 | item, | ||
294 | ) | ||
295 | }) | ||
296 | .collect() | ||
297 | } | ||
298 | } | ||
323 | } | 299 | } |
324 | 300 | ||
325 | fn import_for_item( | 301 | fn import_for_item( |
@@ -434,25 +410,31 @@ fn module_with_segment_name( | |||
434 | } | 410 | } |
435 | 411 | ||
436 | fn trait_applicable_items( | 412 | fn trait_applicable_items( |
437 | db: &RootDatabase, | 413 | sema: &Semantics<RootDatabase>, |
438 | current_crate: Crate, | 414 | current_crate: Crate, |
439 | trait_candidate: &TraitImportCandidate, | 415 | trait_candidate: &TraitImportCandidate, |
440 | trait_assoc_item: bool, | 416 | trait_assoc_item: bool, |
441 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | 417 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, |
442 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
443 | ) -> FxHashSet<LocatedImport> { | 418 | ) -> FxHashSet<LocatedImport> { |
444 | let _p = profile::span("import_assets::trait_applicable_items"); | 419 | let _p = profile::span("import_assets::trait_applicable_items"); |
445 | let mut required_assoc_items = FxHashSet::default(); | ||
446 | 420 | ||
447 | let trait_candidates = items_with_candidate_name | 421 | let db = sema.db; |
448 | .into_iter() | 422 | |
449 | .filter_map(|input| item_as_assoc(db, input)) | 423 | let mut required_assoc_items = FxHashSet::default(); |
450 | .filter_map(|assoc| { | 424 | let trait_candidates = items_locator::items_with_name( |
451 | let assoc_item_trait = assoc.containing_trait(db)?; | 425 | sema, |
452 | required_assoc_items.insert(assoc); | 426 | current_crate, |
453 | Some(assoc_item_trait.into()) | 427 | trait_candidate.assoc_item_name.clone(), |
454 | }) | 428 | AssocItemSearch::AssocItemsOnly, |
455 | .collect(); | 429 | Some(DEFAULT_QUERY_SEARCH_LIMIT), |
430 | ) | ||
431 | .filter_map(|input| item_as_assoc(db, input)) | ||
432 | .filter_map(|assoc| { | ||
433 | let assoc_item_trait = assoc.containing_trait(db)?; | ||
434 | required_assoc_items.insert(assoc); | ||
435 | Some(assoc_item_trait.into()) | ||
436 | }) | ||
437 | .collect(); | ||
456 | 438 | ||
457 | let mut located_imports = FxHashSet::default(); | 439 | let mut located_imports = FxHashSet::default(); |
458 | 440 | ||
@@ -561,10 +543,6 @@ impl ImportCandidate { | |||
561 | ) -> Option<Self> { | 543 | ) -> Option<Self> { |
562 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) | 544 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
563 | } | 545 | } |
564 | |||
565 | fn is_trait_candidate(&self) -> bool { | ||
566 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) | ||
567 | } | ||
568 | } | 546 | } |
569 | 547 | ||
570 | fn path_import_candidate( | 548 | fn path_import_candidate( |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 9e0cb91c3..37acf95f0 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -14,10 +14,12 @@ use syntax::{ | |||
14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | 14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | pub use hir::PrefixKind; | ||
18 | |||
17 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 19 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
18 | pub struct InsertUseConfig { | 20 | pub struct InsertUseConfig { |
19 | pub merge: Option<MergeBehavior>, | 21 | pub merge: Option<MergeBehavior>, |
20 | pub prefix_kind: hir::PrefixKind, | 22 | pub prefix_kind: PrefixKind, |
21 | pub group: bool, | 23 | pub group: bool, |
22 | } | 24 | } |
23 | 25 | ||
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. | ||
4 | use either::Either; | 5 | use either::Either; |
5 | use hir::{ | 6 | use hir::{ |
6 | import_map::{self, ImportKind}, | 7 | import_map::{self, ImportKind}, |
@@ -10,122 +11,121 @@ use syntax::{ast, AstNode, SyntaxKind::NAME}; | |||
10 | 11 | ||
11 | use crate::{ | 12 | use 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 | }; |
16 | use rustc_hash::FxHashSet; | ||
17 | |||
18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; | ||
19 | 18 | ||
20 | pub fn with_exact_name( | 19 | /// A value to use, when uncertain which limit to pick. |
21 | sema: &Semantics<'_, RootDatabase>, | 20 | pub 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)] | ||
44 | pub enum AssocItemSearch { | 24 | pub 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 | ||
50 | pub fn with_similar_name( | 33 | /// Searches for importable items with the given name in the crate and its dependencies. |
51 | sema: &Semantics<'_, RootDatabase>, | 34 | pub 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 | ||
90 | fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { | 96 | fn 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 | |||
96 | fn 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 | ||
131 | fn get_name_definition( | 131 | fn 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 | |||
148 | fn 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 | } | ||
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 9ed9568ce..35e382b5c 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs | |||
@@ -52,6 +52,7 @@ pub struct Query { | |||
52 | only_types: bool, | 52 | only_types: bool, |
53 | libs: bool, | 53 | libs: bool, |
54 | exact: bool, | 54 | exact: bool, |
55 | case_sensitive: bool, | ||
55 | limit: usize, | 56 | limit: usize, |
56 | } | 57 | } |
57 | 58 | ||
@@ -64,6 +65,7 @@ impl Query { | |||
64 | only_types: false, | 65 | only_types: false, |
65 | libs: false, | 66 | libs: false, |
66 | exact: false, | 67 | exact: false, |
68 | case_sensitive: false, | ||
67 | limit: usize::max_value(), | 69 | limit: usize::max_value(), |
68 | } | 70 | } |
69 | } | 71 | } |
@@ -80,6 +82,10 @@ impl Query { | |||
80 | self.exact = true; | 82 | self.exact = true; |
81 | } | 83 | } |
82 | 84 | ||
85 | pub fn case_sensitive(&mut self) { | ||
86 | self.case_sensitive = true; | ||
87 | } | ||
88 | |||
83 | pub fn limit(&mut self, limit: usize) { | 89 | pub fn limit(&mut self, limit: usize) { |
84 | self.limit = limit | 90 | self.limit = limit |
85 | } | 91 | } |
@@ -326,8 +332,14 @@ impl Query { | |||
326 | if self.only_types && !symbol.kind.is_type() { | 332 | if self.only_types && !symbol.kind.is_type() { |
327 | continue; | 333 | continue; |
328 | } | 334 | } |
329 | if self.exact && symbol.name != self.query { | 335 | if self.exact { |
330 | continue; | 336 | if symbol.name != self.query { |
337 | continue; | ||
338 | } | ||
339 | } else if self.case_sensitive { | ||
340 | if self.query.chars().any(|c| !symbol.name.contains(c)) { | ||
341 | continue; | ||
342 | } | ||
331 | } | 343 | } |
332 | 344 | ||
333 | res.push(symbol.clone()); | 345 | res.push(symbol.clone()); |