mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-01 12:24:29 +00:00
Revamp auto-import exclude config
This commit is contained in:
parent
c5bda0d3f7
commit
5303dc5d99
10 changed files with 169 additions and 55 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use crate::{
|
|||
};
|
||||
|
||||
pub use crate::{
|
||||
config::{CallableSnippets, CompletionConfig},
|
||||
config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
|
||||
item::{
|
||||
CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
|
||||
CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,
|
||||
|
|
|
|||
|
|
@ -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: &[],
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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#"
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
@ -2419,6 +2443,21 @@ enum ExprFillDefaultDef {
|
|||
Default,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
|
@ -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})"),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue