From a8a18f3ccf99a120e4dcbcdb0358a9ef701789f5 Mon Sep 17 00:00:00 2001 From: davidsemakula Date: Sat, 20 Jan 2024 19:49:16 +0300 Subject: [PATCH] add normalize import assist --- .../src/handlers/normalize_import.rs | 166 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 13 ++ 3 files changed, 181 insertions(+) create mode 100644 crates/ide-assists/src/handlers/normalize_import.rs 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 0000000000..42076c4ff1 --- /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 edcf52a9b3..2fec104323 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 0ce89ae0a9..8d7c49d52c 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -2217,6 +2217,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(