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