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.
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:
33 //! pub const TEST_ASSOC: usize = 3;
39 //! bar::Item::TEST_A$0
51 //! pub const TEST_ASSOC: usize = 3;
57 //! bar::Item::TEST_ASSOC
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.
64 //! .Fuzzy search details
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).
71 //! .Import configuration
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.
76 //! .LSP and performance implications
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.
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.
90 use hir::{AsAssocItem, ModPath, ModuleDef, ScopeDef};
91 use ide_db::helpers::{
92 import_assets::{ImportAssets, ImportCandidate},
93 insert_use::ImportScope,
95 use syntax::{AstNode, SyntaxNode, T};
98 context::CompletionContext,
99 render::{render_resolution_with_import, RenderContext},
103 use super::Completions;
105 pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
106 if !ctx.config.enable_imports_on_the_fly {
109 if ctx.use_item_syntax.is_some()
110 || ctx.attribute_under_caret.is_some()
111 || ctx.mod_declaration_under_caret.is_some()
115 let potential_import_name = {
116 let token_kind = ctx.token.kind();
117 if matches!(token_kind, T![.] | T![::]) {
120 ctx.token.to_string()
124 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
126 let user_input_lowercased = potential_import_name.to_lowercase();
127 let import_assets = import_assets(ctx, potential_import_name)?;
128 let import_scope = ImportScope::find_insert_use_container(
129 position_for_import(ctx, Some(import_assets.import_candidate()))?,
133 let mut all_imports =
134 import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
135 all_imports.sort_by_cached_key(|import| {
136 compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
139 acc.add_all(all_imports.into_iter().filter_map(|import| {
140 let import_for_trait_assoc_item = import
143 .and_then(|module_def_id| {
144 ModuleDef::from(module_def_id).as_assoc_item(ctx.db)?.containing_trait(ctx.db)
147 let def_to_display = ScopeDef::from(import.item_to_display());
148 render_resolution_with_import(
149 RenderContext::new(ctx),
150 ImportEdit { import, import_scope: import_scope.clone(), import_for_trait_assoc_item },
157 pub(crate) fn position_for_import<'a>(
158 ctx: &'a CompletionContext,
159 import_candidate: Option<&ImportCandidate>,
160 ) -> Option<&'a SyntaxNode> {
161 Some(match import_candidate {
162 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
163 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
168 .map(|name_ref| name_ref.syntax())
169 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
170 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
174 fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<ImportAssets<'a>> {
175 let current_module = ctx.scope.module()?;
176 if let Some(dot_receiver) = &ctx.dot_receiver {
177 ImportAssets::for_fuzzy_method_call(
179 ctx.sema.type_of_expr(dot_receiver)?,
184 let fuzzy_name_length = fuzzy_name.len();
185 let assets_for_path = ImportAssets::for_fuzzy_path(
187 ctx.path_qual.clone(),
193 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
194 && fuzzy_name_length < 2
196 cov_mark::hit!(ignore_short_input_for_path);
199 Some(assets_for_path)
204 fn compute_fuzzy_completion_order_key(
205 proposed_mod_path: &ModPath,
206 user_input_lowercased: &str,
208 cov_mark::hit!(certain_fuzzy_order_test);
209 let import_name = match proposed_mod_path.segments().last() {
210 Some(name) => name.to_string().to_lowercase(),
211 None => return usize::MAX,
213 match import_name.match_indices(user_input_lowercased).next() {
214 Some((first_matching_index, _)) => first_matching_index,
221 use expect_test::{expect, Expect};
224 item::CompletionKind,
225 test_utils::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
228 fn check(ra_fixture: &str, expect: Expect) {
229 let actual = completion_list(ra_fixture, CompletionKind::Magic);
230 expect.assert_eq(&actual);
234 fn function_fuzzy_completion() {
238 //- /lib.rs crate:dep
243 //- /main.rs crate:main deps:dep
259 fn macro_fuzzy_completion() {
261 "macro_with_curlies!",
263 //- /lib.rs crate:dep
264 /// Please call me as macro_with_curlies! {}
266 macro_rules! macro_with_curlies {
270 //- /main.rs crate:main deps:dep
276 use dep::macro_with_curlies;
279 macro_with_curlies! {$0}
286 fn struct_fuzzy_completion() {
290 //- /lib.rs crate:dep
291 pub struct FirstStruct;
292 pub mod some_module {
293 pub struct SecondStruct;
294 pub struct ThirdStruct;
297 //- /main.rs crate:main deps:dep
298 use dep::{FirstStruct, some_module::SecondStruct};
305 use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
315 fn short_paths_are_ignored() {
316 cov_mark::check!(ignore_short_input_for_path);
320 //- /lib.rs crate:dep
321 pub struct FirstStruct;
322 pub mod some_module {
323 pub struct SecondStruct;
324 pub struct ThirdStruct;
327 //- /main.rs crate:main deps:dep
328 use dep::{FirstStruct, some_module::SecondStruct};
339 fn fuzzy_completions_come_in_specific_order() {
340 cov_mark::check!(certain_fuzzy_order_test);
343 //- /lib.rs crate:dep
344 pub struct FirstStruct;
345 pub mod some_module {
346 // already imported, omitted
347 pub struct SecondStruct;
348 // does not contain all letters from the query, omitted
349 pub struct UnrelatedOne;
350 // contains all letters from the query, but not in sequence, displayed last
351 pub struct ThiiiiiirdStruct;
352 // contains all letters from the query, but not in the beginning, displayed second
353 pub struct AfterThirdStruct;
354 // contains all letters from the query in the begginning, displayed first
355 pub struct ThirdStruct;
358 //- /main.rs crate:main deps:dep
359 use dep::{FirstStruct, some_module::SecondStruct};
366 st dep::some_module::ThirdStruct
367 st dep::some_module::AfterThirdStruct
368 st dep::some_module::ThiiiiiirdStruct
374 fn trait_function_fuzzy_completion() {
376 //- /lib.rs crate:dep
378 pub trait TestTrait {
379 const SPECIAL_CONST: u8;
382 fn random_method(&self);
384 pub struct TestStruct {}
385 impl TestTrait for TestStruct {
386 const SPECIAL_CONST: u8 = 42;
387 type HumbleType = ();
388 fn weird_function() {}
389 fn random_method(&self) {}
393 //- /main.rs crate:main deps:dep
395 dep::test_mod::TestStruct::wei$0
402 fn weird_function() (dep::test_mod::TestTrait) -> ()
410 use dep::test_mod::TestTrait;
413 dep::test_mod::TestStruct::weird_function()$0
420 fn trait_const_fuzzy_completion() {
422 //- /lib.rs crate:dep
424 pub trait TestTrait {
425 const SPECIAL_CONST: u8;
428 fn random_method(&self);
430 pub struct TestStruct {}
431 impl TestTrait for TestStruct {
432 const SPECIAL_CONST: u8 = 42;
433 type HumbleType = ();
434 fn weird_function() {}
435 fn random_method(&self) {}
439 //- /main.rs crate:main deps:dep
441 dep::test_mod::TestStruct::spe$0
448 ct SPECIAL_CONST (dep::test_mod::TestTrait)
456 use dep::test_mod::TestTrait;
459 dep::test_mod::TestStruct::SPECIAL_CONST
466 fn trait_method_fuzzy_completion() {
468 //- /lib.rs crate:dep
470 pub trait TestTrait {
471 const SPECIAL_CONST: u8;
474 fn random_method(&self);
476 pub struct TestStruct {}
477 impl TestTrait for TestStruct {
478 const SPECIAL_CONST: u8 = 42;
479 type HumbleType = ();
480 fn weird_function() {}
481 fn random_method(&self) {}
485 //- /main.rs crate:main deps:dep
487 let test_struct = dep::test_mod::TestStruct {};
495 me random_method() (dep::test_mod::TestTrait) -> ()
503 use dep::test_mod::TestTrait;
506 let test_struct = dep::test_mod::TestStruct {};
507 test_struct.random_method()$0
514 fn no_trait_type_fuzzy_completion() {
517 //- /lib.rs crate:dep
519 pub trait TestTrait {
520 const SPECIAL_CONST: u8;
523 fn random_method(&self);
525 pub struct TestStruct {}
526 impl TestTrait for TestStruct {
527 const SPECIAL_CONST: u8 = 42;
528 type HumbleType = ();
529 fn weird_function() {}
530 fn random_method(&self) {}
534 //- /main.rs crate:main deps:dep
536 dep::test_mod::TestStruct::hum$0
544 fn does_not_propose_names_in_scope() {
547 //- /lib.rs crate:dep
549 pub trait TestTrait {
550 const SPECIAL_CONST: u8;
553 fn random_method(&self);
555 pub struct TestStruct {}
556 impl TestTrait for TestStruct {
557 const SPECIAL_CONST: u8 = 42;
558 type HumbleType = ();
559 fn weird_function() {}
560 fn random_method(&self) {}
564 //- /main.rs crate:main deps:dep
565 use dep::test_mod::TestStruct;
575 fn does_not_propose_traits_in_scope() {
578 //- /lib.rs crate:dep
580 pub trait TestTrait {
581 const SPECIAL_CONST: u8;
584 fn random_method(&self);
586 pub struct TestStruct {}
587 impl TestTrait for TestStruct {
588 const SPECIAL_CONST: u8 = 42;
589 type HumbleType = ();
590 fn weird_function() {}
591 fn random_method(&self) {}
595 //- /main.rs crate:main deps:dep
596 use dep::test_mod::{TestStruct, TestTrait};
598 dep::test_mod::TestStruct::hum$0
606 fn blanket_trait_impl_import() {
610 //- /lib.rs crate:dep
612 pub struct TestStruct {}
613 pub trait TestTrait {
614 fn another_function();
616 impl<T> TestTrait for T {
617 fn another_function() {}
621 //- /main.rs crate:main deps:dep
623 dep::test_mod::TestStruct::ano$0
627 use dep::test_mod::TestTrait;
630 dep::test_mod::TestStruct::another_function()$0
637 fn zero_input_deprecated_assoc_item_completion() {
640 //- /lib.rs crate:dep
643 pub trait TestTrait {
644 const SPECIAL_CONST: u8;
647 fn random_method(&self);
649 pub struct TestStruct {}
650 impl TestTrait for TestStruct {
651 const SPECIAL_CONST: u8 = 42;
652 type HumbleType = ();
653 fn weird_function() {}
654 fn random_method(&self) {}
658 //- /main.rs crate:main deps:dep
660 let test_struct = dep::test_mod::TestStruct {};
665 me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
671 //- /lib.rs crate:dep
674 pub trait TestTrait {
675 const SPECIAL_CONST: u8;
678 fn random_method(&self);
680 pub struct TestStruct {}
681 impl TestTrait for TestStruct {
682 const SPECIAL_CONST: u8 = 42;
683 type HumbleType = ();
684 fn weird_function() {}
685 fn random_method(&self) {}
689 //- /main.rs crate:main deps:dep
691 dep::test_mod::TestStruct::$0
695 fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
696 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
702 fn no_completions_in_use_statements() {
705 //- /lib.rs crate:dep
710 //- /main.rs crate:main deps:dep
720 fn prefix_config_usage() {
733 let mut config = TEST_CONFIG;
735 config.insert_use.prefix_kind = hir::PrefixKind::ByCrate;
736 check_edit_with_config(
747 use crate::foo::bar::{self, Item};
754 config.insert_use.prefix_kind = hir::PrefixKind::BySelf;
755 check_edit_with_config(
768 use self::foo::bar::Item;
775 config.insert_use.prefix_kind = hir::PrefixKind::Plain;
776 check_edit_with_config(
798 fn unresolved_qualifier() {
833 fn unresolved_assoc_item_container() {
841 pub const TEST_ASSOC: usize = 3;
856 pub const TEST_ASSOC: usize = 3;
868 fn unresolved_assoc_item_container_with_path() {
877 pub const TEST_ASSOC: usize = 3;
894 pub const TEST_ASSOC: usize = 3;
900 bar::Item::TEST_ASSOC