11use either:: Either ;
2- use ide_db:: imports:: merge_imports:: { try_merge_imports, try_merge_trees, MergeBehavior } ;
2+ use ide_db:: imports:: {
3+ insert_use:: { ImportGranularity , InsertUseConfig } ,
4+ merge_imports:: { try_merge_imports, try_merge_trees, MergeBehavior } ,
5+ } ;
36use syntax:: {
47 algo:: neighbor,
58 ast:: { self , edit_in_place:: Removable } ,
@@ -16,7 +19,7 @@ use Edit::*;
1619
1720// Assist: merge_imports
1821//
19- // Merges two imports with a common prefix.
22+ // Merges neighbor imports with a common prefix.
2023//
2124// ```
2225// use std::$0fmt::Formatter;
@@ -29,15 +32,23 @@ use Edit::*;
2932pub ( crate ) fn merge_imports ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
3033 let ( target, edits) = if ctx. has_empty_selection ( ) {
3134 // Merge a neighbor
32- let tree: ast:: UseTree = ctx. find_node_at_offset ( ) ?;
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+ }
3342 let target = tree. syntax ( ) . text_range ( ) ;
3443
3544 let edits = if let Some ( use_item) = tree. syntax ( ) . parent ( ) . and_then ( ast:: Use :: cast) {
45+ cov_mark:: hit!( merge_with_use_item_neighbors) ;
3646 let mut neighbor = next_prev ( ) . find_map ( |dir| neighbor ( & use_item, dir) ) . into_iter ( ) ;
37- use_item. try_merge_from ( & mut neighbor)
47+ use_item. try_merge_from ( & mut neighbor, & ctx . config . insert_use )
3848 } else {
49+ cov_mark:: hit!( merge_with_use_tree_neighbors) ;
3950 let mut neighbor = next_prev ( ) . find_map ( |dir| neighbor ( & tree, dir) ) . into_iter ( ) ;
40- tree. try_merge_from ( & mut neighbor)
51+ tree. clone ( ) . try_merge_from ( & mut neighbor, & ctx . config . insert_use )
4152 } ;
4253 ( target, edits?)
4354 } else {
@@ -54,10 +65,12 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
5465 let edits = match_ast ! {
5566 match first_selected {
5667 ast:: Use ( use_item) => {
57- use_item. try_merge_from( & mut selected_nodes. filter_map( ast:: Use :: cast) )
68+ cov_mark:: hit!( merge_with_selected_use_item_neighbors) ;
69+ use_item. try_merge_from( & mut selected_nodes. filter_map( ast:: Use :: cast) , & ctx. config. insert_use)
5870 } ,
5971 ast:: UseTree ( use_tree) => {
60- use_tree. try_merge_from( & mut selected_nodes. filter_map( ast:: UseTree :: cast) )
72+ cov_mark:: hit!( merge_with_selected_use_tree_neighbors) ;
73+ use_tree. try_merge_from( & mut selected_nodes. filter_map( ast:: UseTree :: cast) , & ctx. config. insert_use)
6174 } ,
6275 _ => return None ,
6376 }
@@ -89,11 +102,15 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
89102}
90103
91104trait Merge : AstNode + Clone {
92- fn try_merge_from ( self , items : & mut dyn Iterator < Item = Self > ) -> Option < Vec < Edit > > {
105+ fn try_merge_from (
106+ self ,
107+ items : & mut dyn Iterator < Item = Self > ,
108+ cfg : & InsertUseConfig ,
109+ ) -> Option < Vec < Edit > > {
93110 let mut edits = Vec :: new ( ) ;
94111 let mut merged = self . clone ( ) ;
95112 for item in items {
96- merged = merged. try_merge ( & item) ?;
113+ merged = merged. try_merge ( & item, cfg ) ?;
97114 edits. push ( Edit :: Remove ( item. into_either ( ) ) ) ;
98115 }
99116 if !edits. is_empty ( ) {
@@ -103,21 +120,25 @@ trait Merge: AstNode + Clone {
103120 None
104121 }
105122 }
106- fn try_merge ( & self , other : & Self ) -> Option < Self > ;
123+ fn try_merge ( & self , other : & Self , cfg : & InsertUseConfig ) -> Option < Self > ;
107124 fn into_either ( self ) -> Either < ast:: Use , ast:: UseTree > ;
108125}
109126
110127impl Merge for ast:: Use {
111- fn try_merge ( & self , other : & Self ) -> Option < Self > {
112- try_merge_imports ( self , other, MergeBehavior :: Crate )
128+ fn try_merge ( & self , other : & Self , cfg : & InsertUseConfig ) -> Option < Self > {
129+ let mb = match cfg. granularity {
130+ ImportGranularity :: One => MergeBehavior :: One ,
131+ _ => MergeBehavior :: Crate ,
132+ } ;
133+ try_merge_imports ( self , other, mb)
113134 }
114135 fn into_either ( self ) -> Either < ast:: Use , ast:: UseTree > {
115136 Either :: Left ( self )
116137 }
117138}
118139
119140impl Merge for ast:: UseTree {
120- fn try_merge ( & self , other : & Self ) -> Option < Self > {
141+ fn try_merge ( & self , other : & Self , _ : & InsertUseConfig ) -> Option < Self > {
121142 try_merge_trees ( self , other, MergeBehavior :: Crate )
122143 }
123144 fn into_either ( self ) -> Either < ast:: Use , ast:: UseTree > {
@@ -138,12 +159,41 @@ impl Edit {
138159
139160#[ cfg( test) ]
140161mod tests {
141- use crate :: tests:: { check_assist, check_assist_not_applicable} ;
162+ use crate :: tests:: {
163+ check_assist, check_assist_import_one, check_assist_not_applicable,
164+ check_assist_not_applicable_for_import_one,
165+ } ;
142166
143167 use super :: * ;
144168
169+ macro_rules! check_assist_import_one_variations {
170+ ( $first: literal, $second: literal, $expected: literal) => {
171+ check_assist_import_one(
172+ merge_imports,
173+ concat!( concat!( "use " , $first, ";" ) , concat!( "use " , $second, ";" ) ) ,
174+ $expected,
175+ ) ;
176+ check_assist_import_one(
177+ merge_imports,
178+ concat!( concat!( "use {" , $first, "};" ) , concat!( "use " , $second, ";" ) ) ,
179+ $expected,
180+ ) ;
181+ check_assist_import_one(
182+ merge_imports,
183+ concat!( concat!( "use " , $first, ";" ) , concat!( "use {" , $second, "};" ) ) ,
184+ $expected,
185+ ) ;
186+ check_assist_import_one(
187+ merge_imports,
188+ concat!( concat!( "use {" , $first, "};" ) , concat!( "use {" , $second, "};" ) ) ,
189+ $expected,
190+ ) ;
191+ } ;
192+ }
193+
145194 #[ test]
146195 fn test_merge_equal ( ) {
196+ cov_mark:: check!( merge_with_use_item_neighbors) ;
147197 check_assist (
148198 merge_imports,
149199 r"
@@ -153,7 +203,19 @@ use std::fmt::{Display, Debug};
153203 r"
154204use std::fmt::{Display, Debug};
155205" ,
156- )
206+ ) ;
207+
208+ // 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 ) ;
213+ cov_mark:: check_count!( merge_with_use_item_neighbors, 4 ) ;
214+ check_assist_import_one_variations ! (
215+ "std::fmt$0::{Display, Debug}" ,
216+ "std::fmt::{Display, Debug}" ,
217+ "use {std::fmt::{Display, Debug}};"
218+ ) ;
157219 }
158220
159221 #[ test]
@@ -167,7 +229,12 @@ use std::fmt::Display;
167229 r"
168230use std::fmt::{Debug, Display};
169231" ,
170- )
232+ ) ;
233+ check_assist_import_one_variations ! (
234+ "std::fmt$0::Debug" ,
235+ "std::fmt::Display" ,
236+ "use {std::fmt::{Debug, Display}};"
237+ ) ;
171238 }
172239
173240 #[ test]
@@ -182,6 +249,11 @@ use std::fmt$0::Display;
182249use std::fmt::{Debug, Display};
183250" ,
184251 ) ;
252+ check_assist_import_one_variations ! (
253+ "std::fmt::Debug" ,
254+ "std::fmt$0::Display" ,
255+ "use {std::fmt::{Debug, Display}};"
256+ ) ;
185257 }
186258
187259 #[ test]
@@ -196,6 +268,11 @@ use std::fmt::Display;
196268use std::fmt::{self, Display};
197269" ,
198270 ) ;
271+ check_assist_import_one_variations ! (
272+ "std::fmt$0" ,
273+ "std::fmt::Display" ,
274+ "use {std::fmt::{self, Display}};"
275+ ) ;
199276 }
200277
201278 #[ test]
@@ -211,6 +288,15 @@ use std::{fmt::{self, Display}};
211288 ) ;
212289 }
213290
291+ #[ test]
292+ fn not_applicable_to_single_one_style_import ( ) {
293+ cov_mark:: check!( resolve_top_use_tree_for_import_one) ;
294+ check_assist_not_applicable_for_import_one (
295+ merge_imports,
296+ "use {std::{fmt, $0fmt::Display}};" ,
297+ ) ;
298+ }
299+
214300 #[ test]
215301 fn skip_pub1 ( ) {
216302 check_assist_not_applicable (
@@ -299,6 +385,7 @@ pub(in this::path) use std::fmt::{Debug, Display};
299385
300386 #[ test]
301387 fn test_merge_nested ( ) {
388+ cov_mark:: check!( merge_with_use_tree_neighbors) ;
302389 check_assist (
303390 merge_imports,
304391 r"
@@ -335,6 +422,11 @@ use std::{fmt::{self, Debug}};
335422use std::{fmt::{self, Debug, Display, Write}};
336423" ,
337424 ) ;
425+ check_assist_import_one_variations ! (
426+ "std$0::{fmt::{Write, Display}}" ,
427+ "std::{fmt::{self, Debug}}" ,
428+ "use {std::{fmt::{self, Debug, Display, Write}}};"
429+ ) ;
338430 }
339431
340432 #[ test]
@@ -349,6 +441,11 @@ use std::{fmt::{Write, Display}};
349441use std::{fmt::{self, Debug, Display, Write}};
350442" ,
351443 ) ;
444+ check_assist_import_one_variations ! (
445+ "std$0::{fmt::{self, Debug}}" ,
446+ "std::{fmt::{Write, Display}}" ,
447+ "use {std::{fmt::{self, Debug, Display, Write}}};"
448+ ) ;
352449 }
353450
354451 #[ test]
@@ -375,7 +472,12 @@ use foo::{bar};
375472 r"
376473use foo::{bar::{self}};
377474" ,
378- )
475+ ) ;
476+ check_assist_import_one_variations ! (
477+ "foo::$0{bar::{self}}" ,
478+ "foo::{bar}" ,
479+ "use {foo::{bar::{self}}};"
480+ ) ;
379481 }
380482
381483 #[ test]
@@ -389,7 +491,12 @@ use foo::{bar::{self}};
389491 r"
390492use foo::{bar::{self}};
391493" ,
392- )
494+ ) ;
495+ check_assist_import_one_variations ! (
496+ "foo::$0{bar}" ,
497+ "foo::{bar::{self}}" ,
498+ "use {foo::{bar::{self}}};"
499+ ) ;
393500 }
394501
395502 #[ test]
@@ -403,7 +510,12 @@ use std::{fmt::{self, Display}};
403510 r"
404511use std::{fmt::{self, Display, *}};
405512" ,
406- )
513+ ) ;
514+ check_assist_import_one_variations ! (
515+ "std$0::{fmt::*}" ,
516+ "std::{fmt::{self, Display}}" ,
517+ "use {std::{fmt::{self, Display, *}}};"
518+ ) ;
407519 }
408520
409521 #[ test]
@@ -417,7 +529,12 @@ use std::str;
417529 r"
418530use std::{cell::*, str};
419531" ,
420- )
532+ ) ;
533+ check_assist_import_one_variations ! (
534+ "std$0::cell::*" ,
535+ "std::str" ,
536+ "use {std::{cell::*, str}};"
537+ ) ;
421538 }
422539
423540 #[ test]
@@ -431,7 +548,12 @@ use std::str::*;
431548 r"
432549use std::{cell::*, str::*};
433550" ,
434- )
551+ ) ;
552+ check_assist_import_one_variations ! (
553+ "std$0::cell::*" ,
554+ "std::str::*" ,
555+ "use {std::{cell::*, str::*}};"
556+ ) ;
435557 }
436558
437559 #[ test]
@@ -524,10 +646,16 @@ use foo::bar::Baz;
524646use foo::{bar::Baz, *};
525647" ,
526648 ) ;
649+ check_assist_import_one_variations ! (
650+ "foo::$0*" ,
651+ "foo::bar::Baz" ,
652+ "use {foo::{bar::Baz, *}};"
653+ ) ;
527654 }
528655
529656 #[ test]
530657 fn merge_selection_uses ( ) {
658+ cov_mark:: check!( merge_with_selected_use_item_neighbors) ;
531659 check_assist (
532660 merge_imports,
533661 r"
@@ -541,12 +669,30 @@ $0use std::fmt::Result;
541669use std::fmt::Error;
542670use std::fmt::{Debug, Display, Write};
543671use std::fmt::Result;
672+ " ,
673+ ) ;
674+
675+ cov_mark:: check!( merge_with_selected_use_item_neighbors) ;
676+ check_assist_import_one (
677+ merge_imports,
678+ r"
679+ use std::fmt::Error;
680+ $0use std::fmt::Display;
681+ use std::fmt::Debug;
682+ use std::fmt::Write;
683+ $0use std::fmt::Result;
684+ " ,
685+ r"
686+ use std::fmt::Error;
687+ use {std::fmt::{Debug, Display, Write}};
688+ use std::fmt::Result;
544689" ,
545690 ) ;
546691 }
547692
548693 #[ test]
549694 fn merge_selection_use_trees ( ) {
695+ cov_mark:: check!( merge_with_selected_use_tree_neighbors) ;
550696 check_assist (
551697 merge_imports,
552698 r"
@@ -564,7 +710,9 @@ use std::{
564710 fmt::Result,
565711};" ,
566712 ) ;
713+
567714 // FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
715+ cov_mark:: check!( merge_with_selected_use_tree_neighbors) ;
568716 check_assist (
569717 merge_imports,
570718 r"use std::$0{fmt::Display, fmt::Debug}$0;" ,
0 commit comments