Skip to content

Commit fc4940f

Browse files
committed
Revampt auto-import exclude config
1 parent c5bda0d commit fc4940f

File tree

10 files changed

+169
-55
lines changed

10 files changed

+169
-55
lines changed

crates/ide-completion/src/completions/flyimport.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use itertools::Itertools;
88
use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T};
99

1010
use crate::{
11+
config::AutoImportExclusionType,
1112
context::{
1213
CompletionContext, DotAccess, PathCompletionCtx, PathKind, PatternContext, Qualified,
1314
TypeLocation,
@@ -258,8 +259,6 @@ fn import_on_the_fly(
258259

259260
let import_cfg = ctx.config.import_path_config();
260261

261-
let completed_name = ctx.token.to_string();
262-
263262
import_assets
264263
.search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
265264
.filter(ns_filter)
@@ -270,19 +269,17 @@ fn import_on_the_fly(
270269
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
271270
})
272271
.filter(|import| {
273-
if let ModuleDef::Trait(trait_) = import.item_to_import.into_module_def() {
274-
let excluded = ctx.exclude_flyimport_traits.contains(&trait_);
275-
let trait_itself_imported = import.item_to_import == import.original_item;
276-
if !excluded || trait_itself_imported {
277-
return true;
272+
let def = import.item_to_import.into_module_def();
273+
if let Some(&kind) = ctx.exclude_flyimport.get(&def) {
274+
if kind == AutoImportExclusionType::Always {
275+
return false;
276+
}
277+
let method_imported = import.item_to_import != import.original_item;
278+
if method_imported {
279+
return false;
278280
}
279-
280-
let item = import.original_item.into_module_def();
281-
// Filter that item out, unless its name matches the name the user wrote exactly - in which case preserve it.
282-
item.name(ctx.db).is_some_and(|name| name.eq_ident(&completed_name))
283-
} else {
284-
true
285281
}
282+
true
286283
})
287284
.sorted_by(|a, b| {
288285
let key = |import_path| {
@@ -363,23 +360,26 @@ fn import_on_the_fly_method(
363360

364361
let cfg = ctx.config.import_path_config();
365362

366-
let completed_name = ctx.token.to_string();
367-
368363
import_assets
369364
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
370365
.filter(|import| {
371366
!ctx.is_item_hidden(&import.item_to_import)
372367
&& !ctx.is_item_hidden(&import.original_item)
373368
})
374369
.filter(|import| {
375-
if let ModuleDef::Trait(trait_) = import.item_to_import.into_module_def() {
376-
if !ctx.exclude_flyimport_traits.contains(&trait_) {
377-
return true;
370+
let def = import.item_to_import.into_module_def();
371+
if let Some(&kind) = ctx.exclude_flyimport.get(&def) {
372+
if kind == AutoImportExclusionType::Always {
373+
return false;
378374
}
375+
let method_imported = import.item_to_import != import.original_item;
376+
if method_imported {
377+
return false;
378+
}
379+
}
379380

380-
let item = import.original_item.into_module_def();
381-
// Filter that method out, unless its name matches the name the user wrote exactly - in which case preserve it.
382-
item.name(ctx.db).is_some_and(|name| name.eq_ident(&completed_name))
381+
if let ModuleDef::Trait(_) = import.item_to_import.into_module_def() {
382+
!ctx.exclude_flyimport.contains_key(&def)
383383
} else {
384384
true
385385
}

crates/ide-completion/src/config.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ pub struct CompletionConfig<'a> {
2828
pub snippets: Vec<Snippet>,
2929
pub limit: Option<usize>,
3030
pub fields_to_resolve: CompletionFieldsToResolve,
31-
pub exclude_flyimport_traits: &'a [String],
31+
pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>,
3232
pub exclude_traits: &'a [String],
3333
}
3434

35+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
36+
pub enum AutoImportExclusionType {
37+
Always,
38+
Methods,
39+
}
40+
3541
#[derive(Clone, Debug, PartialEq, Eq)]
3642
pub enum CallableSnippets {
3743
FillArguments,

crates/ide-completion/src/context.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use syntax::{
2222
};
2323

2424
use crate::{
25+
config::AutoImportExclusionType,
2526
context::analysis::{expand_and_analyze, AnalysisResult},
2627
CompletionConfig,
2728
};
@@ -466,7 +467,7 @@ pub(crate) struct CompletionContext<'a> {
466467
/// importing those traits.
467468
///
468469
/// Note the trait *themselves* are not excluded, only their methods are.
469-
pub(crate) exclude_flyimport_traits: FxHashSet<hir::Trait>,
470+
pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
470471
/// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`).
471472
/// They will *not* be excluded, however, if they are available as a generic bound.
472473
///
@@ -780,22 +781,20 @@ impl<'a> CompletionContext<'a> {
780781
})
781782
.collect();
782783

783-
let mut exclude_flyimport_traits: FxHashSet<_> = config
784-
.exclude_flyimport_traits
784+
let mut exclude_flyimport: FxHashMap<_, _> = config
785+
.exclude_flyimport
785786
.iter()
786-
.filter_map(|path| {
787+
.flat_map(|(path, kind)| {
787788
scope
788789
.resolve_mod_path(&ModPath::from_segments(
789790
hir::PathKind::Plain,
790791
path.split("::").map(Symbol::intern).map(Name::new_symbol_root),
791792
))
792-
.find_map(|it| match it {
793-
hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
794-
_ => None,
795-
})
793+
.map(|it| (it.into_module_def(), *kind))
796794
})
797795
.collect();
798-
exclude_flyimport_traits.extend(exclude_traits.iter().copied());
796+
exclude_flyimport
797+
.extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
799798

800799
let complete_semicolon = if config.add_semicolon_to_unit {
801800
let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| {
@@ -861,7 +860,7 @@ impl<'a> CompletionContext<'a> {
861860
qualifier_ctx,
862861
locals,
863862
depth_from_crate_root,
864-
exclude_flyimport_traits,
863+
exclude_flyimport,
865864
exclude_traits,
866865
complete_semicolon,
867866
};

crates/ide-completion/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
};
3232

3333
pub use crate::{
34-
config::{CallableSnippets, CompletionConfig},
34+
config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
3535
item::{
3636
CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
3737
CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,

crates/ide-completion/src/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
8585
snippets: Vec::new(),
8686
limit: None,
8787
fields_to_resolve: CompletionFieldsToResolve::empty(),
88-
exclude_flyimport_traits: &[],
88+
exclude_flyimport: vec![],
8989
exclude_traits: &[],
9090
};
9191

crates/ide-completion/src/tests/expression.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use expect_test::{expect, Expect};
33

44
use crate::{
5+
config::AutoImportExclusionType,
56
tests::{
67
check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE,
78
TEST_CONFIG,
@@ -1605,7 +1606,10 @@ fn foo() {
16051606
fn flyimport_excluded_trait_method_is_excluded_from_flyimport() {
16061607
check_with_config(
16071608
CompletionConfig {
1608-
exclude_flyimport_traits: &["test::module2::ExcludedTrait".to_owned()],
1609+
exclude_flyimport: vec![(
1610+
"test::module2::ExcludedTrait".to_owned(),
1611+
AutoImportExclusionType::Methods,
1612+
)],
16091613
..TEST_CONFIG
16101614
},
16111615
r#"

crates/rust-analyzer/src/config.rs

+71-6
Original file line numberDiff line numberDiff line change
@@ -440,22 +440,27 @@ config_data! {
440440
/// Toggles the additional completions that automatically add imports when completed.
441441
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
442442
completion_autoimport_enable: bool = true,
443-
/// A list of full paths to traits to exclude from flyimport.
443+
/// A list of full paths to items to exclude from auto-importing completions.
444444
///
445445
/// Traits in this list won't have their methods suggested in completions unless the trait
446446
/// is in scope.
447447
///
448+
/// You can either specify a string path which defaults to type "always" or use the more verbose
449+
/// form `{ "path": "path::to::item", type: "always" }`.
450+
///
451+
/// For traits the type "methods" can be used to only exclude the methods but not the trait itself.
452+
///
448453
/// This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
449-
completion_autoimport_excludeTraits: Vec<String> = vec![
450-
"core::borrow::Borrow".to_owned(),
451-
"core::borrow::BorrowMut".to_owned(),
454+
completion_autoimport_exclude: Vec<AutoImportExclusion> = vec![
455+
AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods },
456+
AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods },
452457
],
453458
/// Toggles the additional completions that automatically show method calls and field accesses
454459
/// with `self` prefixed to them when inside a method.
455460
completion_autoself_enable: bool = true,
456461
/// Whether to add parenthesis and argument snippets when completing function.
457462
completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments,
458-
/// A list of full paths to traits to exclude from completion.
463+
/// A list of full paths to traits whose methods to exclude from completion.
459464
///
460465
/// 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`.
461466
///
@@ -1478,7 +1483,26 @@ impl Config {
14781483
} else {
14791484
CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields)
14801485
},
1481-
exclude_flyimport_traits: self.completion_autoimport_excludeTraits(source_root),
1486+
exclude_flyimport: self
1487+
.completion_autoimport_exclude(source_root)
1488+
.iter()
1489+
.map(|it| match it {
1490+
AutoImportExclusion::Path(path) => {
1491+
(path.clone(), ide_completion::AutoImportExclusionType::Always)
1492+
}
1493+
AutoImportExclusion::Verbose { path, r#type } => (
1494+
path.clone(),
1495+
match r#type {
1496+
AutoImportExclusionType::Always => {
1497+
ide_completion::AutoImportExclusionType::Always
1498+
}
1499+
AutoImportExclusionType::Methods => {
1500+
ide_completion::AutoImportExclusionType::Methods
1501+
}
1502+
},
1503+
),
1504+
})
1505+
.collect(),
14821506
exclude_traits: self.completion_excludeTraits(source_root),
14831507
}
14841508
}
@@ -2419,6 +2443,21 @@ enum ExprFillDefaultDef {
24192443
Default,
24202444
}
24212445

2446+
#[derive(Serialize, Deserialize, Debug, Clone)]
2447+
#[serde(untagged)]
2448+
#[serde(rename_all = "snake_case")]
2449+
pub enum AutoImportExclusion {
2450+
Path(String),
2451+
Verbose { path: String, r#type: AutoImportExclusionType },
2452+
}
2453+
2454+
#[derive(Serialize, Deserialize, Debug, Clone)]
2455+
#[serde(rename_all = "snake_case")]
2456+
pub enum AutoImportExclusionType {
2457+
Always,
2458+
Methods,
2459+
}
2460+
24222461
#[derive(Serialize, Deserialize, Debug, Clone)]
24232462
#[serde(rename_all = "snake_case")]
24242463
enum ImportGranularityDef {
@@ -3490,6 +3529,32 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
34903529
}
34913530
]
34923531
},
3532+
"Vec<AutoImportExclusion>" => set! {
3533+
"type": "array",
3534+
"items": {
3535+
"anyOf": [
3536+
{
3537+
"type": "string",
3538+
},
3539+
{
3540+
"type": "object",
3541+
"properties": {
3542+
"path": {
3543+
"type": "string",
3544+
},
3545+
"type": {
3546+
"type": "string",
3547+
"enum": ["always", "methods"],
3548+
"enumDescriptions": [
3549+
"Do not show this item or its methods (if it is a trait) in auto-import completions.",
3550+
"Do not show this traits methods in auto-import completions."
3551+
],
3552+
},
3553+
}
3554+
}
3555+
]
3556+
}
3557+
},
34933558
_ => panic!("missing entry for {ty}: {default} (field {field})"),
34943559
}
34953560

crates/rust-analyzer/src/integrated_benchmarks.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ fn integrated_completion_benchmark() {
174174
limit: None,
175175
add_semicolon_to_unit: true,
176176
fields_to_resolve: CompletionFieldsToResolve::empty(),
177-
exclude_flyimport_traits: &[],
177+
exclude_flyimport: vec![],
178178
exclude_traits: &[],
179179
};
180180
let position =
@@ -224,7 +224,7 @@ fn integrated_completion_benchmark() {
224224
limit: None,
225225
add_semicolon_to_unit: true,
226226
fields_to_resolve: CompletionFieldsToResolve::empty(),
227-
exclude_flyimport_traits: &[],
227+
exclude_flyimport: vec![],
228228
exclude_traits: &[],
229229
};
230230
let position =
@@ -272,7 +272,7 @@ fn integrated_completion_benchmark() {
272272
limit: None,
273273
add_semicolon_to_unit: true,
274274
fields_to_resolve: CompletionFieldsToResolve::empty(),
275-
exclude_flyimport_traits: &[],
275+
exclude_flyimport: vec![],
276276
exclude_traits: &[],
277277
};
278278
let position =

docs/user/generated_config.adoc

+16-5
Original file line numberDiff line numberDiff line change
@@ -286,21 +286,32 @@ In `match` arms it completes a comma instead.
286286
Toggles the additional completions that automatically add imports when completed.
287287
Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
288288
--
289-
[[rust-analyzer.completion.autoimport.excludeTraits]]rust-analyzer.completion.autoimport.excludeTraits::
289+
[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude::
290290
+
291291
--
292292
Default:
293293
----
294294
[
295-
"core::borrow::Borrow",
296-
"core::borrow::BorrowMut"
295+
{
296+
"path": "core::borrow::Borrow",
297+
"type": "methods"
298+
},
299+
{
300+
"path": "core::borrow::BorrowMut",
301+
"type": "methods"
302+
}
297303
]
298304
----
299-
A list of full paths to traits to exclude from flyimport.
305+
A list of full paths to items to exclude from auto-importing completions.
300306

301307
Traits in this list won't have their methods suggested in completions unless the trait
302308
is in scope.
303309

310+
You can either specify a string path which defaults to type "always" or use the more verbose
311+
form `{ "path": "path::to::item", type: "always" }`.
312+
313+
For traits the type "methods" can be used to only exclude the methods but not the trait itself.
314+
304315
This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
305316

306317
--
@@ -318,7 +329,7 @@ Whether to add parenthesis and argument snippets when completing function.
318329
[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`)::
319330
+
320331
--
321-
A list of full paths to traits to exclude from completion.
332+
A list of full paths to traits whose methods to exclude from completion.
322333

323334
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`.
324335

0 commit comments

Comments
 (0)