]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_completion/src/completions/flyimport.rs
simplify
[rust.git] / crates / ide_completion / src / completions / flyimport.rs
index 1ef6f8afbcd122f522a25005bd5343f1fa25f355..be9cfbded9bb8d406176a245234aa63e8b49a504 100644 (file)
@@ -1,8 +1,10 @@
 //! Feature: completion with imports-on-the-fly
 //!
 //! When completing names in the current scope, proposes additional imports from other modules or crates,
-//! if they can be qualified in the scope and their name contains all symbols from the completion input
-//! (case-insensitive, in any order or places).
+//! if they can be qualified in the scope and their name contains all symbols from the completion input.
+//!
+//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
+//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively.
 //!
 //! ```
 //! fn main() {
@@ -21,8 +23,9 @@
 //! ```
 //!
 //! Also completes associated items, that require trait imports.
-//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account: only the items with import string
-//! containing this whole path will be considered and the corresponding path import will be added:
+//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
+//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
+//! (no fuzzy matching for qualifier).
 //!
 //! ```
 //! mod foo {
 //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
 //! capability enabled.
 
-use hir::{AsAssocItem, ModPath, ModuleDef, ScopeDef};
+use hir::ModPath;
 use ide_db::helpers::{
     import_assets::{ImportAssets, ImportCandidate},
     insert_use::ImportScope,
 };
