11//! Handle syntactic aspects of inserting a new `use`.
2- use std:: { cmp:: Ordering , iter :: successors } ;
2+ use std:: cmp:: Ordering ;
33
44use hir:: Semantics ;
5- use itertools:: { EitherOrBoth , Itertools } ;
65use syntax:: {
76 algo,
8- ast:: { self , edit :: AstNodeEdit , make, AstNode , AttrsOwner , PathSegmentKind , VisibilityOwner } ,
7+ ast:: { self , make, AstNode , PathSegmentKind } ,
98 ted, AstToken , Direction , NodeOrToken , SyntaxNode , SyntaxToken ,
109} ;
1110
12- use crate :: RootDatabase ;
11+ use crate :: {
12+ helpers:: merge_imports:: { try_merge_imports, use_tree_path_cmp, MergeBehavior } ,
13+ RootDatabase ,
14+ } ;
1315
1416pub use hir:: PrefixKind ;
1517
@@ -85,318 +87,6 @@ pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig
8587 insert_use_ ( scope, path, cfg. group , use_item) ;
8688}
8789
88- fn eq_visibility ( vis0 : Option < ast:: Visibility > , vis1 : Option < ast:: Visibility > ) -> bool {
89- match ( vis0, vis1) {
90- ( None , None ) => true ,
91- // FIXME: Don't use the string representation to check for equality
92- // spaces inside of the node would break this comparison
93- ( Some ( vis0) , Some ( vis1) ) => vis0. to_string ( ) == vis1. to_string ( ) ,
94- _ => false ,
95- }
96- }
97-
98- fn eq_attrs (
99- attrs0 : impl Iterator < Item = ast:: Attr > ,
100- attrs1 : impl Iterator < Item = ast:: Attr > ,
101- ) -> bool {
102- let attrs0 = attrs0. map ( |attr| attr. to_string ( ) ) ;
103- let attrs1 = attrs1. map ( |attr| attr. to_string ( ) ) ;
104- attrs0. eq ( attrs1)
105- }
106-
107- pub fn try_merge_imports (
108- lhs : & ast:: Use ,
109- rhs : & ast:: Use ,
110- merge_behavior : MergeBehavior ,
111- ) -> Option < ast:: Use > {
112- // don't merge imports with different visibilities
113- if !eq_visibility ( lhs. visibility ( ) , rhs. visibility ( ) ) {
114- return None ;
115- }
116- if !eq_attrs ( lhs. attrs ( ) , rhs. attrs ( ) ) {
117- return None ;
118- }
119-
120- let lhs_tree = lhs. use_tree ( ) ?;
121- let rhs_tree = rhs. use_tree ( ) ?;
122- let merged = try_merge_trees ( & lhs_tree, & rhs_tree, merge_behavior) ?;
123- Some ( lhs. with_use_tree ( merged) . clone_for_update ( ) )
124- }
125-
126- pub fn try_merge_trees (
127- lhs : & ast:: UseTree ,
128- rhs : & ast:: UseTree ,
129- merge : MergeBehavior ,
130- ) -> Option < ast:: UseTree > {
131- let lhs_path = lhs. path ( ) ?;
132- let rhs_path = rhs. path ( ) ?;
133-
134- let ( lhs_prefix, rhs_prefix) = common_prefix ( & lhs_path, & rhs_path) ?;
135- let ( lhs, rhs) = if is_simple_path ( lhs)
136- && is_simple_path ( rhs)
137- && lhs_path == lhs_prefix
138- && rhs_path == rhs_prefix
139- {
140- ( lhs. clone ( ) , rhs. clone ( ) )
141- } else {
142- ( lhs. split_prefix ( & lhs_prefix) , rhs. split_prefix ( & rhs_prefix) )
143- } ;
144- recursive_merge ( & lhs, & rhs, merge)
145- }
146-
147- /// Recursively "zips" together lhs and rhs.
148- fn recursive_merge (
149- lhs : & ast:: UseTree ,
150- rhs : & ast:: UseTree ,
151- merge : MergeBehavior ,
152- ) -> Option < ast:: UseTree > {
153- let mut use_trees = lhs
154- . use_tree_list ( )
155- . into_iter ( )
156- . flat_map ( |list| list. use_trees ( ) )
157- // we use Option here to early return from this function(this is not the same as a `filter` op)
158- . map ( |tree| match merge. is_tree_allowed ( & tree) {
159- true => Some ( tree) ,
160- false => None ,
161- } )
162- . collect :: < Option < Vec < _ > > > ( ) ?;
163- use_trees. sort_unstable_by ( |a, b| path_cmp_for_sort ( a. path ( ) , b. path ( ) ) ) ;
164- for rhs_t in rhs. use_tree_list ( ) . into_iter ( ) . flat_map ( |list| list. use_trees ( ) ) {
165- if !merge. is_tree_allowed ( & rhs_t) {
166- return None ;
167- }
168- let rhs_path = rhs_t. path ( ) ;
169- match use_trees. binary_search_by ( |lhs_t| {
170- let ( lhs_t, rhs_t) = match lhs_t
171- . path ( )
172- . zip ( rhs_path. clone ( ) )
173- . and_then ( |( lhs, rhs) | common_prefix ( & lhs, & rhs) )
174- {
175- Some ( ( lhs_p, rhs_p) ) => ( lhs_t. split_prefix ( & lhs_p) , rhs_t. split_prefix ( & rhs_p) ) ,
176- None => ( lhs_t. clone ( ) , rhs_t. clone ( ) ) ,
177- } ;
178-
179- path_cmp_bin_search ( lhs_t. path ( ) , rhs_t. path ( ) )
180- } ) {
181- Ok ( idx) => {
182- let lhs_t = & mut use_trees[ idx] ;
183- let lhs_path = lhs_t. path ( ) ?;
184- let rhs_path = rhs_path?;
185- let ( lhs_prefix, rhs_prefix) = common_prefix ( & lhs_path, & rhs_path) ?;
186- if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
187- let tree_is_self = |tree : ast:: UseTree | {
188- tree. path ( ) . as_ref ( ) . map ( path_is_self) . unwrap_or ( false )
189- } ;
190- // check if only one of the two trees has a tree list, and whether that then contains `self` or not.
191- // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self`
192- let tree_contains_self = |tree : & ast:: UseTree | {
193- tree. use_tree_list ( )
194- . map ( |tree_list| tree_list. use_trees ( ) . any ( tree_is_self) )
195- . unwrap_or ( false )
196- } ;
197- match ( tree_contains_self ( & lhs_t) , tree_contains_self ( & rhs_t) ) {
198- ( true , false ) => continue ,
199- ( false , true ) => {
200- * lhs_t = rhs_t;
201- continue ;
202- }
203- _ => ( ) ,
204- }
205-
206- // glob imports arent part of the use-tree lists so we need to special handle them here as well
207- // this special handling is only required for when we merge a module import into a glob import of said module
208- // see the `merge_self_glob` or `merge_mod_into_glob` tests
209- if lhs_t. star_token ( ) . is_some ( ) || rhs_t. star_token ( ) . is_some ( ) {
210- * lhs_t = make:: use_tree (
211- make:: path_unqualified ( make:: path_segment_self ( ) ) ,
212- None ,
213- None ,
214- false ,
215- ) ;
216- use_trees. insert ( idx, make:: glob_use_tree ( ) ) ;
217- continue ;
218- }
219-
220- if lhs_t. use_tree_list ( ) . is_none ( ) && rhs_t. use_tree_list ( ) . is_none ( ) {
221- continue ;
222- }
223- }
224- let lhs = lhs_t. split_prefix ( & lhs_prefix) ;
225- let rhs = rhs_t. split_prefix ( & rhs_prefix) ;
226- match recursive_merge ( & lhs, & rhs, merge) {
227- Some ( use_tree) => use_trees[ idx] = use_tree,
228- None => return None ,
229- }
230- }
231- Err ( _)
232- if merge == MergeBehavior :: Last
233- && use_trees. len ( ) > 0
234- && rhs_t. use_tree_list ( ) . is_some ( ) =>
235- {
236- return None
237- }
238- Err ( idx) => {
239- use_trees. insert ( idx, rhs_t) ;
240- }
241- }
242- }
243-
244- Some ( if let Some ( old) = lhs. use_tree_list ( ) {
245- lhs. replace_descendant ( old, make:: use_tree_list ( use_trees) ) . clone_for_update ( )
246- } else {
247- lhs. clone ( )
248- } )
249- }
250-
251- /// Traverses both paths until they differ, returning the common prefix of both.
252- fn common_prefix ( lhs : & ast:: Path , rhs : & ast:: Path ) -> Option < ( ast:: Path , ast:: Path ) > {
253- let mut res = None ;
254- let mut lhs_curr = first_path ( & lhs) ;
255- let mut rhs_curr = first_path ( & rhs) ;
256- loop {
257- match ( lhs_curr. segment ( ) , rhs_curr. segment ( ) ) {
258- ( Some ( lhs) , Some ( rhs) ) if lhs. syntax ( ) . text ( ) == rhs. syntax ( ) . text ( ) => ( ) ,
259- _ => break res,
260- }
261- res = Some ( ( lhs_curr. clone ( ) , rhs_curr. clone ( ) ) ) ;
262-
263- match lhs_curr. parent_path ( ) . zip ( rhs_curr. parent_path ( ) ) {
264- Some ( ( lhs, rhs) ) => {
265- lhs_curr = lhs;
266- rhs_curr = rhs;
267- }
268- _ => break res,
269- }
270- }
271- }
272-
273- fn is_simple_path ( use_tree : & ast:: UseTree ) -> bool {
274- use_tree. use_tree_list ( ) . is_none ( ) && use_tree. star_token ( ) . is_none ( )
275- }
276-
277- fn path_is_self ( path : & ast:: Path ) -> bool {
278- path. segment ( ) . and_then ( |seg| seg. self_token ( ) ) . is_some ( ) && path. qualifier ( ) . is_none ( )
279- }
280-
281- #[ inline]
282- fn first_segment ( path : & ast:: Path ) -> Option < ast:: PathSegment > {
283- first_path ( path) . segment ( )
284- }
285-
286- fn first_path ( path : & ast:: Path ) -> ast:: Path {
287- successors ( Some ( path. clone ( ) ) , ast:: Path :: qualifier) . last ( ) . unwrap ( )
288- }
289-
290- fn segment_iter ( path : & ast:: Path ) -> impl Iterator < Item = ast:: PathSegment > + Clone {
291- // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone
292- successors ( first_segment ( path) , |p| p. parent_path ( ) . parent_path ( ) . and_then ( |p| p. segment ( ) ) )
293- }
294-
295- fn path_len ( path : ast:: Path ) -> usize {
296- segment_iter ( & path) . count ( )
297- }
298-
299- /// Orders paths in the following way:
300- /// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
301- // FIXME: rustfmt sorts lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
302- // which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports.
303- // Example foo::{self, foo, baz, Baz, Qux, *, {Bar}}
304- fn path_cmp_for_sort ( a : Option < ast:: Path > , b : Option < ast:: Path > ) -> Ordering {
305- match ( a, b) {
306- ( None , None ) => Ordering :: Equal ,
307- ( None , Some ( _) ) => Ordering :: Less ,
308- ( Some ( _) , None ) => Ordering :: Greater ,
309- ( Some ( ref a) , Some ( ref b) ) => match ( path_is_self ( a) , path_is_self ( b) ) {
310- ( true , true ) => Ordering :: Equal ,
311- ( true , false ) => Ordering :: Less ,
312- ( false , true ) => Ordering :: Greater ,
313- ( false , false ) => path_cmp_short ( a, b) ,
314- } ,
315- }
316- }
317-
318- /// Path comparison func for binary searching for merging.
319- fn path_cmp_bin_search ( lhs : Option < ast:: Path > , rhs : Option < ast:: Path > ) -> Ordering {
320- match ( lhs. as_ref ( ) . and_then ( first_segment) , rhs. as_ref ( ) . and_then ( first_segment) ) {
321- ( None , None ) => Ordering :: Equal ,
322- ( None , Some ( _) ) => Ordering :: Less ,
323- ( Some ( _) , None ) => Ordering :: Greater ,
324- ( Some ( ref a) , Some ( ref b) ) => path_segment_cmp ( a, b) ,
325- }
326- }
327-
328- /// Short circuiting comparison, if both paths are equal until one of them ends they are considered
329- /// equal
330- fn path_cmp_short ( a : & ast:: Path , b : & ast:: Path ) -> Ordering {
331- let a = segment_iter ( a) ;
332- let b = segment_iter ( b) ;
333- // cmp_by would be useful for us here but that is currently unstable
334- // cmp doesnt work due the lifetimes on text's return type
335- a. zip ( b)
336- . find_map ( |( a, b) | match path_segment_cmp ( & a, & b) {
337- Ordering :: Equal => None ,
338- ord => Some ( ord) ,
339- } )
340- . unwrap_or ( Ordering :: Equal )
341- }
342-
343- /// Compares to paths, if one ends earlier than the other the has_tl parameters decide which is
344- /// greater as a a path that has a tree list should be greater, while one that just ends without
345- /// a tree list should be considered less.
346- fn use_tree_path_cmp ( a : & ast:: Path , a_has_tl : bool , b : & ast:: Path , b_has_tl : bool ) -> Ordering {
347- let a_segments = segment_iter ( a) ;
348- let b_segments = segment_iter ( b) ;
349- // cmp_by would be useful for us here but that is currently unstable
350- // cmp doesnt work due the lifetimes on text's return type
351- a_segments
352- . zip_longest ( b_segments)
353- . find_map ( |zipped| match zipped {
354- EitherOrBoth :: Both ( ref a, ref b) => match path_segment_cmp ( a, b) {
355- Ordering :: Equal => None ,
356- ord => Some ( ord) ,
357- } ,
358- EitherOrBoth :: Left ( _) if !b_has_tl => Some ( Ordering :: Greater ) ,
359- EitherOrBoth :: Left ( _) => Some ( Ordering :: Less ) ,
360- EitherOrBoth :: Right ( _) if !a_has_tl => Some ( Ordering :: Less ) ,
361- EitherOrBoth :: Right ( _) => Some ( Ordering :: Greater ) ,
362- } )
363- . unwrap_or ( Ordering :: Equal )
364- }
365-
366- fn path_segment_cmp ( a : & ast:: PathSegment , b : & ast:: PathSegment ) -> Ordering {
367- let a = a. kind ( ) . and_then ( |kind| match kind {
368- PathSegmentKind :: Name ( name_ref) => Some ( name_ref) ,
369- _ => None ,
370- } ) ;
371- let b = b. kind ( ) . and_then ( |kind| match kind {
372- PathSegmentKind :: Name ( name_ref) => Some ( name_ref) ,
373- _ => None ,
374- } ) ;
375- a. as_ref ( ) . map ( ast:: NameRef :: text) . cmp ( & b. as_ref ( ) . map ( ast:: NameRef :: text) )
376- }
377-
378- /// What type of merges are allowed.
379- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
380- pub enum MergeBehavior {
381- /// Merge everything together creating deeply nested imports.
382- Full ,
383- /// Only merge the last import level, doesn't allow import nesting.
384- Last ,
385- }
386-
387- impl MergeBehavior {
388- #[ inline]
389- fn is_tree_allowed ( & self , tree : & ast:: UseTree ) -> bool {
390- match self {
391- MergeBehavior :: Full => true ,
392- // only simple single segment paths are allowed
393- MergeBehavior :: Last => {
394- tree. use_tree_list ( ) . is_none ( ) && tree. path ( ) . map ( path_len) <= Some ( 1 )
395- }
396- }
397- }
398- }
399-
40090#[ derive( Eq , PartialEq , PartialOrd , Ord ) ]
40191enum ImportGroup {
40292 // the order here defines the order of new group inserts
@@ -411,7 +101,7 @@ impl ImportGroup {
411101 fn new ( path : & ast:: Path ) -> ImportGroup {
412102 let default = ImportGroup :: ExternCrate ;
413103
414- let first_segment = match first_segment ( path ) {
104+ let first_segment = match path . first_segment ( ) {
415105 Some ( it) => it,
416106 None => return default,
417107 } ;
0 commit comments