1 //! Feature: completion with imports-on-the-fly
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).
11 //! # pub mod std { pub mod marker { pub struct PhantomData { } } }
15 //! use std::marker::PhantomData;
20 //! # pub mod std { pub mod marker { pub struct PhantomData { } } }
23 //! Also completes associated items, that require trait imports.
25 //! .Fuzzy search details
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).
32 //! .Import configuration
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.
37 //! .LSP and performance implications
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.
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.
51 use hir::{AsAssocItem, ItemInNs, ModPath, ScopeDef};
52 use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope,
56 use syntax::{AstNode, SyntaxNode, T};
59 context::CompletionContext,
60 render::{render_resolution_with_import, RenderContext},
64 use super::Completions;
66 pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
67 if !ctx.config.enable_imports_on_the_fly {
70 if ctx.use_item_syntax.is_some()
71 || ctx.attribute_under_caret.is_some()
72 || ctx.mod_declaration_under_caret.is_some()
76 let potential_import_name = {
77 let token_kind = ctx.token.kind();
78 if matches!(token_kind, T![.] | T![::]) {
85 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
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()))?,
94 let mut all_mod_paths = import_assets
95 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
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()),
103 (import, def_to_display)
105 .collect::<Vec<_>>();
106 all_mod_paths.sort_by_cached_key(|(import, _)| {
107 compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
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))
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)
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(),
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()))?,
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(
147 ctx.sema.type_of_expr(dot_receiver)?,
152 let fuzzy_name_length = fuzzy_name.len();
153 let assets_for_path = ImportAssets::for_fuzzy_path(
155 ctx.path_qual.clone(),
161 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
162 && fuzzy_name_length < 2
164 cov_mark::hit!(ignore_short_input_for_path);
167 Some(assets_for_path)
172 fn compute_fuzzy_completion_order_key(
173 proposed_mod_path: &ModPath,
174 user_input_lowercased: &str,
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,
181 match import_name.match_indices(user_input_lowercased).next() {
182 Some((first_matching_index, _)) => first_matching_index,
189 use expect_test::{expect, Expect};
192 item::CompletionKind,
193 test_utils::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
196 fn check(ra_fixture: &str, expect: Expect) {
197 let actual = completion_list(ra_fixture, CompletionKind::Magic);
198 expect.assert_eq(&actual);
202 fn function_fuzzy_completion() {
206 //- /lib.rs crate:dep
211 //- /main.rs crate:main deps:dep
227 fn macro_fuzzy_completion() {
229 "macro_with_curlies!",
231 //- /lib.rs crate:dep
232 /// Please call me as macro_with_curlies! {}
234 macro_rules! macro_with_curlies {
238 //- /main.rs crate:main deps:dep
244 use dep::macro_with_curlies;
247 macro_with_curlies! {$0}
254 fn struct_fuzzy_completion() {
258 //- /lib.rs crate:dep
259 pub struct FirstStruct;
260 pub mod some_module {
261 pub struct SecondStruct;
262 pub struct ThirdStruct;
265 //- /main.rs crate:main deps:dep
266 use dep::{FirstStruct, some_module::SecondStruct};
273 use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
283 fn short_paths_are_ignored() {
284 cov_mark::check!(ignore_short_input_for_path);
288 //- /lib.rs crate:dep
289 pub struct FirstStruct;
290 pub mod some_module {
291 pub struct SecondStruct;
292 pub struct ThirdStruct;
295 //- /main.rs crate:main deps:dep
296 use dep::{FirstStruct, some_module::SecondStruct};
307 fn fuzzy_completions_come_in_specific_order() {
308 cov_mark::check!(certain_fuzzy_order_test);
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;
326 //- /main.rs crate:main deps:dep
327 use dep::{FirstStruct, some_module::SecondStruct};
334 st dep::some_module::ThirdStruct
335 st dep::some_module::AfterThirdStruct
336 st dep::some_module::ThiiiiiirdStruct
342 fn trait_function_fuzzy_completion() {
344 //- /lib.rs crate:dep
346 pub trait TestTrait {
347 const SPECIAL_CONST: u8;
350 fn random_method(&self);
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) {}
361 //- /main.rs crate:main deps:dep
363 dep::test_mod::TestStruct::wei$0
370 fn weird_function() (dep::test_mod::TestTrait) -> ()
378 use dep::test_mod::TestTrait;
381 dep::test_mod::TestStruct::weird_function()$0
388 fn trait_const_fuzzy_completion() {
390 //- /lib.rs crate:dep
392 pub trait TestTrait {
393 const SPECIAL_CONST: u8;
396 fn random_method(&self);
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) {}
407 //- /main.rs crate:main deps:dep
409 dep::test_mod::TestStruct::spe$0
416 ct SPECIAL_CONST (dep::test_mod::TestTrait)
424 use dep::test_mod::TestTrait;
427 dep::test_mod::TestStruct::SPECIAL_CONST
434 fn trait_method_fuzzy_completion() {
436 //- /lib.rs crate:dep
438 pub trait TestTrait {
439 const SPECIAL_CONST: u8;
442 fn random_method(&self);
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) {}
453 //- /main.rs crate:main deps:dep
455 let test_struct = dep::test_mod::TestStruct {};
463 me random_method() (dep::test_mod::TestTrait) -> ()
471 use dep::test_mod::TestTrait;
474 let test_struct = dep::test_mod::TestStruct {};
475 test_struct.random_method()$0
482 fn no_trait_type_fuzzy_completion() {
485 //- /lib.rs crate:dep
487 pub trait TestTrait {
488 const SPECIAL_CONST: u8;
491 fn random_method(&self);
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) {}
502 //- /main.rs crate:main deps:dep
504 dep::test_mod::TestStruct::hum$0
512 fn does_not_propose_names_in_scope() {
515 //- /lib.rs crate:dep
517 pub trait TestTrait {
518 const SPECIAL_CONST: u8;
521 fn random_method(&self);
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) {}
532 //- /main.rs crate:main deps:dep
533 use dep::test_mod::TestStruct;
543 fn does_not_propose_traits_in_scope() {
546 //- /lib.rs crate:dep
548 pub trait TestTrait {
549 const SPECIAL_CONST: u8;
552 fn random_method(&self);
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) {}
563 //- /main.rs crate:main deps:dep
564 use dep::test_mod::{TestStruct, TestTrait};
566 dep::test_mod::TestStruct::hum$0
574 fn blanket_trait_impl_import() {
578 //- /lib.rs crate:dep
580 pub struct TestStruct {}
581 pub trait TestTrait {
582 fn another_function();
584 impl<T> TestTrait for T {
585 fn another_function() {}
589 //- /main.rs crate:main deps:dep
591 dep::test_mod::TestStruct::ano$0
595 use dep::test_mod::TestTrait;
598 dep::test_mod::TestStruct::another_function()$0
605 fn zero_input_deprecated_assoc_item_completion() {
608 //- /lib.rs crate:dep
611 pub trait TestTrait {
612 const SPECIAL_CONST: u8;
615 fn random_method(&self);
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) {}
626 //- /main.rs crate:main deps:dep
628 let test_struct = dep::test_mod::TestStruct {};
633 me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
639 //- /lib.rs crate:dep
642 pub trait TestTrait {
643 const SPECIAL_CONST: u8;
646 fn random_method(&self);
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) {}
657 //- /main.rs crate:main deps:dep
659 dep::test_mod::TestStruct::$0
663 fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
664 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
670 fn no_completions_in_use_statements() {
673 //- /lib.rs crate:dep
678 //- /main.rs crate:main deps:dep
688 fn prefix_config_usage() {
701 let mut config = TEST_CONFIG;
703 config.insert_use.prefix_kind = hir::PrefixKind::ByCrate;
704 check_edit_with_config(
715 use crate::foo::bar::{self, Item};
722 config.insert_use.prefix_kind = hir::PrefixKind::BySelf;
723 check_edit_with_config(
736 use self::foo::bar::Item;
743 config.insert_use.prefix_kind = hir::PrefixKind::Plain;
744 check_edit_with_config(
766 fn unresolved_qualifier() {
801 fn unresolved_assoc_item_container() {
809 pub const TEST_ASSOC: usize = 3;
824 pub const TEST_ASSOC: usize = 3;
836 fn unresolved_assoc_item_container_with_path() {
845 pub const TEST_ASSOC: usize = 3;
862 pub const TEST_ASSOC: usize = 3;
868 bar::Item::TEST_ASSOC