diff options
-rw-r--r-- | crates/completion/src/completions/flyimport.rs | 149 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 30 | ||||
-rw-r--r-- | crates/ide_db/src/imports_locator.rs | 16 |
3 files changed, 168 insertions, 27 deletions
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs index 9101e405c..a40843669 100644 --- a/crates/completion/src/completions/flyimport.rs +++ b/crates/completion/src/completions/flyimport.rs | |||
@@ -20,11 +20,14 @@ | |||
20 | //! # pub mod std { pub mod marker { pub struct PhantomData { } } } | 20 | //! # pub mod std { pub mod marker { pub struct PhantomData { } } } |
21 | //! ``` | 21 | //! ``` |
22 | //! | 22 | //! |
23 | //! Also completes associated items, that require trait imports. | ||
24 | //! | ||
23 | //! .Fuzzy search details | 25 | //! .Fuzzy search details |
24 | //! | 26 | //! |
25 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only | 27 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only |
26 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). | 28 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). |
27 | //! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols. | 29 | //! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols |
30 | //! (but shows all associated items for any input length). | ||
28 | //! | 31 | //! |
29 | //! .Import configuration | 32 | //! .Import configuration |
30 | //! | 33 | //! |
@@ -46,8 +49,11 @@ | |||
46 | //! capability enabled. | 49 | //! capability enabled. |
47 | 50 | ||
48 | use hir::{ModPath, ScopeDef}; | 51 | use hir::{ModPath, ScopeDef}; |
49 | use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope}; | 52 | use ide_db::helpers::{ |
50 | use syntax::AstNode; | 53 | import_assets::{ImportAssets, ImportCandidate}, |
54 | insert_use::ImportScope, | ||
55 | }; | ||
56 | use syntax::{AstNode, SyntaxNode, T}; | ||
51 | use test_utils::mark; | 57 | use test_utils::mark; |
52 | 58 | ||
53 | use crate::{ | 59 | use crate::{ |
@@ -65,16 +71,24 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
65 | if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { | 71 | if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { |
66 | return None; | 72 | return None; |
67 | } | 73 | } |
68 | let potential_import_name = ctx.token.to_string(); | 74 | let potential_import_name = { |
69 | if potential_import_name.len() < 2 { | 75 | let token_kind = ctx.token.kind(); |
70 | return None; | 76 | if token_kind == T![.] || token_kind == T![::] { |
71 | } | 77 | String::new() |
78 | } else { | ||
79 | ctx.token.to_string() | ||
80 | } | ||
81 | }; | ||
82 | |||
72 | let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); | 83 | let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); |
73 | 84 | ||
74 | let import_scope = | ||
75 | ImportScope::find_insert_use_container(ctx.name_ref_syntax.as_ref()?.syntax(), &ctx.sema)?; | ||
76 | let user_input_lowercased = potential_import_name.to_lowercase(); | 85 | let user_input_lowercased = potential_import_name.to_lowercase(); |
77 | let mut all_mod_paths = import_assets(ctx, potential_import_name)? | 86 | let import_assets = import_assets(ctx, potential_import_name)?; |
87 | let import_scope = ImportScope::find_insert_use_container( | ||
88 | position_for_import(ctx, import_assets.import_candidate())?, | ||
89 | &ctx.sema, | ||
90 | )?; | ||
91 | let mut all_mod_paths = import_assets | ||
78 | .search_for_relative_paths(&ctx.sema) | 92 | .search_for_relative_paths(&ctx.sema) |
79 | .into_iter() | 93 | .into_iter() |
80 | .map(|(mod_path, item_in_ns)| { | 94 | .map(|(mod_path, item_in_ns)| { |
@@ -108,6 +122,17 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
108 | Some(()) | 122 | Some(()) |
109 | } | 123 | } |
110 | 124 | ||
125 | fn position_for_import<'a>( | ||
126 | ctx: &'a CompletionContext, | ||
127 | import_candidate: &ImportCandidate, | ||
128 | ) -> Option<&'a SyntaxNode> { | ||
129 | Some(match import_candidate { | ||
130 | ImportCandidate::Path(_) => ctx.name_ref_syntax.as_ref()?.syntax(), | ||
131 | ImportCandidate::TraitAssocItem(_) => ctx.path_qual.as_ref()?.syntax(), | ||
132 | ImportCandidate::TraitMethod(_) => ctx.dot_receiver.as_ref()?.syntax(), | ||
133 | }) | ||
134 | } | ||
135 | |||
111 | fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { | 136 | fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { |
112 | let current_module = ctx.scope.module()?; | 137 | let current_module = ctx.scope.module()?; |
113 | if let Some(dot_receiver) = &ctx.dot_receiver { | 138 | if let Some(dot_receiver) = &ctx.dot_receiver { |
@@ -117,7 +142,22 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
117 | fuzzy_name, | 142 | fuzzy_name, |
118 | ) | 143 | ) |
119 | } else { | 144 | } else { |
120 | ImportAssets::for_fuzzy_path(current_module, ctx.path_qual.clone(), fuzzy_name, &ctx.sema) | 145 | let fuzzy_name_length = fuzzy_name.len(); |
146 | let assets_for_path = ImportAssets::for_fuzzy_path( | ||
147 | current_module, | ||
148 | ctx.path_qual.clone(), | ||
149 | fuzzy_name, | ||
150 | &ctx.sema, | ||
151 | ); | ||
152 | |||
153 | if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) | ||
154 | && fuzzy_name_length < 2 | ||
155 | { | ||
156 | mark::hit!(ignore_short_input_for_path); | ||
157 | None | ||
158 | } else { | ||
159 | assets_for_path | ||
160 | } | ||
121 | } | 161 | } |
122 | } | 162 | } |
123 | 163 | ||
@@ -233,6 +273,30 @@ fn main() { | |||
233 | } | 273 | } |
234 | 274 | ||
235 | #[test] | 275 | #[test] |
276 | fn short_paths_are_ignored() { | ||
277 | mark::check!(ignore_short_input_for_path); | ||
278 | |||
279 | check( | ||
280 | r#" | ||
281 | //- /lib.rs crate:dep | ||
282 | pub struct FirstStruct; | ||
283 | pub mod some_module { | ||
284 | pub struct SecondStruct; | ||
285 | pub struct ThirdStruct; | ||
286 | } | ||
287 | |||
288 | //- /main.rs crate:main deps:dep | ||
289 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
290 | |||
291 | fn main() { | ||
292 | t$0 | ||
293 | } | ||
294 | "#, | ||
295 | expect![[r#""#]], | ||
296 | ); | ||
297 | } | ||
298 | |||
299 | #[test] | ||
236 | fn fuzzy_completions_come_in_specific_order() { | 300 | fn fuzzy_completions_come_in_specific_order() { |
237 | mark::check!(certain_fuzzy_order_test); | 301 | mark::check!(certain_fuzzy_order_test); |
238 | check( | 302 | check( |
@@ -524,4 +588,67 @@ fn main() { | |||
524 | "#]], | 588 | "#]], |
525 | ); | 589 | ); |
526 | } | 590 | } |
591 | |||
592 | #[test] | ||
593 | fn zero_input_assoc_item_completion() { | ||
594 | check( | ||
595 | r#" | ||
596 | //- /lib.rs crate:dep | ||
597 | pub mod test_mod { | ||
598 | pub trait TestTrait { | ||
599 | const SPECIAL_CONST: u8; | ||
600 | type HumbleType; | ||
601 | fn weird_function(); | ||
602 | fn random_method(&self); | ||
603 | } | ||
604 | pub struct TestStruct {} | ||
605 | impl TestTrait for TestStruct { | ||
606 | const SPECIAL_CONST: u8 = 42; | ||
607 | type HumbleType = (); | ||
608 | fn weird_function() {} | ||
609 | fn random_method(&self) {} | ||
610 | } | ||
611 | } | ||
612 | |||
613 | //- /main.rs crate:main deps:dep | ||
614 | fn main() { | ||
615 | let test_struct = dep::test_mod::TestStruct {}; | ||
616 | test_struct.$0 | ||
617 | } | ||
618 | "#, | ||
619 | expect![[r#" | ||
620 | me random_method() (dep::test_mod::TestTrait) fn random_method(&self) | ||
621 | "#]], | ||
622 | ); | ||
623 | |||
624 | check( | ||
625 | r#" | ||
626 | //- /lib.rs crate:dep | ||
627 | pub mod test_mod { | ||
628 | pub trait TestTrait { | ||
629 | const SPECIAL_CONST: u8; | ||
630 | type HumbleType; | ||
631 | fn weird_function(); | ||
632 | fn random_method(&self); | ||
633 | } | ||
634 | pub struct TestStruct {} | ||
635 | impl TestTrait for TestStruct { | ||
636 | const SPECIAL_CONST: u8 = 42; | ||
637 | type HumbleType = (); | ||
638 | fn weird_function() {} | ||
639 | fn random_method(&self) {} | ||
640 | } | ||
641 | } | ||
642 | |||
643 | //- /main.rs crate:main deps:dep | ||
644 | fn main() { | ||
645 | dep::test_mod::TestStruct::$0 | ||
646 | } | ||
647 | "#, | ||
648 | expect![[r#" | ||
649 | ct SPECIAL_CONST (dep::test_mod::TestTrait) | ||
650 | fn weird_function() (dep::test_mod::TestTrait) fn weird_function() | ||
651 | "#]], | ||
652 | ); | ||
653 | } | ||
527 | } | 654 | } |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index e284220c1..a080b6ca7 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -5,7 +5,7 @@ use rustc_hash::FxHashSet; | |||
5 | use syntax::{ast, AstNode}; | 5 | use syntax::{ast, AstNode}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | imports_locator::{self, AssocItemSearch}, | 8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, |
9 | RootDatabase, | 9 | RootDatabase, |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -173,6 +173,7 @@ impl ImportAssets { | |||
173 | let current_crate = self.module_with_candidate.krate(); | 173 | let current_crate = self.module_with_candidate.krate(); |
174 | 174 | ||
175 | let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { | 175 | let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { |
176 | // TODO kb process all traits at once instead? | ||
176 | trait_candidates.clear(); | 177 | trait_candidates.clear(); |
177 | match &self.import_candidate { | 178 | match &self.import_candidate { |
178 | ImportCandidate::TraitAssocItem(trait_candidate) => { | 179 | ImportCandidate::TraitAssocItem(trait_candidate) => { |
@@ -191,6 +192,11 @@ impl ImportAssets { | |||
191 | None, | 192 | None, |
192 | |_, assoc| { | 193 | |_, assoc| { |
193 | if canidate_assoc_item == assoc { | 194 | if canidate_assoc_item == assoc { |
195 | if let AssocItem::Function(f) = assoc { | ||
196 | if f.self_param(db).is_some() { | ||
197 | return None; | ||
198 | } | ||
199 | } | ||
194 | Some(assoc_to_module_def(assoc)) | 200 | Some(assoc_to_module_def(assoc)) |
195 | } else { | 201 | } else { |
196 | None | 202 | None |
@@ -238,17 +244,21 @@ impl ImportAssets { | |||
238 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 | 244 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 |
239 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 245 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
240 | // for the details | 246 | // for the details |
241 | NameToImport::Fuzzy(fuzzy_name) => imports_locator::find_similar_imports( | 247 | NameToImport::Fuzzy(fuzzy_name) => { |
242 | sema, | 248 | let (assoc_item_search, limit) = match self.import_candidate { |
243 | current_crate, | ||
244 | fuzzy_name.clone(), | ||
245 | match self.import_candidate { | ||
246 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 249 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { |
247 | AssocItemSearch::AssocItemsOnly | 250 | (AssocItemSearch::AssocItemsOnly, None) |
248 | } | 251 | } |
249 | _ => AssocItemSearch::Exclude, | 252 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), |
250 | }, | 253 | }; |
251 | ), | 254 | imports_locator::find_similar_imports( |
255 | sema, | ||
256 | current_crate, | ||
257 | fuzzy_name.clone(), | ||
258 | assoc_item_search, | ||
259 | limit, | ||
260 | ) | ||
261 | } | ||
252 | }; | 262 | }; |
253 | 263 | ||
254 | let mut res = unfiltered_imports | 264 | let mut res = unfiltered_imports |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index d69e65960..502e8281a 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | use either::Either; | 15 | use either::Either; |
16 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
17 | 17 | ||
18 | const QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
19 | 19 | ||
20 | pub fn find_exact_imports<'a>( | 20 | pub fn find_exact_imports<'a>( |
21 | sema: &Semantics<'a, RootDatabase>, | 21 | sema: &Semantics<'a, RootDatabase>, |
@@ -29,11 +29,11 @@ pub fn find_exact_imports<'a>( | |||
29 | { | 29 | { |
30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); | 30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); |
31 | local_query.exact(); | 31 | local_query.exact(); |
32 | local_query.limit(QUERY_SEARCH_LIMIT); | 32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); |
33 | local_query | 33 | local_query |
34 | }, | 34 | }, |
35 | import_map::Query::new(name_to_import) | 35 | import_map::Query::new(name_to_import) |
36 | .limit(QUERY_SEARCH_LIMIT) | 36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) |
37 | .name_only() | 37 | .name_only() |
38 | .search_mode(import_map::SearchMode::Equals) | 38 | .search_mode(import_map::SearchMode::Equals) |
39 | .case_sensitive(), | 39 | .case_sensitive(), |
@@ -51,13 +51,13 @@ pub fn find_similar_imports<'a>( | |||
51 | krate: Crate, | 51 | krate: Crate, |
52 | fuzzy_search_string: String, | 52 | fuzzy_search_string: String, |
53 | assoc_item_search: AssocItemSearch, | 53 | assoc_item_search: AssocItemSearch, |
54 | limit: Option<usize>, | ||
54 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 55 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { |
55 | let _p = profile::span("find_similar_imports"); | 56 | let _p = profile::span("find_similar_imports"); |
56 | 57 | ||
57 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 58 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
58 | .search_mode(import_map::SearchMode::Fuzzy) | 59 | .search_mode(import_map::SearchMode::Fuzzy) |
59 | .name_only() | 60 | .name_only(); |
60 | .limit(QUERY_SEARCH_LIMIT); | ||
61 | 61 | ||
62 | match assoc_item_search { | 62 | match assoc_item_search { |
63 | AssocItemSearch::Include => {} | 63 | AssocItemSearch::Include => {} |
@@ -70,7 +70,11 @@ pub fn find_similar_imports<'a>( | |||
70 | } | 70 | } |
71 | 71 | ||
72 | let mut local_query = symbol_index::Query::new(fuzzy_search_string); | 72 | let mut local_query = symbol_index::Query::new(fuzzy_search_string); |
73 | local_query.limit(QUERY_SEARCH_LIMIT); | 73 | |
74 | if let Some(limit) = limit { | ||
75 | external_query = external_query.limit(limit); | ||
76 | local_query.limit(limit); | ||
77 | } | ||
74 | 78 | ||
75 | let db = sema.db; | 79 | let db = sema.db; |
76 | Box::new(find_imports(sema, krate, local_query, external_query).filter( | 80 | Box::new(find_imports(sema, krate, local_query, external_query).filter( |