Skip to content

Commit cd6cd91

Browse files
committed
Tidy up recursive_merge implementation
1 parent a898752 commit cd6cd91

File tree

1 file changed

+60
-60
lines changed

1 file changed

+60
-60
lines changed

crates/assists/src/utils/insert_use.rs

Lines changed: 60 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ pub(crate) fn insert_use(
120120
}
121121

122122
if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
123-
// FIXME: this alone doesnt properly re-align all cases
124123
buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into());
125124
}
126125
buf.push(use_item.syntax().clone().into());
@@ -164,44 +163,6 @@ pub(crate) fn try_merge_imports(
164163
Some(old.with_use_tree(merged))
165164
}
166165

167-
/// Orders paths in the following way:
168-
/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
169-
/// FIXME: rustfmt sort lowercase idents before uppercase
170-
fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering {
171-
let a = segment_iter(a);
172-
let b = segment_iter(b);
173-
let mut a_clone = a.clone();
174-
let mut b_clone = b.clone();
175-
match (
176-
a_clone.next().and_then(|ps| ps.self_token()).is_some() && a_clone.next().is_none(),
177-
b_clone.next().and_then(|ps| ps.self_token()).is_some() && b_clone.next().is_none(),
178-
) {
179-
(true, true) => Ordering::Equal,
180-
(true, false) => Ordering::Less,
181-
(false, true) => Ordering::Greater,
182-
(false, false) => {
183-
// cmp_by would be useful for us here but that is currently unstable
184-
// cmp doesnt work due the lifetimes on text's return type
185-
a.zip(b)
186-
.flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
187-
.find_map(|(a, b)| match a.text().cmp(b.text()) {
188-
ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord),
189-
Ordering::Equal => None,
190-
})
191-
.unwrap_or(Ordering::Equal)
192-
}
193-
}
194-
}
195-
196-
fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
197-
match (a, b) {
198-
(None, None) => Ordering::Equal,
199-
(None, Some(_)) => Ordering::Less,
200-
(Some(_), None) => Ordering::Greater,
201-
(Some(a), Some(b)) => path_cmp(&a, &b),
202-
}
203-
}
204-
205166
pub(crate) fn try_merge_trees(
206167
old: &ast::UseTree,
207168
new: &ast::UseTree,
@@ -216,17 +177,18 @@ pub(crate) fn try_merge_trees(
216177
recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged)
217178
}
218179

