about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-assists/src/handlers/normalize_import.rs166
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs13
3 files changed, 181 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/normalize_import.rs b/crates/ide-assists/src/handlers/normalize_import.rs
new file mode 100644
index 00000000000..42076c4ff19
--- /dev/null
+++ b/crates/ide-assists/src/handlers/normalize_import.rs
@@ -0,0 +1,166 @@
+use ide_db::imports::merge_imports::try_normalize_import;
+use syntax::{ast, AstNode};
+
+use crate::{
+    assist_context::{AssistContext, Assists},
+    AssistId, AssistKind,
+};
+
+// Assist: normalize_import
+//
+// Normalizes an import.
+//
+// ```
+// use$0 std::{io, {fmt::Formatter}};
+// ```
+// ->
+// ```
+// use std::{fmt::Formatter, io};
+// ```
+pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let use_item = if ctx.has_empty_selection() {
+        ctx.find_node_at_offset()?
+    } else {
+        ctx.covering_element().ancestors().find_map(ast::Use::cast)?
+    };
+
+    let target = use_item.syntax().text_range();
+    let normalized_use_item =
+        try_normalize_import(&use_item, ctx.config.insert_use.granularity.into())?;
+
+    acc.add(
+        AssistId("normalize_import", AssistKind::RefactorRewrite),
+        "Normalize import",
+        target,
+        |builder| {
+            builder.replace_ast(use_item, normalized_use_item);
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{
+        check_assist, check_assist_import_one, check_assist_not_applicable,
+        check_assist_not_applicable_for_import_one,
+    };
+
+    use super::*;
+
+    macro_rules! check_assist_variations {
+        ($fixture: literal, $expected: literal) => {
+            check_assist(
+                normalize_import,
+                concat!("use $0", $fixture, ";"),
+                concat!("use ", $expected, ";"),
+            );
+            check_assist(
+                normalize_import,
+                concat!("$0use ", $fixture, ";"),
+                concat!("use ", $expected, ";"),
+            );
+
+            check_assist_import_one(
+                normalize_import,
+                concat!("use $0", $fixture, ";"),
+                concat!("use {", $expected, "};"),
+            );
+            check_assist_import_one(
+                normalize_import,
+                concat!("$0use ", $fixture, ";"),
+                concat!("use {", $expected, "};"),
+            );
+
+            check_assist_import_one(
+                normalize_import,
+                concat!("use $0{", $fixture, "};"),
+                concat!("use {", $expected, "};"),
+            );
+            check_assist_import_one(
+                normalize_import,
+                concat!("$0use {", $fixture, "};"),
+                concat!("use {", $expected, "};"),
+            );
+
+            check_assist(
+                normalize_import,
+                concat!("use $0", $fixture, "$0;"),
+                concat!("use ", $expected, ";"),
+            );
+            check_assist(
+                normalize_import,
+                concat!("$0use ", $fixture, ";$0"),
+                concat!("use ", $expected, ";"),
+            );
+        };
+    }
+
+    macro_rules! check_assist_not_applicable_variations {
+        ($fixture: literal) => {
+            check_assist_not_applicable(normalize_import, concat!("use $0", $fixture, ";"));
+            check_assist_not_applicable(normalize_import, concat!("$0use ", $fixture, ";"));
+
+            check_assist_not_applicable_for_import_one(
+                normalize_import,
+                concat!("use $0{", $fixture, "};"),
+            );
+            check_assist_not_applicable_for_import_one(
+                normalize_import,
+                concat!("$0use {", $fixture, "};"),
+            );
+        };
+    }
+
+    #[test]
+    fn test_order() {
+        check_assist_variations!(
+            "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz}",
+            "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}"
+        );
+    }
+
+    #[test]
+    fn test_redundant_braces() {
+        check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{baz, Qux}");
+        check_assist_variations!("foo::{bar::{self}}", "foo::bar");
+        check_assist_variations!("foo::{bar::{*}}", "foo::bar::*");
+        check_assist_variations!("foo::{bar::{Qux as Quux}}", "foo::bar::Qux as Quux");
+        check_assist_variations!(
+            "foo::bar::{{FOO_BAZ, Qux, self}, {*, baz}}",
+            "foo::bar::{self, baz, Qux, FOO_BAZ, *}"
+        );
+        check_assist_variations!(
+            "foo::bar::{{{FOO_BAZ}, {{Qux}, {self}}}, {{*}, {baz}}}",
+            "foo::bar::{self, baz, Qux, FOO_BAZ, *}"
+        );
+    }
+
+    #[test]
+    fn test_merge() {
+        check_assist_variations!(
+            "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux}}",
+            "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}"
+        );
+        check_assist_variations!(
+            "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux, bar::{baz::Foo}}}",
+            "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}"
+        );
+    }
+
+    #[test]
+    fn not_applicable_to_normalized_import() {
+        check_assist_not_applicable_variations!("foo::bar");
+        check_assist_not_applicable_variations!("foo::bar::*");
+        check_assist_not_applicable_variations!("foo::bar::Qux as Quux");
+        check_assist_not_applicable_variations!("foo::bar::{self, baz, Qux, FOO_BAZ, *}");
+        check_assist_not_applicable_variations!(
+            "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}"
+        );
+        check_assist_not_applicable_variations!(
+            "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}"
+        );
+        check_assist_not_applicable_variations!(
+            "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}"
+        );
+    }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index edcf52a9b38..2fec104323d 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -183,6 +183,7 @@ mod handlers {
     mod move_guard;
     mod move_module_to_file;
     mod move_to_mod_rs;
+    mod normalize_import;
     mod number_representation;
     mod promote_local_to_const;
     mod pull_assignment_up;
@@ -300,6 +301,7 @@ mod handlers {
             move_module_to_file::move_module_to_file,
             move_to_mod_rs::move_to_mod_rs,
             move_from_mod_rs::move_from_mod_rs,
+            normalize_import::normalize_import,
             number_representation::reformat_number_literal,
             pull_assignment_up::pull_assignment_up,
             promote_local_to_const::promote_local_to_const,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 0ce89ae0a9a..8d7c49d52c2 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2218,6 +2218,19 @@ fn t() {}
 }
 
 #[test]
+fn doctest_normalize_import() {
+    check_doc_test(
+        "normalize_import",
+        r#####"
+use$0 std::{io, {fmt::Formatter}};
+"#####,
+        r#####"
+use std::{fmt::Formatter, io};
+"#####,
+    )
+}
+
+#[test]
 fn doctest_promote_local_to_const() {
     check_doc_test(
         "promote_local_to_const",