2 use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
3 use syntax::{algo::neighbor, ast, match_ast, ted, AstNode, SyntaxElement, SyntaxNode};
6 assist_context::{AssistContext, Assists},
13 // Assist: merge_imports
15 // Merges two imports with a common prefix.
18 // use std::$0fmt::Formatter;
23 // use std::{fmt::Formatter, io};
25 pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
26 let (target, edits) = if ctx.has_empty_selection() {
28 let tree: ast::UseTree = ctx.find_node_at_offset()?;
29 let target = tree.syntax().text_range();
31 let edits = if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
32 let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
33 use_item.try_merge_from(&mut neighbor)
35 let mut neighbor = next_prev().find_map(|dir| neighbor(&tree, dir)).into_iter();
36 tree.try_merge_from(&mut neighbor)
41 let selection_range = ctx.selection_trimmed();
42 let parent_node = match ctx.covering_element() {
43 SyntaxElement::Node(n) => n,
44 SyntaxElement::Token(t) => t.parent()?,
46 let mut selected_nodes =
47 parent_node.children().filter(|it| selection_range.contains_range(it.text_range()));
49 let first_selected = selected_nodes.next()?;
50 let edits = match_ast! {
51 match first_selected {
52 ast::Use(use_item) => {
53 use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast))
55 ast::UseTree(use_tree) => {
56 use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast))
61 (selection_range, edits?)
65 AssistId("merge_imports", AssistKind::RefactorRewrite),
69 let edits_mut: Vec<Edit> = edits
72 Remove(Either::Left(it)) => Remove(Either::Left(builder.make_mut(it))),
73 Remove(Either::Right(it)) => Remove(Either::Right(builder.make_mut(it))),
74 Replace(old, new) => Replace(builder.make_syntax_mut(old), new),
77 for edit in edits_mut {
79 Remove(it) => it.as_ref().either(ast::Use::remove, ast::UseTree::remove),
80 Replace(old, new) => ted::replace(old, new),
87 trait Merge: AstNode + Clone {
88 fn try_merge_from(self, items: &mut dyn Iterator<Item = Self>) -> Option<Vec<Edit>> {
89 let mut edits = Vec::new();
90 let mut merged = self.clone();
91 while let Some(item) = items.next() {
92 merged = merged.try_merge(&item)?;
93 edits.push(Edit::Remove(item.into_either()));
95 if !edits.is_empty() {
96 edits.push(Edit::replace(self, merged));
102 fn try_merge(&self, other: &Self) -> Option<Self>;
103 fn into_either(self) -> Either<ast::Use, ast::UseTree>;
106 impl Merge for ast::Use {
107 fn try_merge(&self, other: &Self) -> Option<Self> {
108 try_merge_imports(self, other, MergeBehavior::Crate)
110 fn into_either(self) -> Either<ast::Use, ast::UseTree> {
115 impl Merge for ast::UseTree {
116 fn try_merge(&self, other: &Self) -> Option<Self> {
117 try_merge_trees(self, other, MergeBehavior::Crate)
119 fn into_either(self) -> Either<ast::Use, ast::UseTree> {
125 Remove(Either<ast::Use, ast::UseTree>),
126 Replace(SyntaxNode, SyntaxNode),
130 fn replace(old: impl AstNode, new: impl AstNode) -> Self {
131 Edit::Replace(old.syntax().clone(), new.syntax().clone())
137 use crate::tests::{check_assist, check_assist_not_applicable};
142 fn test_merge_equal() {
146 use std::fmt$0::{Display, Debug};
147 use std::fmt::{Display, Debug};
150 use std::fmt::{Display, Debug};
156 fn test_merge_first() {
160 use std::fmt$0::Debug;
161 use std::fmt::Display;
164 use std::fmt::{Debug, Display};
170 fn test_merge_second() {
175 use std::fmt$0::Display;
178 use std::fmt::{Display, Debug};
189 use std::fmt::Display;
192 use std::fmt::{self, Display};
202 use std::{fmt, $0fmt::Display};
205 use std::{fmt::{Display, self}};
212 check_assist_not_applicable(
215 pub use std::fmt$0::Debug;
216 use std::fmt::Display;
223 check_assist_not_applicable(
226 use std::fmt$0::Debug;
227 pub use std::fmt::Display;
233 fn skip_pub_crate_pub() {
234 check_assist_not_applicable(
237 pub(crate) use std::fmt$0::Debug;
238 pub use std::fmt::Display;
244 fn skip_pub_pub_crate() {
245 check_assist_not_applicable(
248 pub use std::fmt$0::Debug;
249 pub(crate) use std::fmt::Display;
259 pub use std::fmt$0::Debug;
260 pub use std::fmt::Display;
263 pub use std::fmt::{Debug, Display};
269 fn merge_pub_crate() {
273 pub(crate) use std::fmt$0::Debug;
274 pub(crate) use std::fmt::Display;
277 pub(crate) use std::fmt::{Debug, Display};
283 fn merge_pub_in_path_crate() {
287 pub(in this::path) use std::fmt$0::Debug;
288 pub(in this::path) use std::fmt::Display;
291 pub(in this::path) use std::fmt::{Debug, Display};
297 fn test_merge_nested() {
301 use std::{fmt$0::Debug, fmt::Display};
304 use std::{fmt::{Debug, Display}};
310 fn test_merge_nested2() {
314 use std::{fmt::Debug, fmt$0::Display};
317 use std::{fmt::{Display, Debug}};
323 fn test_merge_with_nested_self_item() {
327 use std$0::{fmt::{Write, Display}};
328 use std::{fmt::{self, Debug}};
331 use std::{fmt::{Write, Display, self, Debug}};
337 fn test_merge_with_nested_self_item2() {
341 use std$0::{fmt::{self, Debug}};
342 use std::{fmt::{Write, Display}};
345 use std::{fmt::{self, Debug, Write, Display}};
351 fn test_merge_self_with_nested_self_item() {
355 use std::{fmt$0::{self, Debug}, fmt::{Write, Display}};
358 use std::{fmt::{self, Debug, Write, Display}};
364 fn test_merge_nested_self_and_empty() {
368 use foo::$0{bar::{self}};
372 use foo::{bar::{self}};
378 fn test_merge_nested_empty_and_self() {
383 use foo::{bar::{self}};
386 use foo::{bar::{self}};
392 fn test_merge_nested_list_self_and_glob() {
397 use std::{fmt::{self, Display}};
400 use std::{fmt::{*, self, Display}};
406 fn test_merge_single_wildcard_diff_prefixes() {
414 use std::{cell::*, str};
420 fn test_merge_both_wildcard_diff_prefixes() {
428 use std::{cell::*, str::*};
434 fn removes_just_enough_whitespace() {
452 fn works_with_trailing_comma() {
484 fn test_double_comma() {
502 fn test_empty_use() {
503 check_assist_not_applicable(
520 use foo::{*, bar::Baz};
526 fn merge_selection_uses() {
531 $0use std::fmt::Display;
534 $0use std::fmt::Result;
538 use std::fmt::{Display, Debug, Write};
539 use std::fmt::Result;
545 fn merge_selection_use_trees() {
559 fmt::{Display, Debug, Write},
563 // FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
566 r"use std::$0{fmt::Display, fmt::Debug}$0;",
567 r"use std::{fmt::{Display, Debug}};",