219-
/// Returns the merged tree and the number of children it has, which is required to check if the tree is allowed to be used for MergeBehaviour::Last
180+
/// Recursively "zips" together lhs and rhs.
220181
fn recursive_merge(
221182
lhs: &ast::UseTree,
222183
rhs: &ast::UseTree,
223184
merge: MergeBehaviour,
224-
) -> Option<(ast::UseTree, usize)> {
185+
) -> Option<(ast::UseTree, bool)> {
225186
let mut use_trees = lhs
226187
.use_tree_list()
227188
.into_iter()
228189
.flat_map(|list| list.use_trees())
229-
// check if any of the use trees are nested, if they are and the behaviour is last only we are not allowed to merge this
190+
// check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this
191+
// so early exit the iterator by using Option's Intoiterator impl
230192
.map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() {
231193
true => None,
232194
false => Some(tree),
@@ -237,15 +199,17 @@ fn recursive_merge(
237199
let rhs_path = rhs_t.path();
238200
match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) {
239201
Ok(idx) => {
240-
let lhs_path = use_trees[idx].path()?;
202+
let lhs_t = &mut use_trees[idx];
203+
let lhs_path = lhs_t.path()?;
241204
let rhs_path = rhs_path?;
242205
let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
243206
if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
244-
let tree_is_self =
245-
|tree: ast::UseTree| tree.path().map(path_is_self).unwrap_or(false);
207+
let tree_is_self = |tree: ast::UseTree| {
208+
tree.path().as_ref().map(path_is_self).unwrap_or(false)
209+
};
246210
// check if only one of the two trees has a tree list, and whether that then contains `self` or not.
247211
// 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`
248-
if use_trees[idx]
212+
if lhs_t
249213
.use_tree_list()
250214
.xor(rhs_t.use_tree_list())
251215
.map(|tree_list| tree_list.use_trees().any(tree_is_self))
@@ -257,8 +221,8 @@ fn recursive_merge(
257221
// glob imports arent part of the use-tree lists so we need to special handle them here as well
258222
// this special handling is only required for when we merge a module import into a glob import of said module
259223
// see the `merge_self_glob` or `merge_mod_into_glob` tests
260-
if use_trees[idx].star_token().is_some() || rhs_t.star_token().is_some() {
261-
use_trees[idx] = make::use_tree(
224+
if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
225+
*lhs_t = make::use_tree(
262226
make::path_unqualified(make::path_segment_self()),
263227
None,
264228
None,
@@ -276,11 +240,14 @@ fn recursive_merge(
276240
continue;
277241
}
278242
}
279-
let lhs = use_trees[idx].split_prefix(&lhs_prefix);
243+
let lhs = lhs_t.split_prefix(&lhs_prefix);
280244
let rhs = rhs_t.split_prefix(&rhs_prefix);
245+
let this_has_children = use_trees.len() > 0;
281246
match recursive_merge(&lhs, &rhs, merge) {
282-
Some((_, count))
283-
if merge == MergeBehaviour::Last && use_trees.len() > 1 && count > 1 =>
247+
Some((_, has_multiple_children))
248+
if merge == MergeBehaviour::Last
249+
&& this_has_children
250+
&& has_multiple_children =>
284251
{
285252
return None
286253
}
@@ -293,9 +260,8 @@ fn recursive_merge(
293260
}
294261
}
295262
}
296-
let count = use_trees.len();
297-
let tl = make::use_tree_list(use_trees);
298-
Some((lhs.with_use_tree_list(tl), count))
263+
let has_multiple_children = use_trees.len() > 1;
264+
Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children))
299265
}
300266

301267
/// Traverses both paths until they differ, returning the common prefix of both.
@@ -306,7 +272,7 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa
306272
loop {
307273
match (lhs_curr.segment(), rhs_curr.segment()) {
308274
(Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
309-
_ => break,
275+
_ => break res,
310276
}
311277
res = Some((lhs_curr.clone(), rhs_curr.clone()));
312278

@@ -315,17 +281,16 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa
315281
lhs_curr = lhs;
316282
rhs_curr = rhs;
317283
}
318-
_ => break,
284+
_ => break res,
319285
}
320286
}
321-
322-
res
323287
}
324288

325-
fn path_is_self(path: ast::Path) -> bool {
289+
fn path_is_self(path: &ast::Path) -> bool {
326290
path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none()
327291
}
328292

293+
#[inline]
329294
fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
330295
first_path(path).segment()
331296
}
@@ -339,6 +304,41 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl
339304
successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment()))
340305
}
341306

307+
/// Orders paths in the following way:
308+
/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
309+
// FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
310+
// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports.
311+
// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}}
312+
fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering {
313+
match (path_is_self(a), path_is_self(b)) {
314+
(true, true) => Ordering::Equal,
315+
(true, false) => Ordering::Less,
316+
(false, true) => Ordering::Greater,
317+
(false, false) => {
318+
let a = segment_iter(a);
319+
let b = segment_iter(b);
320+
// cmp_by would be useful for us here but that is currently unstable
321+
// cmp doesnt work due the lifetimes on text's return type
322+
a.zip(b)
323+
.flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
324+
.find_map(|(a, b)| match a.text().cmp(b.text()) {
325+
ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord),
326+
Ordering::Equal => None,
327+
})
328+
.unwrap_or(Ordering::Equal)
329+
}
330+
}
331+
}
332+
333+
fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
334+
match (a, b) {
335+
(None, None) => Ordering::Equal,
336+
(None, Some(_)) => Ordering::Less,
337+
(Some(_), None) => Ordering::Greater,
338+
(Some(a), Some(b)) => path_cmp(&a, &b),
339+
}
340+
}
341+
342342
/// What type of merges are allowed.
343343
#[derive(Copy, Clone, PartialEq, Eq)]
344344
pub enum MergeBehaviour {
@@ -924,6 +924,6 @@ use foo::bar::baz::Qux;",
924924
.unwrap();
925925

926926
let result = try_merge_imports(&use0, &use1, mb);
927-
assert_eq!(result, None);
927+
assert_eq!(result.map(|u| u.to_string()), None);
928928
}
929929
}

0 commit comments

Comments
 (0)