+use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, T};
 
 use crate::{
@@ -106,10 +110,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    if ctx.use_item_syntax.is_some()
-        || ctx.attribute_under_caret.is_some()
-        || ctx.mod_declaration_under_caret.is_some()
-    {
+    if ctx.use_item_syntax.is_some() || ctx.is_path_disallowed() {
         return None;
     }
     let potential_import_name = {
@@ -125,32 +126,28 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
 
     let user_input_lowercased = potential_import_name.to_lowercase();
     let import_assets = import_assets(ctx, potential_import_name)?;
-    let import_scope = ImportScope::find_insert_use_container(
+    let import_scope = ImportScope::find_insert_use_container_with_macros(
         position_for_import(ctx, Some(import_assets.import_candidate()))?,
         &ctx.sema,
     )?;
 
-    let mut all_imports =
-        import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
-    all_imports.sort_by_cached_key(|import| {
-        compute_fuzzy_completion_order_key(import.display_path(), &user_input_lowercased)
-    });
-
-    acc.add_all(all_imports.into_iter().filter_map(|import| {
-        let import_for_trait_assoc_item = import
-            .item_to_display()
-            .as_module_def_id()
-            .and_then(|module_def_id| {
-                ModuleDef::from(module_def_id).as_assoc_item(ctx.db)?.containing_trait(ctx.db)
+    acc.add_all(
+        import_assets
+            .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+            .into_iter()
+            .sorted_by_key(|located_import| {
+                compute_fuzzy_completion_order_key(
+                    &located_import.import_path,
+                    &user_input_lowercased,
+                )
             })
-            .is_some();
-        let def_to_display = ScopeDef::from(import.item_to_display());
-        render_resolution_with_import(
-            RenderContext::new(ctx),
-            ImportEdit { import, import_scope: import_scope.clone(), import_for_trait_assoc_item },
-            &def_to_display,
-        )
-    }));
+            .filter_map(|import| {
+                render_resolution_with_import(
+                    RenderContext::new(ctx),
+                    ImportEdit { import, scope: import_scope.clone() },
+                )
+            }),
+    );
     Some(())
 }
 
@@ -171,23 +168,28 @@ pub(crate) fn position_for_import<'a>(
     })
 }
 
-fn import_assets<'a>(ctx: &'a CompletionContext, fuzzy_name: String) -> Option<ImportAssets<'a>> {
+fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
     let current_module = ctx.scope.module()?;
     if let Some(dot_receiver) = &ctx.dot_receiver {
         ImportAssets::for_fuzzy_method_call(
             current_module,
             ctx.sema.type_of_expr(dot_receiver)?,
             fuzzy_name,
-            ctx.scope.clone(),
+            dot_receiver.syntax().clone(),
         )
     } else {
         let fuzzy_name_length = fuzzy_name.len();
+        let approximate_node = match current_module.definition_source(ctx.db).value {
+            hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
+            hir::ModuleSource::Module(m) => m.syntax().clone(),
+            hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
+        };
         let assets_for_path = ImportAssets::for_fuzzy_path(
             current_module,
             ctx.path_qual.clone(),
             fuzzy_name,
             &ctx.sema,
-            ctx.scope.clone(),
+            approximate_node,
         )?;
 
         if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
@@ -399,7 +401,7 @@ fn main() {
         check(
             fixture,
             expect![[r#"
-                fn weird_function() (dep::test_mod::TestTrait) -> ()
+                fn weird_function() (dep::test_mod::TestTrait) fn()
             "#]],
         );
 
@@ -492,7 +494,7 @@ fn main() {
         check(
             fixture,
             expect![[r#"
-                me random_method() (dep::test_mod::TestTrait) -> ()
+                me random_method() (dep::test_mod::TestTrait) fn(&self)
             "#]],
         );
 
@@ -662,7 +664,7 @@ fn main() {
 }
         "#,
             expect![[r#"
-                me random_method() (dep::test_mod::TestTrait) -> () DEPRECATED
+                me random_method() (dep::test_mod::TestTrait) fn(&self) DEPRECATED
             "#]],
         );
 
@@ -692,8 +694,8 @@ fn main() {
 }
 "#,
             expect![[r#"
-                fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
                 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
+                fn weird_function() (dep::test_mod::TestTrait) fn() DEPRECATED
             "#]],
         );
     }
@@ -796,9 +798,7 @@ fn main() {
 
     #[test]
     fn unresolved_qualifier() {
-        check_edit(
-            "Item",
-            r#"
+        let fixture = r#"
 mod foo {
     pub mod bar {
         pub mod baz {
@@ -809,31 +809,38 @@ pub mod baz {
 
 fn main() {
     bar::baz::Ite$0
-}
-"#,
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        st foo::bar::baz::Item
+        "#]],
+        );
+
+        check_edit(
+            "Item",
+            fixture,
             r#"
-use foo::bar;
+        use foo::bar;
 
-mod foo {
-    pub mod bar {
-        pub mod baz {
-            pub struct Item;
+        mod foo {
+            pub mod bar {
+                pub mod baz {
+                    pub struct Item;
+                }
+            }
         }
-    }
-}
 
-fn main() {
-    bar::baz::Item
-}
-"#,
+        fn main() {
+            bar::baz::Item
+        }"#,
         );
     }
 
     #[test]
     fn unresolved_assoc_item_container() {
-        check_edit(
-            "TEST_ASSOC",
-            r#"
+        let fixture = r#"
 mod foo {
     pub struct Item;
 
@@ -844,8 +851,18 @@ impl Item {
 
 fn main() {
     Item::TEST_A$0
-}
-"#,
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        ct TEST_ASSOC (foo::Item)
+        "#]],
+        );
+
+        check_edit(
+            "TEST_ASSOC",
+            fixture,
             r#"
 use foo::Item;
 
@@ -859,16 +876,13 @@ impl Item {
 
 fn main() {
     Item::TEST_ASSOC
-}
-"#,
+}"#,
         );
     }
 
     #[test]
     fn unresolved_assoc_item_container_with_path() {
-        check_edit(
-            "TEST_ASSOC",
-            r#"
+        let fixture = r#"
 mod foo {
     pub mod bar {
         pub struct Item;
@@ -881,8 +895,18 @@ impl Item {
 
 fn main() {
     bar::Item::TEST_A$0
-}
-"#,
+}"#;
+
+        check(
+            fixture,
+            expect![[r#"
+        ct TEST_ASSOC (foo::bar::Item)
+    "#]],
+        );
+
+        check_edit(
+            "TEST_ASSOC",
+            fixture,
             r#"
 use foo::bar;
 
@@ -898,8 +922,226 @@ impl Item {
 
 fn main() {
     bar::Item::TEST_ASSOC
+}"#,
+        );
+    }
+
+    #[test]
+    fn fuzzy_unresolved_path() {
+        check(
+            r#"
+mod foo {
+    pub mod bar {
+        pub struct Item;
+
+        impl Item {
+            pub const TEST_ASSOC: usize = 3;
+        }
+    }
+}
+
+fn main() {
+    bar::ASS$0
+}"#,
+            expect![[]],
+        )
+    }
+
+    #[test]
+    fn unqualified_assoc_items_are_omitted() {
+        check(
+            r#"
+mod something {
+    pub trait BaseTrait {
+        fn test_function() -> i32;
+    }
+
+    pub struct Item1;
+    pub struct Item2;
+
+    impl BaseTrait for Item1 {
+        fn test_function() -> i32 {
+            1
+        }
+    }
+
+    impl BaseTrait for Item2 {
+        fn test_function() -> i32 {
+            2
+        }
+    }
+}
+
+fn main() {
+    test_f$0
+}"#,
+            expect![[]],
+        )
+    }
+
+    #[test]
+    fn case_matters() {
+        check(
+            r#"
+mod foo {
+    pub const TEST_CONST: usize = 3;
+    pub fn test_function() -> i32 {
+        4
+    }
+}
+
+fn main() {
+    TE$0
+}"#,
+            expect![[r#"
+        ct foo::TEST_CONST
+    "#]],
+        );
+
+        check(
+            r#"
+mod foo {
+    pub const TEST_CONST: usize = 3;
+    pub fn test_function() -> i32 {
+        4
+    }
+}
+
+fn main() {
+    te$0
+}"#,
+            expect![[r#"
+        ct foo::TEST_CONST
+        fn test_function() (foo::test_function) fn() -> i32
+    "#]],
+        );
+
+        check(
+            r#"
+mod foo {
+    pub const TEST_CONST: usize = 3;
+    pub fn test_function() -> i32 {
+        4
+    }
+}
+
+fn main() {
+    Te$0
+}"#,
+            expect![[]],
+        );
+    }
+
+    #[test]
+    fn no_fuzzy_during_fields_of_record_lit_syntax() {
+        check(
+            r#"
+mod m {
+    pub fn some_fn() -> i32 {
+        42
+    }
+}
+struct Foo {
+    some_field: i32,
+}
+fn main() {
+    let _ = Foo { so$0 };
+}
+"#,
+            expect![[]],
+        );
+    }
+
+    #[test]
+    fn fuzzy_after_fields_of_record_lit_syntax() {
+        check(
+            r#"
+mod m {
+    pub fn some_fn() -> i32 {
+        42
+    }
+}
+struct Foo {
+    some_field: i32,
+}
+fn main() {
+    let _ = Foo { some_field: so$0 };
+}
+"#,
+            expect![[r#"
+                fn some_fn() (m::some_fn) fn() -> i32
+            "#]],
+        );
+    }
+
+    #[test]
+    fn no_flyimports_in_traits_and_impl_declarations() {
+        check(
+            r#"
+mod m {
+    pub fn some_fn() -> i32 {
+        42
+    }
+}
+trait Foo {
+    som$0
+}
+"#,
+            expect![[r#""#]],
+        );
+
+        check(
+            r#"
+mod m {
+    pub fn some_fn() -> i32 {
+        42
+    }
+}
+struct Foo;
+impl Foo {
+    som$0
 }
 "#,
+            expect![[r#""#]],
+        );
+
+        check(
+            r#"
+mod m {
+    pub fn some_fn() -> i32 {
+        42
+    }
+}
+struct Foo;
+trait Bar {}
+impl Bar for Foo {
+    som$0
+}
+"#,
+            expect![[r#""#]],
+        );
+    }
+
+    #[test]
+    fn no_inherent_candidates_proposed() {
+        check(
+            r#"
+mod baz {
+    pub trait DefDatabase {
+        fn method1(&self);
+    }
+    pub trait HirDatabase: DefDatabase {
+        fn method2(&self);
+    }
+}
+
+mod bar {
+    fn test(db: &dyn crate::baz::HirDatabase) {
+        db.metho$0
+    }
+}
+            "#,
+            expect![[r#""#]],
         );
     }
 }