]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/flyimport.rs
Draft the qualifier import resolution
[rust.git] / crates / ide_completion / src / completions / flyimport.rs
1 //! Feature: completion with imports-on-the-fly
2 //!
3 //! When completing names in the current scope, proposes additional imports from other modules or crates,
4 //! if they can be qualified in the scope and their name contains all symbols from the completion input
5 //! (case-insensitive, in any order or places).
6 //!
7 //! ```
8 //! fn main() {
9 //!     pda$0
10 //! }
11 //! # pub mod std { pub mod marker { pub struct PhantomData { } } }
12 //! ```
13 //! ->
14 //! ```
15 //! use std::marker::PhantomData;
16 //!
17 //! fn main() {
18 //!     PhantomData
19 //! }
20 //! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21 //! ```
22 //!
23 //! Also completes associated items, that require trait imports.
24 //!
25 //! .Fuzzy search details
26 //!
27 //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
28 //! (i.e. in `HashMap` in the `std::collections::HashMap` path).
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).
31 //!
32 //! .Import configuration
33 //!
34 //! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
35 //! Mimics the corresponding behavior of the `Auto Import` feature.
36 //!
37 //! .LSP and performance implications
38 //!
39 //! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
40 //! (case sensitive) resolve client capability in its client capabilities.
41 //! This way the server is able to defer the costly computations, doing them for a selected completion item only.
42 //! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
43 //! which might be slow ergo the feature is automatically disabled.
44 //!
45 //! .Feature toggle
46 //!
47 //! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
48 //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49 //! capability enabled.
50
51 use hir::{AsAssocItem, ModPath, ScopeDef};
52 use ide_db::helpers::{
53     import_assets::{ImportAssets, ImportCandidate},
54     insert_use::ImportScope,
55 };
56 use rustc_hash::FxHashSet;
57 use syntax::{AstNode, SyntaxNode, T};
58
59 use crate::{
60     context::CompletionContext,
61     render::{render_resolution_with_import, RenderContext},
62     ImportEdit,
63 };
64
65 use super::Completions;
66
67 pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
68     if !ctx.config.enable_imports_on_the_fly {
69         return None;
70     }
71     if ctx.use_item_syntax.is_some()
72         || ctx.attribute_under_caret.is_some()
73         || ctx.mod_declaration_under_caret.is_some()
74     {
75         return None;
76     }
77     let potential_import_name = {
78         let token_kind = ctx.token.kind();
79         if matches!(token_kind, T![.] | T![::]) {
80             String::new()
81         } else {
82             ctx.token.to_string()
83         }
84     };
85
86     let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
87
88     let user_input_lowercased = potential_import_name.to_lowercase();
89     let import_assets = import_assets(ctx, potential_import_name)?;
90     let import_scope = ImportScope::find_insert_use_container(
91         position_for_import(ctx, Some(import_assets.import_candidate()))?,
92         &ctx.sema,
93     )?;
94
95     let scope_definitions = scope_definitions(ctx);
96     let mut all_mod_paths = import_assets
97         .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
98         .into_iter()
99         .map(|(mod_path, item_in_ns)| {
100             let scope_item = match item_in_ns {
101                 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
102                 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
103                 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
104             };
105             (mod_path, scope_item)
106         })
107         .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def))
108         .collect::<Vec<_>>();
109     all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
110         compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
111     });
112
113     acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
114         let import_for_trait_assoc_item = match definition {
115             ScopeDef::ModuleDef(module_def) => module_def
116                 .as_assoc_item(ctx.db)
117                 .and_then(|assoc| assoc.containing_trait(ctx.db))
118                 .is_some(),
119             _ => false,
120         };
121         let import_edit = ImportEdit {
122             import_path,
123             import_scope: import_scope.clone(),
124             import_for_trait_assoc_item,
125         };
126         render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
127     }));
128     Some(())
129 }
130
131 fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
132     let mut scope_definitions = FxHashSet::default();
133     ctx.scope.process_all_names(&mut |_, scope_def| {
134         scope_definitions.insert(scope_def);
135     });
136     scope_definitions
137 }
138
139 pub(crate) fn position_for_import<'a>(
140     ctx: &'a CompletionContext,
141     import_candidate: Option<&ImportCandidate>,
142 ) -> Option<&'a SyntaxNode> {
143     Some(match import_candidate {
144         Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
145         Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
146         Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
147         None => ctx
148             .name_ref_syntax
149             .as_ref()
150             .map(|name_ref| name_ref.syntax())
151             .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
152             .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
153     })
154 }
155
156 fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
157     let current_module = ctx.scope.module()?;
158     if let Some(dot_receiver) = &ctx.dot_receiver {
159         ImportAssets::for_fuzzy_method_call(
160             current_module,
161             ctx.sema.type_of_expr(dot_receiver)?,
162             fuzzy_name,
163         )
164     } else {
165         let fuzzy_name_length = fuzzy_name.len();
166         let assets_for_path = ImportAssets::for_fuzzy_path(
167             current_module,
168             ctx.path_qual.clone(),
169             fuzzy_name,
170             &ctx.sema,
171         )?;
172
173         if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
174             && fuzzy_name_length < 2
175         {
176             cov_mark::hit!(ignore_short_input_for_path);
177             None
178         } else {
179             Some(assets_for_path)
180         }
181     }
182 }
183
184 fn compute_fuzzy_completion_order_key(
185     proposed_mod_path: &ModPath,
186     user_input_lowercased: &str,
187 ) -> usize {
188     cov_mark::hit!(certain_fuzzy_order_test);
189     let proposed_import_name = match proposed_mod_path.segments().last() {
190         Some(name) => name.to_string().to_lowercase(),
191         None => return usize::MAX,
192     };
193     match proposed_import_name.match_indices(user_input_lowercased).next() {
194         Some((first_matching_index, _)) => first_matching_index,
195         None => usize::MAX,
196     }
197 }
198
199 #[cfg(test)]
200 mod tests {
201     use expect_test::{expect, Expect};
202
203     use crate::{
204         item::CompletionKind,
205         test_utils::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
206     };
207
208     fn check(ra_fixture: &str, expect: Expect) {
209         let actual = completion_list(ra_fixture, CompletionKind::Magic);
210         expect.assert_eq(&actual);
211     }
212
213     #[test]
214     fn function_fuzzy_completion() {
215         check_edit(
216             "stdin",
217             r#"
218 //- /lib.rs crate:dep
219 pub mod io {
220     pub fn stdin() {}
221 };
222
223 //- /main.rs crate:main deps:dep
224 fn main() {
225     stdi$0
226 }
227 "#,
228             r#"
229 use dep::io::stdin;
230
231 fn main() {
232     stdin()$0
233 }
234 "#,
235         );
236     }
237
238     #[test]
239     fn macro_fuzzy_completion() {
240         check_edit(
241             "macro_with_curlies!",
242             r#"
243 //- /lib.rs crate:dep
244 /// Please call me as macro_with_curlies! {}
245 #[macro_export]
246 macro_rules! macro_with_curlies {
247     () => {}
248 }
249
250 //- /main.rs crate:main deps:dep
251 fn main() {
252     curli$0
253 }
254 "#,
255             r#"
256 use dep::macro_with_curlies;
257
258 fn main() {
259     macro_with_curlies! {$0}
260 }
261 "#,
262         );
263     }
264
265     #[test]
266     fn struct_fuzzy_completion() {
267         check_edit(
268             "ThirdStruct",
269             r#"
270 //- /lib.rs crate:dep
271 pub struct FirstStruct;
272 pub mod some_module {
273     pub struct SecondStruct;
274     pub struct ThirdStruct;
275 }
276
277 //- /main.rs crate:main deps:dep
278 use dep::{FirstStruct, some_module::SecondStruct};
279
280 fn main() {
281     this$0
282 }
283 "#,
284             r#"
285 use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
286
287 fn main() {
288     ThirdStruct
289 }
290 "#,
291         );
292     }
293
294     #[test]
295     fn short_paths_are_ignored() {
296         cov_mark::check!(ignore_short_input_for_path);
297
298         check(
299             r#"
300 //- /lib.rs crate:dep
301 pub struct FirstStruct;
302 pub mod some_module {
303     pub struct SecondStruct;
304     pub struct ThirdStruct;
305 }
306
307 //- /main.rs crate:main deps:dep
308 use dep::{FirstStruct, some_module::SecondStruct};
309
310 fn main() {
311     t$0
312 }
313 "#,
314             expect![[r#""#]],
315         );
316     }
317
318     #[test]
319     fn fuzzy_completions_come_in_specific_order() {
320         cov_mark::check!(certain_fuzzy_order_test);
321         check(
322             r#"
323 //- /lib.rs crate:dep
324 pub struct FirstStruct;
325 pub mod some_module {
326     // already imported, omitted
327     pub struct SecondStruct;
328     // does not contain all letters from the query, omitted
329     pub struct UnrelatedOne;
330     // contains all letters from the query, but not in sequence, displayed last
331     pub struct ThiiiiiirdStruct;
332     // contains all letters from the query, but not in the beginning, displayed second
333     pub struct AfterThirdStruct;
334     // contains all letters from the query in the begginning, displayed first
335     pub struct ThirdStruct;
336 }
337
338 //- /main.rs crate:main deps:dep
339 use dep::{FirstStruct, some_module::SecondStruct};
340
341 fn main() {
342     hir$0
343 }
344 "#,
345             expect![[r#"
346                 st dep::some_module::ThirdStruct
347                 st dep::some_module::AfterThirdStruct
348                 st dep::some_module::ThiiiiiirdStruct
349             "#]],
350         );
351     }
352
353     #[test]
354     fn trait_function_fuzzy_completion() {
355         let fixture = r#"
356         //- /lib.rs crate:dep
357         pub mod test_mod {
358             pub trait TestTrait {
359                 const SPECIAL_CONST: u8;
360                 type HumbleType;
361                 fn weird_function();
362                 fn random_method(&self);
363             }
364             pub struct TestStruct {}
365             impl TestTrait for TestStruct {
366                 const SPECIAL_CONST: u8 = 42;
367                 type HumbleType = ();
368                 fn weird_function() {}
369                 fn random_method(&self) {}
370             }
371         }
372
373         //- /main.rs crate:main deps:dep
374         fn main() {
375             dep::test_mod::TestStruct::wei$0
376         }
377         "#;
378
379         check(
380             fixture,
381             expect![[r#"
382                 fn weird_function() (dep::test_mod::TestTrait) -> ()
383             "#]],
384         );
385
386         check_edit(
387             "weird_function",
388             fixture,
389             r#"
390 use dep::test_mod::TestTrait;
391
392 fn main() {
393     dep::test_mod::TestStruct::weird_function()$0
394 }
395 "#,
396         );
397     }
398
399     #[test]
400     fn trait_const_fuzzy_completion() {
401         let fixture = r#"
402         //- /lib.rs crate:dep
403         pub mod test_mod {
404             pub trait TestTrait {
405                 const SPECIAL_CONST: u8;
406                 type HumbleType;
407                 fn weird_function();
408                 fn random_method(&self);
409             }
410             pub struct TestStruct {}
411             impl TestTrait for TestStruct {
412                 const SPECIAL_CONST: u8 = 42;
413                 type HumbleType = ();
414                 fn weird_function() {}
415                 fn random_method(&self) {}
416             }
417         }
418
419         //- /main.rs crate:main deps:dep
420         fn main() {
421             dep::test_mod::TestStruct::spe$0
422         }
423         "#;
424
425         check(
426             fixture,
427             expect![[r#"
428             ct SPECIAL_CONST (dep::test_mod::TestTrait)
429         "#]],
430         );
431
432         check_edit(
433             "SPECIAL_CONST",
434             fixture,
435             r#"
436 use dep::test_mod::TestTrait;
437
438 fn main() {
439     dep::test_mod::TestStruct::SPECIAL_CONST
440 }
441 "#,
442         );
443     }
444
445     #[test]
446     fn trait_method_fuzzy_completion() {
447         let fixture = r#"
448         //- /lib.rs crate:dep
449         pub mod test_mod {
450             pub trait TestTrait {
451                 const SPECIAL_CONST: u8;
452                 type HumbleType;
453                 fn weird_function();
454                 fn random_method(&self);
455             }
456             pub struct TestStruct {}
457             impl TestTrait for TestStruct {
458                 const SPECIAL_CONST: u8 = 42;
459                 type HumbleType = ();
460                 fn weird_function() {}
461                 fn random_method(&self) {}
462             }
463         }
464
465         //- /main.rs crate:main deps:dep
466         fn main() {
467             let test_struct = dep::test_mod::TestStruct {};
468             test_struct.ran$0
469         }
470         "#;
471
472         check(
473             fixture,
474             expect![[r#"
475                 me random_method() (dep::test_mod::TestTrait) -> ()
476             "#]],
477         );
478
479         check_edit(
480             "random_method",
481             fixture,
482             r#"
483 use dep::test_mod::TestTrait;
484
485 fn main() {
486     let test_struct = dep::test_mod::TestStruct {};
487     test_struct.random_method()$0
488 }
489 "#,
490         );
491     }
492
493     #[test]
494     fn no_trait_type_fuzzy_completion() {
495         check(
496             r#"
497 //- /lib.rs crate:dep
498 pub mod test_mod {
499     pub trait TestTrait {
500         const SPECIAL_CONST: u8;
501         type HumbleType;
502         fn weird_function();
503         fn random_method(&self);
504     }
505     pub struct TestStruct {}
506     impl TestTrait for TestStruct {
507         const SPECIAL_CONST: u8 = 42;
508         type HumbleType = ();
509         fn weird_function() {}
510         fn random_method(&self) {}
511     }
512 }
513
514 //- /main.rs crate:main deps:dep
515 fn main() {
516     dep::test_mod::TestStruct::hum$0
517 }
518 "#,
519             expect![[r#""#]],
520         );
521     }
522
523     #[test]
524     fn does_not_propose_names_in_scope() {
525         check(
526             r#"
527 //- /lib.rs crate:dep
528 pub mod test_mod {
529     pub trait TestTrait {
530         const SPECIAL_CONST: u8;
531         type HumbleType;
532         fn weird_function();
533         fn random_method(&self);
534     }
535     pub struct TestStruct {}
536     impl TestTrait for TestStruct {
537         const SPECIAL_CONST: u8 = 42;
538         type HumbleType = ();
539         fn weird_function() {}
540         fn random_method(&self) {}
541     }
542 }
543
544 //- /main.rs crate:main deps:dep
545 use dep::test_mod::TestStruct;
546 fn main() {
547     TestSt$0
548 }
549 "#,
550             expect![[r#""#]],
551         );
552     }
553
554     #[test]
555     fn does_not_propose_traits_in_scope() {
556         check(
557             r#"
558 //- /lib.rs crate:dep
559 pub mod test_mod {
560     pub trait TestTrait {
561         const SPECIAL_CONST: u8;
562         type HumbleType;
563         fn weird_function();
564         fn random_method(&self);
565     }
566     pub struct TestStruct {}
567     impl TestTrait for TestStruct {
568         const SPECIAL_CONST: u8 = 42;
569         type HumbleType = ();
570         fn weird_function() {}
571         fn random_method(&self) {}
572     }
573 }
574
575 //- /main.rs crate:main deps:dep
576 use dep::test_mod::{TestStruct, TestTrait};
577 fn main() {
578     dep::test_mod::TestStruct::hum$0
579 }
580 "#,
581             expect![[r#""#]],
582         );
583     }
584
585     #[test]
586     fn blanket_trait_impl_import() {
587         check_edit(
588             "another_function",
589             r#"
590 //- /lib.rs crate:dep
591 pub mod test_mod {
592     pub struct TestStruct {}
593     pub trait TestTrait {
594         fn another_function();
595     }
596     impl<T> TestTrait for T {
597         fn another_function() {}
598     }
599 }
600
601 //- /main.rs crate:main deps:dep
602 fn main() {
603     dep::test_mod::TestStruct::ano$0
604 }
605 "#,
606             r#"
607 use dep::test_mod::TestTrait;
608
609 fn main() {
610     dep::test_mod::TestStruct::another_function()$0
611 }
612 "#,
613         );
614     }
615
616     #[test]
617     fn zero_input_deprecated_assoc_item_completion() {
618         check(
619             r#"
620 //- /lib.rs crate:dep
621 pub mod test_mod {
622     #[deprecated]
623     pub trait TestTrait {
624         const SPECIAL_CONST: u8;
625         type HumbleType;
626         fn weird_function();
627         fn random_method(&self);
628     }
629     pub struct TestStruct {}
630     impl TestTrait for TestStruct {
631         const SPECIAL_CONST: u8 = 42;
632         type HumbleType = ();
633         fn weird_function() {}
634         fn random_method(&self) {}
635     }
636 }
637
638 //- /main.rs crate:main deps:dep
639 fn main() {
640     let test_struct = dep::test_mod::TestStruct {};
641     test_struct.$0
642 }
643         "#,
644             expect![[r#"
645                 me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
646             "#]],
647         );
648
649         check(
650             r#"
651 //- /lib.rs crate:dep
652 pub mod test_mod {
653     #[deprecated]
654     pub trait TestTrait {
655         const SPECIAL_CONST: u8;
656         type HumbleType;
657         fn weird_function();
658         fn random_method(&self);
659     }
660     pub struct TestStruct {}
661     impl TestTrait for TestStruct {
662         const SPECIAL_CONST: u8 = 42;
663         type HumbleType = ();
664         fn weird_function() {}
665         fn random_method(&self) {}
666     }
667 }
668
669 //- /main.rs crate:main deps:dep
670 fn main() {
671     dep::test_mod::TestStruct::$0
672 }
673 "#,
674             expect![[r#"
675                 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
676                 fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
677             "#]],
678         );
679     }
680
681     #[test]
682     fn no_completions_in_use_statements() {
683         check(
684             r#"
685 //- /lib.rs crate:dep
686 pub mod io {
687     pub fn stdin() {}
688 };
689
690 //- /main.rs crate:main deps:dep
691 use stdi$0
692
693 fn main() {}
694 "#,
695             expect![[]],
696         );
697     }
698
699     #[test]
700     fn prefix_config_usage() {
701         let fixture = r#"
702 mod foo {
703     pub mod bar {
704         pub struct Item;
705     }
706 }
707
708 use crate::foo::bar;
709
710 fn main() {
711     Ite$0
712 }"#;
713         let mut config = TEST_CONFIG;
714
715         config.insert_use.prefix_kind = hir::PrefixKind::ByCrate;
716         check_edit_with_config(
717             config.clone(),
718             "Item",
719             fixture,
720             r#"
721 mod foo {
722     pub mod bar {
723         pub struct Item;
724     }
725 }
726
727 use crate::foo::bar::{self, Item};
728
729 fn main() {
730     Item
731 }"#,
732         );
733
734         config.insert_use.prefix_kind = hir::PrefixKind::BySelf;
735         check_edit_with_config(
736             config.clone(),
737             "Item",
738             fixture,
739             r#"
740 mod foo {
741     pub mod bar {
742         pub struct Item;
743     }
744 }
745
746 use crate::foo::bar;
747
748 use self::foo::bar::Item;
749
750 fn main() {
751     Item
752 }"#,
753         );
754
755         config.insert_use.prefix_kind = hir::PrefixKind::Plain;
756         check_edit_with_config(
757             config,
758             "Item",
759             fixture,
760             r#"
761 mod foo {
762     pub mod bar {
763         pub struct Item;
764     }
765 }
766
767 use foo::bar::Item;
768
769 use crate::foo::bar;
770
771 fn main() {
772     Item
773 }"#,
774         );
775     }
776
777     #[test]
778     fn unresolved_qualifier() {
779         check_edit(
780             "Item",
781             r#"
782 mod foo {
783     pub mod bar {
784         pub mod baz {
785             pub struct Item;
786         }
787     }
788 }
789
790 fn main() {
791     bar::baz::Ite$0
792 }
793 "#,
794             r#"
795 use foo::bar;
796
797 mod foo {
798     pub mod bar {
799         pub mod baz {
800             pub struct Item;
801         }
802     }
803 }
804
805 fn main() {
806     bar::baz::Item
807 }
808 "#,
809         );
810     }
811
812     #[test]
813     fn unresolved_assoc_item_container() {
814         check_edit(
815             "Item",
816             r#"
817 mod foo {
818     pub struct Item;
819
820     impl Item {
821         pub const TEST_ASSOC: usize = 3;
822     }
823 }
824
825 fn main() {
826     Item::TEST_A$0;
827 }
828 "#,
829             r#"
830 use foo::Item;
831
832 mod foo {
833     pub struct Item;
834
835     impl Item {
836         pub const TEST_ASSOC: usize = 3;
837     }
838 }
839
840 fn main() {
841     Item::TEST_ASSOC
842 }
843 "#,
844         );
845     }
846
847     #[test]
848     fn unresolved_assoc_item_container_with_path() {
849         check_edit(
850             "Item",
851             r#"
852 mod foo {
853     pub mod bar {
854         pub struct Item;
855
856         impl Item {
857             pub const TEST_ASSOC: usize = 3;
858         }
859     }
860 }
861
862 fn main() {
863     bar::Item::TEST_A$0;
864 }
865 "#,
866             r#"
867 use foo::bar;
868
869 mod foo {
870     pub mod bar {
871         pub struct Item;
872
873         impl Item {
874             pub const TEST_ASSOC: usize = 3;
875         }
876     }
877 }
878
879 fn main() {
880     bar::Item::TEST_ASSOC
881 }
882 "#,
883         );
884     }
885 }