Skip to content

Commit 63123ab

Browse files
committed
Auto merge of #16417 - davidsemakula:normalize-use-trees, r=Veykril
feat: "Normalize import" assist and utilities for normalizing use trees - Add import/use tree normalization utilities - Add "normalize import" assist - Update "merge imports" assist to always apply to the covering use item except for nested use tree selections - Update "merge imports" assist to avoid adding unnecessary braces when merging nested use tree selections See [this discussion](#16372 (comment)) for the motivation for the new "normalize import" assist and changes to the "merge imports" assist.
2 parents da4d5f8 + dba3fc4 commit 63123ab

File tree

9 files changed

+642
-147
lines changed

9 files changed

+642
-147
lines changed

crates/ide-assists/src/handlers/merge_imports.rs

Lines changed: 76 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use either::Either;
22
use ide_db::imports::{
33
insert_use::{ImportGranularity, InsertUseConfig},
4-
merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior},
4+
merge_imports::{try_merge_imports, try_merge_trees, try_normalize_use_tree, MergeBehavior},
55
};
6+
use itertools::Itertools;
67
use syntax::{
78
algo::neighbor,
89
ast::{self, edit_in_place::Removable},
@@ -32,24 +33,13 @@ use Edit::*;
3233
pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
3334
let (target, edits) = if ctx.has_empty_selection() {
3435
// Merge a neighbor
35-
let mut tree: ast::UseTree = ctx.find_node_at_offset()?;
36-
if ctx.config.insert_use.granularity == ImportGranularity::One
37-
&& tree.parent_use_tree_list().is_some()
38-
{
39-
cov_mark::hit!(resolve_top_use_tree_for_import_one);
40-
tree = tree.top_use_tree();
41-
}
36+
cov_mark::hit!(merge_with_use_item_neighbors);
37+
let tree = ctx.find_node_at_offset::<ast::UseTree>()?.top_use_tree();
4238
let target = tree.syntax().text_range();
4339

44-
let edits = if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
45-
cov_mark::hit!(merge_with_use_item_neighbors);
46-
let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
47-
use_item.try_merge_from(&mut neighbor, &ctx.config.insert_use)
48-
} else {
49-
cov_mark::hit!(merge_with_use_tree_neighbors);
50-
let mut neighbor = next_prev().find_map(|dir| neighbor(&tree, dir)).into_iter();
51-
tree.clone().try_merge_from(&mut neighbor, &ctx.config.insert_use)
52-
};
40+
let use_item = tree.syntax().parent().and_then(ast::Use::cast)?;
41+
let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
42+
let edits = use_item.try_merge_from(&mut neighbor, &ctx.config.insert_use);
5343
(target, edits?)
5444
} else {
5545
// Merge selected
@@ -94,7 +84,35 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
9484
for edit in edits_mut {
9585
match edit {
9686
Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
97-
Replace(old, new) => ted::replace(old, new),
87+
Replace(old, new) => {
88+
ted::replace(old, &new);
89+
90+
// If there's a selection and we're replacing a use tree in a tree list,
91+
// normalize the parent use tree if it only contains the merged subtree.
92+
if !ctx.has_empty_selection() {
93+
let normalized_use_tree = ast::UseTree::cast(new)
94+
.as_ref()
95+
.and_then(ast::UseTree::parent_use_tree_list)
96+
.and_then(|use_tree_list| {
97+
if use_tree_list.use_trees().collect_tuple::<(_,)>().is_some() {
98+
Some(use_tree_list.parent_use_tree())
99+
} else {
100+
None
101+
}
102+
})
103+
.and_then(|target_tree| {
104+
try_normalize_use_tree(
105+
&target_tree,
106+
ctx.config.insert_use.granularity.into(),
107+
)
108+
.map(|top_use_tree_flat| (target_tree, top_use_tree_flat))
109+
});
110+
if let Some((old_tree, new_tree)) = normalized_use_tree {
111+
cov_mark::hit!(replace_parent_with_normalized_use_tree);
112+
ted::replace(old_tree.syntax(), new_tree.syntax());
113+
}
114+
}
115+
}
98116
}
99117
}
100118
},
@@ -201,20 +219,17 @@ use std::fmt$0::{Display, Debug};
201219
use std::fmt::{Display, Debug};
202220
",
203221
r"
204-
use std::fmt::{Display, Debug};
222+
use std::fmt::{Debug, Display};
205223
",
206224
);
207225

