diff options
Diffstat (limited to 'crates/completion/src/completions/flyimport.rs')
-rw-r--r-- | crates/completion/src/completions/flyimport.rs | 149 |
1 files changed, 138 insertions, 11 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 | } |