MergeBehavior -> ImportGranularity

This commit is contained in:
Lukas Tobias Wirth 2021-05-18 19:49:15 +02:00
parent e3d0d89d7e
commit 64f7072c25
8 changed files with 75 additions and 38 deletions

View file

@ -4,7 +4,10 @@ use expect_test::expect;
use hir::Semantics; use hir::Semantics;
use ide_db::{ use ide_db::{
base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}, helpers::{
insert_use::{ImportGranularity, InsertUseConfig},
SnippetCap,
},
source_change::FileSystemEdit, source_change::FileSystemEdit,
RootDatabase, RootDatabase,
}; };
@ -21,7 +24,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
snippet_cap: SnippetCap::new(true), snippet_cap: SnippetCap::new(true),
allowed: None, allowed: None,
insert_use: InsertUseConfig { insert_use: InsertUseConfig {
merge: Some(MergeBehavior::Crate), granularity: ImportGranularity::Crate,
prefix_kind: hir::PrefixKind::Plain, prefix_kind: hir::PrefixKind::Plain,
group: true, group: true,
}, },

View file

@ -3,7 +3,10 @@
use hir::{PrefixKind, Semantics}; use hir::{PrefixKind, Semantics};
use ide_db::{ use ide_db::{
base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}, helpers::{
insert_use::{ImportGranularity, InsertUseConfig},
SnippetCap,
},
RootDatabase, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -20,7 +23,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
add_call_argument_snippets: true, add_call_argument_snippets: true,
snippet_cap: SnippetCap::new(true), snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig { insert_use: InsertUseConfig {
merge: Some(MergeBehavior::Crate), granularity: ImportGranularity::Crate,
prefix_kind: PrefixKind::Plain, prefix_kind: PrefixKind::Plain,
group: true, group: true,
}, },

View file

@ -15,9 +15,32 @@ use crate::{
pub use hir::PrefixKind; 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.
Module,
/// Flatten imports so that each has its own use statement.
Item,
}
impl ImportGranularity {
pub fn merge_behavior(self) -> Option<MergeBehavior> {
match self {
ImportGranularity::Crate => Some(MergeBehavior::Crate),
ImportGranularity::Module => Some(MergeBehavior::Module),
ImportGranularity::Preserve | ImportGranularity::Item => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct InsertUseConfig { pub struct InsertUseConfig {
pub merge: Option<MergeBehavior>, pub granularity: ImportGranularity,
pub prefix_kind: PrefixKind, pub prefix_kind: PrefixKind,
pub group: bool, pub group: bool,
} }
@ -73,7 +96,7 @@ pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig
let use_item = let use_item =
make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update();
// merge into existing imports if possible // merge into existing imports if possible
if let Some(mb) = cfg.merge { if let Some(mb) = cfg.granularity.merge_behavior() {
for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
ted::replace(existing_use.syntax(), merged.syntax()); ted::replace(existing_use.syntax(), merged.syntax());

View file

@ -21,7 +21,7 @@ use crate::bar::A;
use self::bar::A; use self::bar::A;
use super::bar::A; use super::bar::A;
use external_crate2::bar::A;", use external_crate2::bar::A;",
None, ImportGranularity::Item,
false, false,
false, false,
); );
@ -36,7 +36,7 @@ fn insert_not_group_empty() {
r"use external_crate2::bar::A; r"use external_crate2::bar::A;
", ",
None, ImportGranularity::Item,
false, false,
false, false,
); );
@ -281,7 +281,7 @@ fn insert_empty_module() {
r"{ r"{
use foo::bar; use foo::bar;
}", }",
None, ImportGranularity::Item,
true, true,
true, true,
) )
@ -635,7 +635,7 @@ fn check(
path: &str, path: &str,
ra_fixture_before: &str, ra_fixture_before: &str,
ra_fixture_after: &str, ra_fixture_after: &str,
mb: Option<MergeBehavior>, granularity: ImportGranularity,
module: bool, module: bool,
group: bool, group: bool,
) { ) {
@ -651,21 +651,21 @@ fn check(
.find_map(ast::Path::cast) .find_map(ast::Path::cast)
.unwrap(); .unwrap();
insert_use(&file, path, InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }); insert_use(&file, path, InsertUseConfig { granularity, prefix_kind: PrefixKind::Plain, group });
let result = file.as_syntax_node().to_string(); let result = file.as_syntax_node().to_string();
assert_eq_text!(ra_fixture_after, &result); assert_eq_text!(ra_fixture_after, &result);
} }
fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Crate), false, true) check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Crate, false, true)
} }
fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Module), false, true) check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Module, false, true)
} }
fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
check(path, ra_fixture_before, ra_fixture_after, None, false, true) check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Item, false, true)
} }
fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {

View file

@ -12,8 +12,7 @@ use std::{ffi::OsString, iter, path::PathBuf};
use flycheck::FlycheckConfig; use flycheck::FlycheckConfig;
use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
use ide_db::helpers::{ use ide_db::helpers::{
insert_use::{InsertUseConfig, PrefixKind}, insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
merge_imports::MergeBehavior,
SnippetCap, SnippetCap,
}; };
use lsp_types::{ClientCapabilities, MarkupKind}; use lsp_types::{ClientCapabilities, MarkupKind};
@ -35,8 +34,9 @@ use crate::{
config_data! { config_data! {
struct ConfigData { struct ConfigData {
/// The strategy to use when inserting new imports or merging imports. /// The strategy to use when inserting new imports or merging imports.
assist_importGranularity |
assist_importMergeBehavior | assist_importMergeBehavior |
assist_importMergeBehaviour: MergeBehaviorDef = "\"crate\"", assist_importMergeBehaviour: ImportGranularityDef = "\"preserve\"",
/// The path structure for newly inserted paths to use. /// The path structure for newly inserted paths to use.
assist_importPrefix: ImportPrefixDef = "\"plain\"", assist_importPrefix: ImportPrefixDef = "\"plain\"",
/// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
@ -609,10 +609,11 @@ impl Config {
} }
fn insert_use_config(&self) -> InsertUseConfig { fn insert_use_config(&self) -> InsertUseConfig {
InsertUseConfig { InsertUseConfig {
merge: match self.data.assist_importMergeBehavior { granularity: match self.data.assist_importGranularity {
MergeBehaviorDef::None => None, ImportGranularityDef::Preserve => ImportGranularity::Preserve,
MergeBehaviorDef::Crate => Some(MergeBehavior::Crate), ImportGranularityDef::Item => ImportGranularity::Item,
MergeBehaviorDef::Module => Some(MergeBehavior::Module), ImportGranularityDef::Crate => ImportGranularity::Crate,
ImportGranularityDef::Module => ImportGranularity::Module,
}, },
prefix_kind: match self.data.assist_importPrefix { prefix_kind: match self.data.assist_importPrefix {
ImportPrefixDef::Plain => PrefixKind::Plain, ImportPrefixDef::Plain => PrefixKind::Plain,
@ -717,8 +718,10 @@ enum ManifestOrProjectJson {
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
enum MergeBehaviorDef { enum ImportGranularityDef {
None, #[serde(alias = "none")]
Item,
Preserve,
#[serde(alias = "full")] #[serde(alias = "full")]
Crate, Crate,
#[serde(alias = "last")] #[serde(alias = "last")]
@ -737,7 +740,7 @@ macro_rules! _config_data {
(struct $name:ident { (struct $name:ident {
$( $(
$(#[doc=$doc:literal])* $(#[doc=$doc:literal])*
$field:ident $(| $alias:ident)?: $ty:ty = $default:expr, $field:ident $(| $alias:ident)*: $ty:ty = $default:expr,
)* )*
}) => { }) => {
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -749,7 +752,7 @@ macro_rules! _config_data {
$field: get_field( $field: get_field(
&mut json, &mut json,
stringify!($field), stringify!($field),
None$(.or(Some(stringify!($alias))))?, None$(.or(Some(stringify!($alias))))*,
$default, $default,
), ),
)*} )*}

View file

@ -13,7 +13,10 @@
use std::{convert::TryFrom, sync::Arc}; use std::{convert::TryFrom, sync::Arc};
use ide::{Change, CompletionConfig, FilePosition, TextSize}; use ide::{Change, CompletionConfig, FilePosition, TextSize};
use ide_db::helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap}; use ide_db::helpers::{
insert_use::{ImportGranularity, InsertUseConfig},
SnippetCap,
};
use test_utils::project_root; use test_utils::project_root;
use vfs::{AbsPathBuf, VfsPath}; use vfs::{AbsPathBuf, VfsPath};
@ -133,7 +136,7 @@ fn integrated_completion_benchmark() {
add_call_argument_snippets: true, add_call_argument_snippets: true,
snippet_cap: SnippetCap::new(true), snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig { insert_use: InsertUseConfig {
merge: Some(MergeBehavior::Crate), granularity: ImportGranularity::Crate,
prefix_kind: hir::PrefixKind::ByCrate, prefix_kind: hir::PrefixKind::ByCrate,
group: true, group: true,
}, },
@ -166,7 +169,7 @@ fn integrated_completion_benchmark() {
add_call_argument_snippets: true, add_call_argument_snippets: true,
snippet_cap: SnippetCap::new(true), snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig { insert_use: InsertUseConfig {
merge: Some(MergeBehavior::Crate), granularity: ImportGranularity::Crate,
prefix_kind: hir::PrefixKind::ByCrate, prefix_kind: hir::PrefixKind::ByCrate,
group: true, group: true,
}, },

View file

@ -1145,7 +1145,7 @@ mod tests {
use ide::Analysis; use ide::Analysis;
use ide_db::helpers::{ use ide_db::helpers::{
insert_use::{InsertUseConfig, PrefixKind}, insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
SnippetCap, SnippetCap,
}; };
@ -1177,7 +1177,7 @@ mod tests {
add_call_argument_snippets: true, add_call_argument_snippets: true,
snippet_cap: SnippetCap::new(true), snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig { insert_use: InsertUseConfig {
merge: None, granularity: ImportGranularity::Item,
prefix_kind: PrefixKind::Plain, prefix_kind: PrefixKind::Plain,
group: true, group: true,
}, },

View file

@ -385,19 +385,21 @@
"markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
}, },
"$generated-start": false, "$generated-start": false,
"rust-analyzer.assist.importMergeBehavior": { "rust-analyzer.assist.importGranularity": {
"markdownDescription": "The strategy to use when inserting new imports or merging imports.", "markdownDescription": "How imports should be grouped into use statements.",
"default": "crate", "default": "preserve",
"type": "string", "type": "string",
"enum": [ "enum": [
"none", "preserve",
"crate", "crate",
"module" "module",
"item"
], ],
"enumDescriptions": [ "enumDescriptions": [
"Do not merge imports at all.", "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.", "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." "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."
] ]
}, },
"rust-analyzer.assist.importPrefix": { "rust-analyzer.assist.importPrefix": {