208226
// The assist macro below calls `check_assist_import_one` 4 times with different input
209-
// use item variations based on the first 2 input parameters, but only 2 calls
210-
// contain `use {std::fmt$0::{Display, Debug}};` for which the top use tree will need
211-
// to be resolved.
212-
cov_mark::check_count!(resolve_top_use_tree_for_import_one, 2);
227+
// use item variations based on the first 2 input parameters.
213228
cov_mark::check_count!(merge_with_use_item_neighbors, 4);
214229
check_assist_import_one_variations!(
215230
"std::fmt$0::{Display, Debug}",
216231
"std::fmt::{Display, Debug}",
217-
"use {std::fmt::{Display, Debug}};"
232+
"use {std::fmt::{Debug, Display}};"
218233
);
219234
}
220235

@@ -257,7 +272,7 @@ use std::fmt::{Debug, Display};
257272
}
258273

259274
#[test]
260-
fn merge_self1() {
275+
fn merge_self() {
261276
check_assist(
262277
merge_imports,
263278
r"
@@ -276,21 +291,8 @@ use std::fmt::{self, Display};
276291
}
277292

278293
#[test]
279-
fn merge_self2() {
280-
check_assist(
281-
merge_imports,
282-
r"
283-
use std::{fmt, $0fmt::Display};
284-
",
285-
r"
286-
use std::{fmt::{self, Display}};
287-
",
288-
);
289-
}
290-
291-
#[test]
292-
fn not_applicable_to_single_one_style_import() {
293-
cov_mark::check!(resolve_top_use_tree_for_import_one);
294+
fn not_applicable_to_single_import() {
295+
check_assist_not_applicable(merge_imports, "use std::{fmt, $0fmt::Display};");
294296
check_assist_not_applicable_for_import_one(
295297
merge_imports,
296298
"use {std::{fmt, $0fmt::Display}};",
@@ -385,14 +387,14 @@ pub(in this::path) use std::fmt::{Debug, Display};
385387

386388
#[test]
387389
fn test_merge_nested() {
388-
cov_mark::check!(merge_with_use_tree_neighbors);
389390
check_assist(
390391
merge_imports,
391392
r"
392-
use std::{fmt$0::Debug, fmt::Display};
393+
use std::{fmt$0::Debug, fmt::Error};
394+
use std::{fmt::Write, fmt::Display};
393395
",
394396
r"
395-
use std::{fmt::{Debug, Display}};
397+
use std::fmt::{Debug, Display, Error, Write};
396398
",
397399
);
398400
}
@@ -402,10 +404,11 @@ use std::{fmt::{Debug, Display}};
402404
check_assist(
403405
merge_imports,
404406
r"
405-
use std::{fmt::Debug, fmt$0::Display};
407+
use std::{fmt::Debug, fmt$0::Error};
408+
use std::{fmt::Write, fmt::Display};
406409
",
407410
r"
408-
use std::{fmt::{Debug, Display}};
411+
use std::fmt::{Debug, Display, Error, Write};
409412
",
410413
);
411414
}
@@ -419,13 +422,13 @@ use std$0::{fmt::{Write, Display}};
419422
use std::{fmt::{self, Debug}};
420423
",
421424
r"
422-
use std::{fmt::{self, Debug, Display, Write}};
425+
use std::fmt::{self, Debug, Display, Write};
423426
",
424427
);
425428
check_assist_import_one_variations!(
426429
"std$0::{fmt::{Write, Display}}",
427430
"std::{fmt::{self, Debug}}",
428-
"use {std::{fmt::{self, Debug, Display, Write}}};"
431+
"use {std::fmt::{self, Debug, Display, Write}};"
429432
);
430433
}
431434

@@ -438,26 +441,13 @@ use std$0::{fmt::{self, Debug}};
438441
use std::{fmt::{Write, Display}};
439442
",
440443
r"
441-
use std::{fmt::{self, Debug, Display, Write}};
444+
use std::fmt::{self, Debug, Display, Write};
442445
",
443446
);
444447
check_assist_import_one_variations!(
445448
"std$0::{fmt::{self, Debug}}",
446449
"std::{fmt::{Write, Display}}",
447-
"use {std::{fmt::{self, Debug, Display, Write}}};"
448-
);
449-
}
450-
451-
#[test]
452-
fn test_merge_self_with_nested_self_item() {
453-
check_assist(
454-
merge_imports,
455-
r"
456-
use std::{fmt$0::{self, Debug}, fmt::{Write, Display}};
457-
",
458-
r"
459-
use std::{fmt::{self, Debug, Display, Write}};
460-
",
450+
"use {std::fmt::{self, Debug, Display, Write}};"
461451
);
462452
}
463453

