From 10db7ac7a42a97eed45a1c67fbd5e4e43a6a300e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 15:19:52 +0300 Subject: [PATCH 1/2] Deprecate `preserve` import granularity option It didn't do anything (behaved like `item`), as with `enforceGranularity = false` (which is the default), the style of the current file is always preferred, regardless of the setting. We could make it fail when the setting is `preserve` and the current file's style could not be detected, but that makes little sense. It is a bit weird that the default is `crate` but `preserve` falls back to `item`, however that was the previous behavior. --- crates/ide-db/src/imports/insert_use.rs | 5 +--- crates/ide-diagnostics/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 29 +++++++++++++------ editors/code/package.json | 37 ++++++++++++++++--------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index b174adfd7e44..caba1cdd7061 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -27,9 +27,6 @@ pub use hir::PrefixKind; /// How imports should be grouped into use statements. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ImportGranularity { - /// Do not change the granularity of any imports and preserve the original structure written - /// by the developer. - Preserve, /// Merge imports from the same crate into a single use statement. Crate, /// Merge imports from the same module into a single use statement. @@ -174,7 +171,7 @@ fn insert_use_with_alias_option( ImportGranularity::Crate => Some(MergeBehavior::Crate), ImportGranularity::Module => Some(MergeBehavior::Module), ImportGranularity::One => Some(MergeBehavior::One), - ImportGranularity::Item | ImportGranularity::Preserve => None, + ImportGranularity::Item => None, }; if !cfg.enforce_granularity { let file_granularity = guess_granularity_from_scope(scope); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index a1db92641f5e..1530e6465246 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -253,7 +253,7 @@ impl DiagnosticsConfig { style_lints: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { - granularity: ImportGranularity::Preserve, + granularity: ImportGranularity::Item, enforce_granularity: false, prefix_kind: PrefixKind::Plain, group: false, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a88d228fcb60..d4805dc1bdaf 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -1944,8 +1944,9 @@ impl Config { fn insert_use_config(&self, source_root: Option) -> InsertUseConfig { InsertUseConfig { granularity: match self.imports_granularity_group(source_root) { - ImportGranularityDef::Preserve => ImportGranularity::Preserve, - ImportGranularityDef::Item => ImportGranularity::Item, + ImportGranularityDef::Item | ImportGranularityDef::Preserve => { + ImportGranularity::Item + } ImportGranularityDef::Crate => ImportGranularity::Crate, ImportGranularityDef::Module => ImportGranularity::Module, ImportGranularityDef::One => ImportGranularity::One, @@ -3504,13 +3505,23 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "ImportGranularityDef" => set! { "type": "string", - "enum": ["preserve", "crate", "module", "item", "one"], - "enumDescriptions": [ - "Do not change the granularity of any imports and preserve the original structure written by the developer.", - "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", - "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", - "Flatten imports so that each has its own use statement.", - "Merge all imports into a single use statement as long as they have the same visibility and attributes." + "anyOf": [ + { + "enum": ["crate", "module", "item", "one"], + "enumDescriptions": [ + "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", + "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", + "Flatten imports so that each has its own use statement.", + "Merge all imports into a single use statement as long as they have the same visibility and attributes." + ], + }, + { + "enum": ["preserve"], + "enumDescriptions": [ + "Deprecated - unless `enforceGranularity` is `true`, the style of the current file is preferred over this setting. Behaves like `item`.", + ], + "deprecated": true, + } ], }, "ImportPrefixDef" => set! { diff --git a/editors/code/package.json b/editors/code/package.json index 745e0da4efef..5c75a45ee925 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2009,19 +2009,30 @@ "markdownDescription": "How imports should be grouped into use statements.", "default": "crate", "type": "string", - "enum": [ - "preserve", - "crate", - "module", - "item", - "one" - ], - "enumDescriptions": [ - "Do not change the granularity of any imports and preserve the original structure written by the developer.", - "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", - "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", - "Flatten imports so that each has its own use statement.", - "Merge all imports into a single use statement as long as they have the same visibility and attributes." + "anyOf": [ + { + "enum": [ + "crate", + "module", + "item", + "one" + ], + "enumDescriptions": [ + "Merge imports from the same crate into a single use statement. Conversely, imports from different crates are split into separate statements.", + "Merge imports from the same module into a single use statement. Conversely, imports from different modules are split into separate statements.", + "Flatten imports so that each has its own use statement.", + "Merge all imports into a single use statement as long as they have the same visibility and attributes." + ] + }, + { + "enum": [ + "preserve" + ], + "enumDescriptions": [ + "Deprecated - unless `enforceGranularity` is `true`, the style of the current file is preferred over this setting. Behaves like `item`." + ], + "deprecated": true + } ] } } From 9d70d32163c152546d9c4c26cf497ac7dbbbfae8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 5 Oct 2025 15:48:03 +0300 Subject: [PATCH 2/2] Fix merging of import granularity setting with the granularity we infer Previously it was wrong for some combinations. --- crates/ide-db/src/imports/insert_use.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index caba1cdd7061..4444ef5d81d5 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -179,9 +179,18 @@ fn insert_use_with_alias_option( ImportGranularityGuess::Unknown => mb, ImportGranularityGuess::Item => None, ImportGranularityGuess::Module => Some(MergeBehavior::Module), - ImportGranularityGuess::ModuleOrItem => mb.and(Some(MergeBehavior::Module)), + // We use the user's setting to infer if this is module or item. + ImportGranularityGuess::ModuleOrItem => match mb { + Some(MergeBehavior::Module) | None => mb, + // There isn't really a way to decide between module or item here, so we just pick one. + // FIXME: Maybe it is possible to infer based on semantic analysis? + Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), + }, ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), - ImportGranularityGuess::CrateOrModule => mb.or(Some(MergeBehavior::Crate)), + ImportGranularityGuess::CrateOrModule => match mb { + Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, + Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), + }, ImportGranularityGuess::One => Some(MergeBehavior::One), }; }