2 use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
5 ast::{self, edit_in_place::Removable},
6 match_ast, ted, AstNode, SyntaxElement, SyntaxNode,
10 assist_context::{AssistContext, Assists},
17 // Assist: merge_imports
19 // Merges two imports with a common prefix.
22 // use std::$0fmt::Formatter;
27 // use std::{fmt::Formatter, io};
29 pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
30 let (target, edits) = if ctx.has_empty_selection() {
32 let tree: ast::UseTree = ctx.find_node_at_offset()?;
33 let target = tree.syntax().text_range();
35 let edits = if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
36 let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
37 use_item.try_merge_from(&mut neighbor)
39 let mut neighbor = next_prev().find_map(|dir| neighbor(&tree, dir)).into_iter();
40 tree.try_merge_from(&mut neighbor)
45 let selection_range = ctx.selection_trimmed();
46 let parent_node = match ctx.covering_element() {
47 SyntaxElement::Node(n) => n,
48 SyntaxElement::Token(t) => t.parent()?,
50 let mut selected_nodes =
51 parent_node.children().filter(|it| selection_range.contains_range(it.text_range()));
53 let first_selected = selected_nodes.next()?;
54 let edits = match_ast! {
55 match first_selected {
56 ast::Use(use_item) => {
57 use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast))
59 ast::UseTree(use_tree) => {
60 use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast))
65 (selection_range, edits?)
69 AssistId("merge_imports", AssistKind::RefactorRewrite),
73 let edits_mut: Vec<Edit> = edits
76 Remove(Either::Left(it)) => Remove(Either::Left(builder.make_mut(it))),
77 Remove(Either::Right(it)) => Remove(Either::Right(builder.make_mut(it))),
78 Replace(old, new) => Replace(builder.make_syntax_mut(old), new),
81 for edit in edits_mut {
83 Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
84 Replace(old, new) => ted::replace(old, new),
91 trait Merge: AstNode + Clone {
92 fn try_merge_from(self, items: &mut dyn Iterator<Item = Self>) -> Option<Vec<Edit>> {
93 let mut edits = Vec::new();
94 let mut merged = self.clone();
95 while let Some(item) = items.next() {
96 merged = merged.try_merge(&item)?;
97 edits.push(Edit::Remove(item.into_either()));
99 if !edits.is_empty() {
100 edits.push(Edit::replace(self, merged));
106 fn try_merge(&self, other: &Self) -> Option<Self>;
107 fn into_either(self) -> Either<ast::Use, ast::UseTree>;
110 impl Merge for ast::Use {
111 fn try_merge(&self, other: &Self) -> Option<Self> {
112 try_merge_imports(self, other, MergeBehavior::Crate)
114 fn into_either(self) -> Either<ast::Use, ast::UseTree> {
119 impl Merge for ast::UseTree {
120 fn try_merge(&self, other: &Self) -> Option<Self> {
121 try_merge_trees(self, other, MergeBehavior::Crate)
123 fn into_either(self) -> Either<ast::Use, ast::UseTree> {
129 Remove(Either<ast::Use, ast::UseTree>),
130 Replace(SyntaxNode, SyntaxNode),
134 fn replace(old: impl AstNode, new: impl AstNode) -> Self {
135 Edit::Replace(old.syntax().clone(), new.syntax().clone())
141 use crate::tests::{check_assist, check_assist_not_applicable};
146 fn test_merge_equal() {
150 use std::fmt$0::{Display, Debug};
151 use std::fmt::{Display, Debug};
154 use std::fmt::{Display, Debug};
160 fn test_merge_first() {
164 use std::fmt$0::Debug;
165 use std::fmt::Display;
168 use std::fmt::{Debug, Display};
174 fn test_merge_second() {
179 use std::fmt$0::Display;
182 use std::fmt::{Display, Debug};
193 use std::fmt::Display;
196 use std::fmt::{self, Display};
206 use std::{fmt, $0fmt::Display};
209 use std::{fmt::{Display, self}};
216 check_assist_not_applicable(
219 pub use std::fmt$0::Debug;
220 use std::fmt::Display;
227 check_assist_not_applicable(
230 use std::fmt$0::Debug;
231 pub use std::fmt::Display;
237 fn skip_pub_crate_pub() {
238 check_assist_not_applicable(
241 pub(crate) use std::fmt$0::Debug;
242 pub use std::fmt::Display;
248 fn skip_pub_pub_crate() {
249 check_assist_not_applicable(
252 pub use std::fmt$0::Debug;
253 pub(crate) use std::fmt::Display;
263 pub use std::fmt$0::Debug;
264 pub use std::fmt::Display;
267 pub use std::fmt::{Debug, Display};
273 fn merge_pub_crate() {
277 pub(crate) use std::fmt$0::Debug;
278 pub(crate) use std::fmt::Display;
281 pub(crate) use std::fmt::{Debug, Display};
287 fn merge_pub_in_path_crate() {
291 pub(in this::path) use std::fmt$0::Debug;
292 pub(in this::path) use std::fmt::Display;
295 pub(in this::path) use std::fmt::{Debug, Display};
301 fn test_merge_nested() {
305 use std::{fmt$0::Debug, fmt::Display};
308 use std::{fmt::{Debug, Display}};
314 fn test_merge_nested2() {
318 use std::{fmt::Debug, fmt$0::Display};
321 use std::{fmt::{Display, Debug}};
327 fn test_merge_with_nested_self_item() {
331 use std$0::{fmt::{Write, Display}};
332 use std::{fmt::{self, Debug}};
335 use std::{fmt::{Write, Display, self, Debug}};
341 fn test_merge_with_nested_self_item2() {
345 use std$0::{fmt::{self, Debug}};
346 use std::{fmt::{Write, Display}};
349 use std::{fmt::{self, Debug, Write, Display}};
355 fn test_merge_self_with_nested_self_item() {
359 use std::{fmt$0::{self, Debug}, fmt::{Write, Display}};
362 use std::{fmt::{self, Debug, Write, Display}};
368 fn test_merge_nested_self_and_empty() {
372 use foo::$0{bar::{self}};
376 use foo::{bar::{self}};
382 fn test_merge_nested_empty_and_self() {
387 use foo::{bar::{self}};
390 use foo::{bar::{self}};
396 fn test_merge_nested_list_self_and_glob() {
401 use std::{fmt::{self, Display}};
404 use std::{fmt::{*, self, Display}};
410 fn test_merge_single_wildcard_diff_prefixes() {
418 use std::{cell::*, str};
424 fn test_merge_both_wildcard_diff_prefixes() {
432 use std::{cell::*, str::*};
438 fn removes_just_enough_whitespace() {
456 fn works_with_trailing_comma() {
488 fn test_double_comma() {
506 fn test_empty_use() {
507 check_assist_not_applicable(
524 use foo::{*, bar::Baz};
530 fn merge_selection_uses() {
535 $0use std::fmt::Display;
538 $0use std::fmt::Result;
542 use std::fmt::{Display, Debug, Write};
543 use std::fmt::Result;
549 fn merge_selection_use_trees() {
563 fmt::{Display, Debug, Write},
567 // FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
570 r"use std::$0{fmt::Display, fmt::Debug}$0;",
571 r"use std::{fmt::{Display, Debug}};",