about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/config.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs77
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs6
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc21
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json41
10 files changed, 169 insertions, 55 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index afa94affb3b..3b2b2fd706e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -8,6 +8,7 @@ use itertools::Itertools;
 use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T};
 
 use crate::{
+    config::AutoImportExclusionType,
     context::{
         CompletionContext, DotAccess, PathCompletionCtx, PathKind, PatternContext, Qualified,
         TypeLocation,
@@ -258,8 +259,6 @@ fn import_on_the_fly(
 
     let import_cfg = ctx.config.import_path_config();
 
-    let completed_name = ctx.token.to_string();
-
     import_assets
         .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
         .filter(ns_filter)
@@ -270,19 +269,17 @@ fn import_on_the_fly(
                 && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
         })
         .filter(|import| {
-            if let ModuleDef::Trait(trait_) = import.item_to_import.into_module_def() {
-                let excluded = ctx.exclude_flyimport_traits.contains(&trait_);
-                let trait_itself_imported = import.item_to_import == import.original_item;
-                if !excluded || trait_itself_imported {
-                    return true;
+            let def = import.item_to_import.into_module_def();
+            if let Some(&kind) = ctx.exclude_flyimport.get(&def) {
+                if kind == AutoImportExclusionType::Always {
+                    return false;
+                }
+                let method_imported = import.item_to_import != import.original_item;
+                if method_imported {
+                    return false;
                 }
-
-                let item = import.original_item.into_module_def();
-                // Filter that item out, unless its name matches the name the user wrote exactly - in which case preserve it.
-                item.name(ctx.db).is_some_and(|name| name.eq_ident(&completed_name))
-            } else {
-                true
             }
+            true
         })
         .sorted_by(|a, b| {
             let key = |import_path| {
@@ -363,8 +360,6 @@ fn import_on_the_fly_method(
 
     let cfg = ctx.config.import_path_config();
 
-    let completed_name = ctx.token.to_string();
-
     import_assets
         .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
         .filter(|import| {
@@ -372,14 +367,19 @@ fn import_on_the_fly_method(
                 && !ctx.is_item_hidden(&import.original_item)
         })
         .filter(|import| {
-            if let ModuleDef::Trait(trait_) = import.item_to_import.into_module_def() {
-                if !ctx.exclude_flyimport_traits.contains(&trait_) {
-                    return true;
+            let def = import.item_to_import.into_module_def();
+            if let Some(&kind) = ctx.exclude_flyimport.get(&def) {
+                if kind == AutoImportExclusionType::Always {
+                    return false;
                 }
+                let method_imported = import.item_to_import != import.original_item;
+                if method_imported {
+                    return false;
+                }
+            }
 
-                let item = import.original_item.into_module_def();
-                // Filter that method out, unless its name matches the name the user wrote exactly - in which case preserve it.
-                item.name(ctx.db).is_some_and(|name| name.eq_ident(&completed_name))
+            if let ModuleDef::Trait(_) = import.item_to_import.into_module_def() {
+                !ctx.exclude_flyimport.contains_key(&def)
             } else {
                 true
             }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
index ed36fe8d028..8b1ce11e8a4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
@@ -28,10 +28,16 @@ pub struct CompletionConfig<'a> {
     pub snippets: Vec<Snippet>,
     pub limit: Option<usize>,
     pub fields_to_resolve: CompletionFieldsToResolve,
-    pub exclude_flyimport_traits: &'a [String],
+    pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>,
     pub exclude_traits: &'a [String],
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum AutoImportExclusionType {
+    Always,
+    Methods,
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum CallableSnippets {
     FillArguments,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index c4365284251..183490c2ed8 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -22,6 +22,7 @@ use syntax::{
 };
 
 use crate::{
+    config::AutoImportExclusionType,
     context::analysis::{expand_and_analyze, AnalysisResult},
     CompletionConfig,
 };
@@ -466,7 +467,7 @@ pub(crate) struct CompletionContext<'a> {
     /// importing those traits.
     ///
     /// Note the trait *themselves* are not excluded, only their methods are.
-    pub(crate) exclude_flyimport_traits: FxHashSet<hir::Trait>,
+    pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
     /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`).
     /// They will *not* be excluded, however, if they are available as a generic bound.
     ///
@@ -780,22 +781,20 @@ impl<'a> CompletionContext<'a> {
             })
             .collect();
 
-        let mut exclude_flyimport_traits: FxHashSet<_> = config
-            .exclude_flyimport_traits
+        let mut exclude_flyimport: FxHashMap<_, _> = config
+            .exclude_flyimport
             .iter()
-            .filter_map(|path| {
+            .flat_map(|(path, kind)| {
                 scope
                     .resolve_mod_path(&ModPath::from_segments(
                         hir::PathKind::Plain,
                         path.split("::").map(Symbol::intern).map(Name::new_symbol_root),
                     ))
-                    .find_map(|it| match it {
-                        hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
-                        _ => None,
-                    })
+                    .map(|it| (it.into_module_def(), *kind))
             })
             .collect();
-        exclude_flyimport_traits.extend(exclude_traits.iter().copied());
+        exclude_flyimport
+            .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
 
         let complete_semicolon = if config.add_semicolon_to_unit {
             let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| {
@@ -861,7 +860,7 @@ impl<'a> CompletionContext<'a> {
             qualifier_ctx,
             locals,
             depth_from_crate_root,
-            exclude_flyimport_traits,
+            exclude_flyimport,
             exclude_traits,
             complete_semicolon,
         };
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 05563bc912d..ca6c9ad9f08 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -31,7 +31,7 @@ use crate::{
 };
 
 pub use crate::{
-    config::{CallableSnippets, CompletionConfig},
+    config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
     item::{
         CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
         CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 3b2d3fbbfc1..1815f340532 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -85,7 +85,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
     snippets: Vec::new(),
     limit: None,
     fields_to_resolve: CompletionFieldsToResolve::empty(),
-    exclude_flyimport_traits: &[],
+    exclude_flyimport: vec![],
     exclude_traits: &[],
 };
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index d1bbf1e8d0b..a9db1d95318 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -2,6 +2,7 @@
 use expect_test::{expect, Expect};
 
 use crate::{
+    config::AutoImportExclusionType,
     tests::{
         check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE,
         TEST_CONFIG,
@@ -1605,7 +1606,10 @@ fn foo() {
 fn flyimport_excluded_trait_method_is_excluded_from_flyimport() {
     check_with_config(
         CompletionConfig {
-            exclude_flyimport_traits: &["test::module2::ExcludedTrait".to_owned()],
+            exclude_flyimport: vec![(
+                "test::module2::ExcludedTrait".to_owned(),
+                AutoImportExclusionType::Methods,
+            )],
             ..TEST_CONFIG
         },
         r#"
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index b6678c12c70..b7297d0c02a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -440,22 +440,27 @@ config_data! {
         /// Toggles the additional completions that automatically add imports when completed.
         /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
         completion_autoimport_enable: bool       = true,
-        /// A list of full paths to traits to exclude from flyimport.
+        /// A list of full paths to items to exclude from auto-importing completions.
         ///
         /// Traits in this list won't have their methods suggested in completions unless the trait
         /// is in scope.
         ///
+        /// You can either specify a string path which defaults to type "always" or use the more verbose
+        /// form `{ "path": "path::to::item", type: "always" }`.
+        ///
+        /// For traits the type "methods" can be used to only exclude the methods but not the trait itself.
+        ///
         /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
-        completion_autoimport_excludeTraits: Vec<String> = vec![
-            "core::borrow::Borrow".to_owned(),
-            "core::borrow::BorrowMut".to_owned(),
+        completion_autoimport_exclude: Vec<AutoImportExclusion> = vec![
+            AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods },
+            AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods },
         ],
         /// Toggles the additional completions that automatically show method calls and field accesses
         /// with `self` prefixed to them when inside a method.
         completion_autoself_enable: bool        = true,
         /// Whether to add parenthesis and argument snippets when completing function.
         completion_callable_snippets: CallableCompletionDef  = CallableCompletionDef::FillArguments,
-        /// A list of full paths to traits to exclude from completion.
+        /// A list of full paths to traits whose methods to exclude from completion.
         ///
         /// Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
         ///
@@ -1478,7 +1483,26 @@ impl Config {
             } else {
                 CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields)
             },
-            exclude_flyimport_traits: self.completion_autoimport_excludeTraits(source_root),
+            exclude_flyimport: self
+                .completion_autoimport_exclude(source_root)
+                .iter()
+                .map(|it| match it {
+                    AutoImportExclusion::Path(path) => {
+                        (path.clone(), ide_completion::AutoImportExclusionType::Always)
+                    }
+                    AutoImportExclusion::Verbose { path, r#type } => (
+                        path.clone(),
+                        match r#type {
+                            AutoImportExclusionType::Always => {
+                                ide_completion::AutoImportExclusionType::Always
+                            }
+                            AutoImportExclusionType::Methods => {
+                                ide_completion::AutoImportExclusionType::Methods
+                            }
+                        },
+                    ),
+                })
+                .collect(),
             exclude_traits: self.completion_excludeTraits(source_root),
         }
     }
@@ -2420,6 +2444,21 @@ enum ExprFillDefaultDef {
 }
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(untagged)]
+#[serde(rename_all = "snake_case")]
+pub enum AutoImportExclusion {
+    Path(String),
+    Verbose { path: String, r#type: AutoImportExclusionType },
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum AutoImportExclusionType {
+    Always,
+    Methods,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportGranularityDef {
     Preserve,
@@ -3490,6 +3529,32 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 }
             ]
         },
+        "Vec<AutoImportExclusion>" => set! {
+            "type": "array",
+            "items": {
+                "anyOf": [
+                    {
+                        "type": "string",
+                    },
+                    {
+                        "type": "object",
+                        "properties": {
+                            "path": {
+                                "type": "string",
+                            },
+                            "type": {
+                                "type": "string",
+                                "enum": ["always", "methods"],
+                                "enumDescriptions": [
+                                    "Do not show this item or its methods (if it is a trait) in auto-import completions.",
+                                    "Do not show this traits methods in auto-import completions."
+                                ],
+                            },
+                        }
+                    }
+                ]
+             }
+        },
         _ => panic!("missing entry for {ty}: {default} (field {field})"),
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index bd164fe4408..fcfd06679bf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -174,7 +174,7 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
-            exclude_flyimport_traits: &[],
+            exclude_flyimport: vec![],
             exclude_traits: &[],
         };
         let position =
@@ -224,7 +224,7 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
-            exclude_flyimport_traits: &[],
+            exclude_flyimport: vec![],
             exclude_traits: &[],
         };
         let position =
@@ -272,7 +272,7 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
-            exclude_flyimport_traits: &[],
+            exclude_flyimport: vec![],
             exclude_traits: &[],
         };
         let position =
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index bce26f7dd74..a44f5a12b28 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -286,21 +286,32 @@ In `match` arms it completes a comma instead.
 Toggles the additional completions that automatically add imports when completed.
 Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
 --
-[[rust-analyzer.completion.autoimport.excludeTraits]]rust-analyzer.completion.autoimport.excludeTraits::
+[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude::
 +
 --
 Default:
 ----
 [
-  "core::borrow::Borrow",
-  "core::borrow::BorrowMut"
+  {
+    "path": "core::borrow::Borrow",
+    "type": "methods"
+  },
+  {
+    "path": "core::borrow::BorrowMut",
+    "type": "methods"
+  }
 ]
 ----
-A list of full paths to traits to exclude from flyimport.
+A list of full paths to items to exclude from auto-importing completions.
 
 Traits in this list won't have their methods suggested in completions unless the trait
 is in scope.
 
+You can either specify a string path which defaults to type "always" or use the more verbose
+form `{ "path": "path::to::item", type: "always" }`.
+
+For traits the type "methods" can be used to only exclude the methods but not the trait itself.
+
 This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
 
 --
@@ -318,7 +329,7 @@ Whether to add parenthesis and argument snippets when completing function.
 [[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`)::
 +
 --
-A list of full paths to traits to exclude from completion.
+A list of full paths to traits whose methods to exclude from completion.
 
 Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
 
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 31419a1942d..03c00a37fb1 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -1142,15 +1142,44 @@
             {
                 "title": "completion",
                 "properties": {
-                    "rust-analyzer.completion.autoimport.excludeTraits": {
-                        "markdownDescription": "A list of full paths to traits to exclude from flyimport.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.",
+                    "rust-analyzer.completion.autoimport.exclude": {
+                        "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more verbose\nform `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait itself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.",
                         "default": [
-                            "core::borrow::Borrow",
-                            "core::borrow::BorrowMut"
+                            {
+                                "path": "core::borrow::Borrow",
+                                "type": "methods"
+                            },
+                            {
+                                "path": "core::borrow::BorrowMut",
+                                "type": "methods"
+                            }
                         ],
                         "type": "array",
                         "items": {
-                            "type": "string"
+                            "anyOf": [
+                                {
+                                    "type": "string"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "path": {
+                                            "type": "string"
+                                        },
+                                        "type": {
+                                            "type": "string",
+                                            "enum": [
+                                                "always",
+                                                "methods"
+                                            ],
+                                            "enumDescriptions": [
+                                                "Do not show this item or its methods (if it is a trait) in auto-import completions.",
+                                                "Do not show this traits methods in auto-import completions."
+                                            ]
+                                        }
+                                    }
+                                }
+                            ]
                         }
                     }
                 }
@@ -1189,7 +1218,7 @@
                 "title": "completion",
                 "properties": {
                     "rust-analyzer.completion.excludeTraits": {
-                        "markdownDescription": "A list of full paths to traits to exclude from completion.\n\nMethods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.\n\nNote that the trait themselves can still be completed.",
+                        "markdownDescription": "A list of full paths to traits whose methods to exclude from completion.\n\nMethods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.\n\nNote that the trait themselves can still be completed.",
                         "default": [],
                         "type": "array",
                         "items": {