ra_ap_ide_assists/handlers/
normalize_import.rs

1use ide_db::imports::merge_imports::try_normalize_import;
2use syntax::{AstNode, ast};
3
4use crate::{
5    AssistId,
6    assist_context::{AssistContext, Assists},
7};
8
9// Assist: normalize_import
10//
11// Normalizes an import.
12//
13// ```
14// use$0 std::{io, {fmt::Formatter}};
15// ```
16// ->
17// ```
18// use std::{fmt::Formatter, io};
19// ```
20pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
21    let use_item = if ctx.has_empty_selection() {
22        ctx.find_node_at_offset()?
23    } else {
24        ctx.covering_element().ancestors().find_map(ast::Use::cast)?
25    };
26
27    let target = use_item.syntax().text_range();
28    let normalized_use_item =
29        try_normalize_import(&use_item, ctx.config.insert_use.granularity.into())?;
30
31    acc.add(AssistId::refactor_rewrite("normalize_import"), "Normalize import", target, |builder| {
32        builder.replace_ast(use_item, normalized_use_item);
33    })
34}
35
36#[cfg(test)]
37mod tests {
38    use crate::tests::{
39        check_assist, check_assist_import_one, check_assist_not_applicable,
40        check_assist_not_applicable_for_import_one,
41    };
42
43    use super::*;
44
45    macro_rules! check_assist_variations {
46        ($fixture: literal, $expected: literal) => {
47            check_assist(
48                normalize_import,
49                concat!("use $0", $fixture, ";"),
50                concat!("use ", $expected, ";"),
51            );
52            check_assist(
53                normalize_import,
54                concat!("$0use ", $fixture, ";"),
55                concat!("use ", $expected, ";"),
56            );
57
58            check_assist_import_one(
59                normalize_import,
60                concat!("use $0", $fixture, ";"),
61                concat!("use {", $expected, "};"),
62            );
63            check_assist_import_one(
64                normalize_import,
65                concat!("$0use ", $fixture, ";"),
66                concat!("use {", $expected, "};"),
67            );
68
69            check_assist_import_one(
70                normalize_import,
71                concat!("use $0{", $fixture, "};"),
72                concat!("use {", $expected, "};"),
73            );
74            check_assist_import_one(
75                normalize_import,
76                concat!("$0use {", $fixture, "};"),
77                concat!("use {", $expected, "};"),
78            );
79
80            check_assist(
81                normalize_import,
82                concat!("use $0", $fixture, "$0;"),
83                concat!("use ", $expected, ";"),
84            );
85            check_assist(
86                normalize_import,
87                concat!("$0use ", $fixture, ";$0"),
88                concat!("use ", $expected, ";"),
89            );
90        };
91    }
92
93    macro_rules! check_assist_not_applicable_variations {
94        ($fixture: literal) => {
95            check_assist_not_applicable(normalize_import, concat!("use $0", $fixture, ";"));
96            check_assist_not_applicable(normalize_import, concat!("$0use ", $fixture, ";"));
97
98            check_assist_not_applicable_for_import_one(
99                normalize_import,
100                concat!("use $0{", $fixture, "};"),
101            );
102            check_assist_not_applicable_for_import_one(
103                normalize_import,
104                concat!("$0use {", $fixture, "};"),
105            );
106        };
107    }
108
109    #[test]
110    fn test_order() {
111        check_assist_variations!(
112            "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz, v10, v9, r#aaa}",
113            "foo::{self, Baz, FOO_BAZ, Qux, r#aaa, bar::{Bar, Quux}, baz, v9, v10, *}"
114        );
115    }
116
117    #[test]
118    fn test_braces_kept() {
119        check_assist_not_applicable_variations!("foo::bar::{$0self}");
120
121        // This code compiles but transforming "bar::{self}" into "bar" causes a
122        // compilation error (the name `bar` is defined multiple times).
123        // Therefore, the normalize_input assist must not apply here.
124        check_assist_not_applicable(
125            normalize_import,
126            r"
127mod foo {
128
129    pub mod bar {}
130
131    pub const bar: i32 = 8;
132}
133
134use foo::bar::{$0self};
135
136const bar: u32 = 99;
137
138fn main() {
139    let local_bar = bar;
140}
141
142",
143        );
144    }
145
146    #[test]
147    fn test_redundant_braces() {
148        check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{Qux, baz}");
149        check_assist_variations!("foo::{bar::{self}}", "foo::bar::{self}");
150        check_assist_variations!("foo::{bar::{*}}", "foo::bar::*");
151        check_assist_variations!("foo::{bar::{Qux as Quux}}", "foo::bar::Qux as Quux");
152        check_assist_variations!(
153            "foo::bar::{{FOO_BAZ, Qux, self}, {*, baz}}",
154            "foo::bar::{self, FOO_BAZ, Qux, baz, *}"
155        );
156        check_assist_variations!(
157            "foo::bar::{{{FOO_BAZ}, {{Qux}, {self}}}, {{*}, {baz}}}",
158            "foo::bar::{self, FOO_BAZ, Qux, baz, *}"
159        );
160    }
161
162    #[test]
163    fn test_merge() {
164        check_assist_variations!(
165            "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux}}",
166            "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}"
167        );
168        check_assist_variations!(
169            "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux, bar::{baz::Foo}}}",
170            "foo::{FOO_BAZ, Quux, bar::{self, baz::{self, Foo}, *}, qux, *}"
171        );
172    }
173
174    #[test]
175    fn test_merge_self() {
176        check_assist_variations!("std::{fmt, fmt::Display}", "std::fmt::{self, Display}");
177    }
178
179    #[test]
180    fn test_merge_nested() {
181        check_assist_variations!("std::{fmt::Debug, fmt::Display}", "std::fmt::{Debug, Display}");
182    }
183
184    #[test]
185    fn test_merge_nested2() {
186        check_assist_variations!("std::{fmt::Debug, fmt::Display}", "std::fmt::{Debug, Display}");
187    }
188
189    #[test]
190    fn test_merge_self_with_nested_self_item() {
191        check_assist_variations!(
192            "std::{fmt::{self, Debug}, fmt::{Write, Display}}",
193            "std::fmt::{self, Debug, Display, Write}"
194        );
195    }
196
197    #[test]
198    fn works_with_trailing_comma() {
199        check_assist(
200            normalize_import,
201            r"
202use $0{
203    foo::bar,
204    foo::baz,
205};
206        ",
207            r"
208use foo::{bar, baz};
209        ",
210        );
211        check_assist_import_one(
212            normalize_import,
213            r"
214use $0{
215    foo::bar,
216    foo::baz,
217};
218",
219            r"
220use {
221    foo::{bar, baz},
222};
223",
224        );
225    }
226
227    #[test]
228    fn not_applicable_to_normalized_import() {
229        check_assist_not_applicable_variations!("foo::bar");
230        check_assist_not_applicable_variations!("foo::bar::*");
231        check_assist_not_applicable_variations!("foo::bar::Qux as Quux");
232        check_assist_not_applicable_variations!("foo::bar::{self, FOO_BAZ, Qux, baz, *}");
233        check_assist_not_applicable_variations!(
234            "foo::{self, Baz, FOO_BAZ, Qux, bar::{Bar, Quux}, baz, *}"
235        );
236        check_assist_not_applicable_variations!(
237            "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}"
238        );
239        check_assist_not_applicable_variations!(
240            "foo::{bar::{self, FOO_BAZ, Quux, baz::{self, Foo}, *}, qux, *}"
241        );
242    }
243}