diff options
Diffstat (limited to 'crates/ide_db')
-rw-r--r-- | crates/ide_db/src/apply_change.rs | 1 | ||||
-rw-r--r-- | crates/ide_db/src/call_info.rs | 14 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 212 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 8 | ||||
-rw-r--r-- | crates/ide_db/src/items_locator.rs | 178 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 170 | ||||
-rw-r--r-- | crates/ide_db/src/symbol_index.rs | 16 |
7 files changed, 313 insertions, 286 deletions
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 9b590d919..047a9b6bc 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -181,6 +181,7 @@ impl RootDatabase { | |||
181 | hir::db::GenericPredicatesQuery | 181 | hir::db::GenericPredicatesQuery |
182 | hir::db::GenericDefaultsQuery | 182 | hir::db::GenericDefaultsQuery |
183 | hir::db::InherentImplsInCrateQuery | 183 | hir::db::InherentImplsInCrateQuery |
184 | hir::db::TraitEnvironmentQuery | ||
184 | hir::db::TraitImplsInCrateQuery | 185 | hir::db::TraitImplsInCrateQuery |
185 | hir::db::TraitImplsInDepsQuery | 186 | hir::db::TraitImplsInDepsQuery |
186 | hir::db::AssociatedTyDataQuery | 187 | hir::db::AssociatedTyDataQuery |
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 7c8844e95..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,129 +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 | let mut mod_path = mod_path(item)?; | 258 | path_candidate.name.clone(), |
308 | if let Some(assoc_item) = item_as_assoc(db, item) { | 259 | // FIXME: we could look up assoc items by the input and propose those in completion, |
309 | mod_path.push_segment(assoc_item.name(db)?); | 260 | // but that requries more preparation first: |
310 | } | 261 | // * store non-trait assoc items in import_map to fully enable this lookup |
311 | Some(LocatedImport::new(mod_path.clone(), item, item, Some(mod_path))) | 262 | // * ensure that does not degrade the performance (bencmark it) |
312 | }) | 263 | // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now) |
313 | .collect(); | 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() | ||
314 | } | 275 | } |
315 | Some(first_segment_unresolved) => ( | 276 | Some(first_segment_unresolved) => { |
316 | first_segment_unresolved.fist_segment.to_string(), | 277 | let unresolved_qualifier = |
317 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), | 278 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier); |
318 | ), | 279 | let unresolved_first_segment = first_segment_unresolved.fist_segment.text(); |
319 | }; | 280 | items_locator::items_with_name( |
320 | 281 | sema, | |
321 | items_with_candidate_name | 282 | current_crate, |
322 | .into_iter() | 283 | path_candidate.name.clone(), |
323 | .filter_map(|item| { | 284 | AssocItemSearch::Include, |
324 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | 285 | Some(DEFAULT_QUERY_SEARCH_LIMIT), |
325 | }) | 286 | ) |
326 | .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 | } | ||
327 | } | 299 | } |
328 | 300 | ||
329 | fn import_for_item( | 301 | fn import_for_item( |
@@ -438,25 +410,31 @@ fn module_with_segment_name( | |||
438 | } | 410 | } |
439 | 411 | ||
440 | fn trait_applicable_items( | 412 | fn trait_applicable_items( |
441 | db: &RootDatabase, | 413 | sema: &Semantics<RootDatabase>, |
442 | current_crate: Crate, | 414 | current_crate: Crate, |
443 | trait_candidate: &TraitImportCandidate, | 415 | trait_candidate: &TraitImportCandidate, |
444 | trait_assoc_item: bool, | 416 | trait_assoc_item: bool, |
445 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | 417 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, |
446 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
447 | ) -> FxHashSet<LocatedImport> { | 418 | ) -> FxHashSet<LocatedImport> { |
448 | let _p = profile::span("import_assets::trait_applicable_items"); | 419 | let _p = profile::span("import_assets::trait_applicable_items"); |
449 | let mut required_assoc_items = FxHashSet::default(); | ||
450 | 420 | ||
451 | let trait_candidates = items_with_candidate_name | 421 | let db = sema.db; |
452 | .into_iter() | 422 | |
453 | .filter_map(|input| item_as_assoc(db, input)) | 423 | let mut required_assoc_items = FxHashSet::default(); |
454 | .filter_map(|assoc| { | 424 | let trait_candidates = items_locator::items_with_name( |
455 | let assoc_item_trait = assoc.containing_trait(db)?; | 425 | sema, |
456 | required_assoc_items.insert(assoc); | 426 | current_crate, |
457 | Some(assoc_item_trait.into()) | 427 | trait_candidate.assoc_item_name.clone(), |
458 | }) | 428 | AssocItemSearch::AssocItemsOnly, |
459 | .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(); | ||
460 | 438 | ||
461 | let mut located_imports = FxHashSet::default(); | 439 | let mut located_imports = FxHashSet::default(); |
462 | 440 | ||
@@ -565,10 +543,6 @@ impl ImportCandidate { | |||
565 | ) -> Option<Self> { | 543 | ) -> Option<Self> { |
566 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) | 544 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
567 | } | 545 | } |
568 | |||
569 | fn is_trait_candidate(&self) -> bool { | ||
570 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) | ||
571 | } | ||
572 | } | 546 | } |
573 | 547 | ||
574 | 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..20c195f82 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 | ||
@@ -211,7 +213,7 @@ pub fn try_merge_imports( | |||
211 | let lhs_tree = lhs.use_tree()?; | 213 | let lhs_tree = lhs.use_tree()?; |
212 | let rhs_tree = rhs.use_tree()?; | 214 | let rhs_tree = rhs.use_tree()?; |
213 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; | 215 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behavior)?; |
214 | Some(lhs.with_use_tree(merged)) | 216 | Some(lhs.with_use_tree(merged).clone_for_update()) |
215 | } | 217 | } |
216 | 218 | ||
217 | pub fn try_merge_trees( | 219 | pub fn try_merge_trees( |
@@ -232,7 +234,7 @@ pub fn try_merge_trees( | |||
232 | } else { | 234 | } else { |
233 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) | 235 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) |
234 | }; | 236 | }; |
235 | recursive_merge(&lhs, &rhs, merge) | 237 | recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update()) |
236 | } | 238 | } |
237 | 239 | ||
238 | /// Recursively "zips" together lhs and rhs. | 240 | /// Recursively "zips" together lhs and rhs. |
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/search.rs b/crates/ide_db/src/search.rs index 324817cd0..3634b2b26 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | use std::{convert::TryInto, mem}; | 7 | use std::{convert::TryInto, mem}; |
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; |
10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; | 10 | use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility}; |
11 | use once_cell::unsync::Lazy; | 11 | use once_cell::unsync::Lazy; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
@@ -78,6 +78,76 @@ impl SearchScope { | |||
78 | SearchScope { entries } | 78 | SearchScope { entries } |
79 | } | 79 | } |
80 | 80 | ||
81 | fn crate_graph(db: &RootDatabase) -> SearchScope { | ||
82 | let mut entries = FxHashMap::default(); | ||
83 | |||
84 | let graph = db.crate_graph(); | ||
85 | for krate in graph.iter() { | ||
86 | let root_file = graph[krate].root_file_id; | ||
87 | let source_root_id = db.file_source_root(root_file); | ||
88 | let source_root = db.source_root(source_root_id); | ||
89 | entries.extend(source_root.iter().map(|id| (id, None))); | ||
90 | } | ||
91 | SearchScope { entries } | ||
92 | } | ||
93 | |||
94 | fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope { | ||
95 | let mut entries = FxHashMap::default(); | ||
96 | for rev_dep in of.transitive_reverse_dependencies(db) { | ||
97 | let root_file = rev_dep.root_file(db); | ||
98 | let source_root_id = db.file_source_root(root_file); | ||
99 | let source_root = db.source_root(source_root_id); | ||
100 | entries.extend(source_root.iter().map(|id| (id, None))); | ||
101 | } | ||
102 | SearchScope { entries } | ||
103 | } | ||
104 | |||
105 | fn krate(db: &RootDatabase, of: hir::Crate) -> SearchScope { | ||
106 | let root_file = of.root_file(db); | ||
107 | let source_root_id = db.file_source_root(root_file); | ||
108 | let source_root = db.source_root(source_root_id); | ||
109 | SearchScope { | ||
110 | entries: source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(), | ||
111 | } | ||
112 | } | ||
113 | |||
114 | fn module(db: &RootDatabase, module: hir::Module) -> SearchScope { | ||
115 | let mut entries = FxHashMap::default(); | ||
116 | |||
117 | let mut to_visit = vec![module]; | ||
118 | let mut is_first = true; | ||
119 | while let Some(module) = to_visit.pop() { | ||
120 | let src = module.definition_source(db); | ||
121 | let file_id = src.file_id.original_file(db); | ||
122 | match src.value { | ||
123 | ModuleSource::Module(m) => { | ||
124 | if is_first { | ||
125 | let range = Some(m.syntax().text_range()); | ||
126 | entries.insert(file_id, range); | ||
127 | } else { | ||
128 | // We have already added the enclosing file to the search scope, | ||
129 | // so do nothing. | ||
130 | } | ||
131 | } | ||
132 | ModuleSource::BlockExpr(b) => { | ||
133 | if is_first { | ||
134 | let range = Some(b.syntax().text_range()); | ||
135 | entries.insert(file_id, range); | ||
136 | } else { | ||
137 | // We have already added the enclosing file to the search scope, | ||
138 | // so do nothing. | ||
139 | } | ||
140 | } | ||
141 | ModuleSource::SourceFile(_) => { | ||
142 | entries.insert(file_id, None); | ||
143 | } | ||
144 | }; | ||
145 | is_first = false; | ||
146 | to_visit.extend(module.children(db)); | ||
147 | } | ||
148 | SearchScope { entries } | ||
149 | } | ||
150 | |||
81 | pub fn empty() -> SearchScope { | 151 | pub fn empty() -> SearchScope { |
82 | SearchScope::new(FxHashMap::default()) | 152 | SearchScope::new(FxHashMap::default()) |
83 | } | 153 | } |
@@ -140,24 +210,15 @@ impl Definition { | |||
140 | let _p = profile::span("search_scope"); | 210 | let _p = profile::span("search_scope"); |
141 | 211 | ||
142 | if let Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) = self { | 212 | if let Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) = self { |
143 | let mut res = FxHashMap::default(); | 213 | return SearchScope::crate_graph(db); |
144 | |||
145 | let graph = db.crate_graph(); | ||
146 | for krate in graph.iter() { | ||
147 | let root_file = graph[krate].root_file_id; | ||
148 | let source_root_id = db.file_source_root(root_file); | ||
149 | let source_root = db.source_root(source_root_id); | ||
150 | res.extend(source_root.iter().map(|id| (id, None))); | ||
151 | } | ||
152 | return SearchScope::new(res); | ||
153 | } | 214 | } |
154 | 215 | ||
155 | let module = match self.module(db) { | 216 | let module = match self.module(db) { |
156 | Some(it) => it, | 217 | Some(it) => it, |
157 | None => return SearchScope::empty(), | 218 | None => return SearchScope::empty(), |
158 | }; | 219 | }; |
159 | let module_src = module.definition_source(db); | 220 | let InFile { file_id, value: module_source } = module.definition_source(db); |
160 | let file_id = module_src.file_id.original_file(db); | 221 | let file_id = file_id.original_file(db); |
161 | 222 | ||
162 | if let Definition::Local(var) = self { | 223 | if let Definition::Local(var) = self { |
163 | let range = match var.parent(db) { | 224 | let range = match var.parent(db) { |
@@ -165,9 +226,10 @@ impl Definition { | |||
165 | DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()), | 226 | DefWithBody::Const(c) => c.source(db).map(|src| src.value.syntax().text_range()), |
166 | DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()), | 227 | DefWithBody::Static(s) => s.source(db).map(|src| src.value.syntax().text_range()), |
167 | }; | 228 | }; |
168 | let mut res = FxHashMap::default(); | 229 | return match range { |
169 | res.insert(file_id, range); | 230 | Some(range) => SearchScope::file_range(FileRange { file_id, range }), |
170 | return SearchScope::new(res); | 231 | None => SearchScope::single_file(file_id), |
232 | }; | ||
171 | } | 233 | } |
172 | 234 | ||
173 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { | 235 | if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { |
@@ -198,75 +260,39 @@ impl Definition { | |||
198 | it.source(db).map(|src| src.value.syntax().text_range()) | 260 | it.source(db).map(|src| src.value.syntax().text_range()) |
199 | } | 261 | } |
200 | }; | 262 | }; |
201 | let mut res = FxHashMap::default(); | 263 | return match range { |
202 | res.insert(file_id, range); | 264 | Some(range) => SearchScope::file_range(FileRange { file_id, range }), |
203 | return SearchScope::new(res); | 265 | None => SearchScope::single_file(file_id), |
266 | }; | ||
204 | } | 267 | } |
205 | 268 | ||
206 | let vis = self.visibility(db); | 269 | if let Definition::Macro(macro_def) = self { |
207 | 270 | if macro_def.kind() == hir::MacroKind::Declarative { | |
208 | if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { | 271 | return if macro_def.attrs(db).by_key("macro_export").exists() { |
209 | let module: Module = module.into(); | 272 | SearchScope::reverse_dependencies(db, module.krate()) |
210 | let mut res = FxHashMap::default(); | 273 | } else { |
211 | 274 | SearchScope::krate(db, module.krate()) | |
212 | let mut to_visit = vec![module]; | ||
213 | let mut is_first = true; | ||
214 | while let Some(module) = to_visit.pop() { | ||
215 | let src = module.definition_source(db); | ||
216 | let file_id = src.file_id.original_file(db); | ||
217 | match src.value { | ||
218 | ModuleSource::Module(m) => { | ||
219 | if is_first { | ||
220 | let range = Some(m.syntax().text_range()); | ||
221 | res.insert(file_id, range); | ||
222 | } else { | ||
223 | // We have already added the enclosing file to the search scope, | ||
224 | // so do nothing. | ||
225 | } | ||
226 | } | ||
227 | ModuleSource::BlockExpr(b) => { | ||
228 | if is_first { | ||
229 | let range = Some(b.syntax().text_range()); | ||
230 | res.insert(file_id, range); | ||
231 | } else { | ||
232 | // We have already added the enclosing file to the search scope, | ||
233 | // so do nothing. | ||
234 | } | ||
235 | } | ||
236 | ModuleSource::SourceFile(_) => { | ||
237 | res.insert(file_id, None); | ||
238 | } | ||
239 | }; | 275 | }; |
240 | is_first = false; | ||
241 | to_visit.extend(module.children(db)); | ||
242 | } | 276 | } |
243 | |||
244 | return SearchScope::new(res); | ||
245 | } | 277 | } |
246 | 278 | ||
279 | let vis = self.visibility(db); | ||
247 | if let Some(Visibility::Public) = vis { | 280 | if let Some(Visibility::Public) = vis { |
248 | let source_root_id = db.file_source_root(file_id); | 281 | return SearchScope::reverse_dependencies(db, module.krate()); |
249 | let source_root = db.source_root(source_root_id); | 282 | } |
250 | let mut res = source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(); | 283 | if let Some(Visibility::Module(module)) = vis { |
251 | 284 | return SearchScope::module(db, module.into()); | |
252 | let krate = module.krate(); | ||
253 | for rev_dep in krate.transitive_reverse_dependencies(db) { | ||
254 | let root_file = rev_dep.root_file(db); | ||
255 | let source_root_id = db.file_source_root(root_file); | ||
256 | let source_root = db.source_root(source_root_id); | ||
257 | res.extend(source_root.iter().map(|id| (id, None))); | ||
258 | } | ||
259 | return SearchScope::new(res); | ||
260 | } | 285 | } |
261 | 286 | ||
262 | let mut res = FxHashMap::default(); | 287 | let range = match module_source { |
263 | let range = match module_src.value { | ||
264 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | 288 | ModuleSource::Module(m) => Some(m.syntax().text_range()), |
265 | ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()), | 289 | ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()), |
266 | ModuleSource::SourceFile(_) => None, | 290 | ModuleSource::SourceFile(_) => None, |
267 | }; | 291 | }; |
268 | res.insert(file_id, range); | 292 | match range { |
269 | SearchScope::new(res) | 293 | Some(range) => SearchScope::file_range(FileRange { file_id, range }), |
294 | None => SearchScope::single_file(file_id), | ||
295 | } | ||
270 | } | 296 | } |
271 | 297 | ||
272 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { | 298 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { |
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()); |