@@ -470,13 +460,13 @@ use foo::$0{bar::{self}};
470460
use foo::{bar};
471461
",
472462
r"
473-
use foo::{bar::{self}};
463+
use foo::bar;
474464
",
475465
);
476466
check_assist_import_one_variations!(
477467
"foo::$0{bar::{self}}",
478468
"foo::{bar}",
479-
"use {foo::{bar::{self}}};"
469+
"use {foo::bar};"
480470
);
481471
}
482472

@@ -489,13 +479,13 @@ use foo::$0{bar};
489479
use foo::{bar::{self}};
490480
",
491481
r"
492-
use foo::{bar::{self}};
482+
use foo::bar;
493483
",
494484
);
495485
check_assist_import_one_variations!(
496486
"foo::$0{bar}",
497487
"foo::{bar::{self}}",
498-
"use {foo::{bar::{self}}};"
488+
"use {foo::bar};"
499489
);
500490
}
501491

@@ -508,13 +498,13 @@ use std$0::{fmt::*};
508498
use std::{fmt::{self, Display}};
509499
",
510500
r"
511-
use std::{fmt::{self, Display, *}};
501+
use std::fmt::{self, Display, *};
512502
",
513503
);
514504
check_assist_import_one_variations!(
515505
"std$0::{fmt::*}",
516506
"std::{fmt::{self, Display}}",
517-
"use {std::{fmt::{self, Display, *}}};"
507+
"use {std::fmt::{self, Display, *}};"
518508
);
519509
}
520510

@@ -579,29 +569,27 @@ use foo::{bar, baz};
579569
check_assist(
580570
merge_imports,
581571
r"
582-
use {
583-
foo$0::bar,
584-
foo::baz,
572+
use foo$0::{
573+
bar, baz,
585574
};
575+
use foo::qux;
586576
",
587577
r"
588-
use {
589-
foo::{bar, baz},
578+
use foo::{
579+
bar, baz, qux,
590580
};
591581
",
592582
);
593583
check_assist(
594584
merge_imports,
595585
r"
596-
use {
597-
foo::baz,
598-
foo$0::bar,
586+
use foo::{
587+
baz, bar,
599588
};
589+
use foo$0::qux;
600590
",
601591
r"
602-
use {
603-
foo::{bar, baz},
604-
};
592+
use foo::{bar, baz, qux};
605593
",
606594
);
607595
}
@@ -711,12 +699,19 @@ use std::{
711699
};",
712700
);
713701

714-
// FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
715702
cov_mark::check!(merge_with_selected_use_tree_neighbors);
703+
check_assist(
704+
merge_imports,
705+
r"use std::{fmt::Result, $0fmt::Display, fmt::Debug$0};",
706+
r"use std::{fmt::Result, fmt::{Debug, Display}};",
707+
);
708+
709+
cov_mark::check!(merge_with_selected_use_tree_neighbors);
710+
cov_mark::check!(replace_parent_with_normalized_use_tree);
716711
check_assist(
717712
merge_imports,
718713
r"use std::$0{fmt::Display, fmt::Debug}$0;",
719-
r"use std::{fmt::{Debug, Display}};",
714+
r"use std::fmt::{Debug, Display};",
720715
);
721716
}
722717
}

0 commit comments

Comments
 (0)