1
1
//! Handle syntactic aspects of inserting a new `use`.
2
- use std:: { cmp:: Ordering , iter :: successors } ;
2
+ use std:: cmp:: Ordering ;
3
3
4
4
use hir:: Semantics ;
5
- use itertools:: { EitherOrBoth , Itertools } ;
6
5
use syntax:: {
7
6
algo,
8
- ast:: { self , edit :: AstNodeEdit , make, AstNode , AttrsOwner , PathSegmentKind , VisibilityOwner } ,
7
+ ast:: { self , make, AstNode , PathSegmentKind } ,
9
8
ted, AstToken , Direction , NodeOrToken , SyntaxNode , SyntaxToken ,
10
9
} ;
11
10
12
- use crate :: RootDatabase ;
11
+ use crate :: {
12
+ helpers:: merge_imports:: { try_merge_imports, use_tree_path_cmp, MergeBehavior } ,
13
+ RootDatabase ,
14
+ } ;
13
15
14
16
pub use hir:: PrefixKind ;
15
17
@@ -85,318 +87,6 @@ pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig
85
87
insert_use_ ( scope, path, cfg. group , use_item) ;
86
88
}
87
89
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
-
400
90
#[ derive( Eq , PartialEq , PartialOrd , Ord ) ]
401
91
enum ImportGroup {
402
92
// the order here defines the order of new group inserts
@@ -411,7 +101,7 @@ impl ImportGroup {
411
101
fn new ( path : & ast:: Path ) -> ImportGroup {
412
102
let default = ImportGroup :: ExternCrate ;
413
103
414
- let first_segment = match first_segment ( path ) {
104
+ let first_segment = match path . first_segment ( ) {
415
105
Some ( it) => it,
416
106
None => return default,
417
107
} ;
0 commit comments