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