Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/imports/merge_imports.rs')
| -rw-r--r-- | crates/ide-db/src/imports/merge_imports.rs | 664 |
1 files changed, 397 insertions, 267 deletions
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index bbd351a4cc..9b68c27ae3 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -4,12 +4,12 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use parser::T; use syntax::{ - Direction, SyntaxElement, ToSmolStr, algo, + ToSmolStr, ast::{ - self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, edit_in_place::Removable, - make, + self, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind, + syntax_factory::SyntaxFactory, }, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::syntax_helpers::node_ext::vis_eq; @@ -39,8 +39,8 @@ impl MergeBehavior { } /// Merge `rhs` into `lhs` keeping both intact. -/// Returned AST is mutable. pub fn try_merge_imports( + make: &SyntaxFactory, lhs: &ast::Use, rhs: &ast::Use, merge_behavior: MergeBehavior, @@ -53,39 +53,39 @@ pub fn try_merge_imports( return None; } - let lhs = lhs.clone_subtree().clone_for_update(); - let rhs = rhs.clone_subtree().clone_for_update(); let lhs_tree = lhs.use_tree()?; let rhs_tree = rhs.use_tree()?; - try_merge_trees_mut(&lhs_tree, &rhs_tree, merge_behavior)?; + let merged_tree = try_merge_trees_with_factory(lhs_tree, rhs_tree, merge_behavior, make)?; // Ignore `None` result because normalization should not affect the merge result. - try_normalize_use_tree_mut(&lhs_tree, merge_behavior.into()); + let use_tree = try_normalize_use_tree(merged_tree.clone(), merge_behavior.into(), make) + .unwrap_or(merged_tree); - Some(lhs) + make_use_with_tree(lhs, use_tree) } /// Merge `rhs` into `lhs` keeping both intact. -/// Returned AST is mutable. pub fn try_merge_trees( + make: &SyntaxFactory, lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior, ) -> Option<ast::UseTree> { - let lhs = lhs.clone_subtree().clone_for_update(); - let rhs = rhs.clone_subtree().clone_for_update(); - try_merge_trees_mut(&lhs, &rhs, merge)?; + let merged = try_merge_trees_with_factory(lhs.clone(), rhs.clone(), merge, make)?; // Ignore `None` result because normalization should not affect the merge result. - try_normalize_use_tree_mut(&lhs, merge.into()); - - Some(lhs) + Some(try_normalize_use_tree(merged.clone(), merge.into(), make).unwrap_or(merged)) } -fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> { +fn try_merge_trees_with_factory( + mut lhs: ast::UseTree, + mut rhs: ast::UseTree, + merge: MergeBehavior, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { if merge == MergeBehavior::One { - lhs.wrap_in_tree_list(); - rhs.wrap_in_tree_list(); + lhs = wrap_in_tree_list(&lhs, make).unwrap_or(lhs); + rhs = wrap_in_tree_list(&rhs, make).unwrap_or(rhs); } else { let lhs_path = lhs.path()?; let rhs_path = rhs.path()?; @@ -100,46 +100,53 @@ fn try_merge_trees_mut(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehav // and we can safely return here let lhs_name = lhs.rename().and_then(|lhs_name| lhs_name.name()); let rhs_name = rhs.rename().and_then(|rhs_name| rhs_name.name()); - if lhs_name != rhs_name { + if lhs_name.as_ref().map(|name| name.text()) + != rhs_name.as_ref().map(|name| name.text()) + { return None; } - ted::replace(lhs.syntax(), rhs.syntax()); - // we can safely return here, in this case `recursive_merge` doesn't do anything - return Some(()); + return Some(rhs); } else { - lhs.split_prefix(&lhs_prefix); - rhs.split_prefix(&rhs_prefix); + lhs = split_prefix(&lhs, &lhs_prefix, make)?; + rhs = split_prefix(&rhs, &rhs_prefix, make)?; } } - recursive_merge(lhs, rhs, merge) + recursive_merge(lhs, rhs, merge, make) } /// Recursively merges rhs to lhs #[must_use] -fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) -> Option<()> { +fn recursive_merge( + lhs: ast::UseTree, + rhs: ast::UseTree, + merge: MergeBehavior, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { let mut use_trees: Vec<ast::UseTree> = lhs - .use_tree_list() - .into_iter() - .flat_map(|list| list.use_trees()) - // We use Option here to early return from this function(this is not the - // same as a `filter` op). + .use_tree_list()? + .use_trees() + // We use Option here to early return from this function. This is not the + // same as a `filter` op. .map(|tree| merge.is_tree_allowed(&tree).then_some(tree)) .collect::<Option<_>>()?; + // Sorts the use trees similar to rustfmt's algorithm for ordering imports // (see `use_tree_cmp` doc). use_trees.sort_unstable_by(use_tree_cmp); - for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { + + for rhs_t in rhs.use_tree_list()?.use_trees() { if !merge.is_tree_allowed(&rhs_t) { return None; } match use_trees.binary_search_by(|lhs_t| use_tree_cmp_bin_search(lhs_t, &rhs_t)) { Ok(idx) => { - let lhs_t = &mut use_trees[idx]; + let mut lhs_t = use_trees[idx].clone(); let lhs_path = lhs_t.path()?; let rhs_path = rhs_t.path()?; let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; + if lhs_prefix == lhs_path && rhs_prefix == rhs_path { let tree_is_self = |tree: &ast::UseTree| { tree.path().as_ref().map(path_is_self).unwrap_or(false) @@ -157,20 +164,20 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) }; if lhs_t.rename().and_then(|x| x.underscore_token()).is_some() { - ted::replace(lhs_t.syntax(), rhs_t.syntax()); - *lhs_t = rhs_t; + use_trees[idx] = rhs_t; continue; } - match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) { + match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { (Some(true), None) => { - remove_subtree_if_only_self(lhs_t); + lhs_t = remove_subtree_if_only_self(lhs_t, make)?; + use_trees[idx] = lhs_t; continue; } (None, Some(true)) => { - ted::replace(lhs_t.syntax(), rhs_t.syntax()); - *lhs_t = rhs_t; - remove_subtree_if_only_self(lhs_t); + lhs_t = rhs_t; + lhs_t = remove_subtree_if_only_self(lhs_t, make)?; + use_trees[idx] = lhs_t; continue; } _ => (), @@ -180,9 +187,11 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) continue; } } - lhs_t.split_prefix(&lhs_prefix); - rhs_t.split_prefix(&rhs_prefix); - recursive_merge(lhs_t, &rhs_t, merge)?; + + lhs_t = split_prefix(&lhs_t, &lhs_prefix, make)?; + let rhs_t = split_prefix(&rhs_t, &rhs_prefix, make)?; + lhs_t = recursive_merge(lhs_t, rhs_t, merge, make)?; + use_trees[idx] = lhs_t; } Err(_) if merge == MergeBehavior::Module @@ -192,15 +201,12 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior) return None; } Err(insert_idx) => { - use_trees.insert(insert_idx, rhs_t.clone()); - // We simply add the use tree to the end of tree list. Ordering of use trees - // and imports is done by the `try_normalize_*` functions. The sorted `use_trees` - // vec is only used for binary search. - lhs.get_or_create_use_tree_list().add_use_tree(rhs_t); + use_trees.insert(insert_idx, rhs_t); } } } - Some(()) + + with_use_tree_list(&lhs, use_trees, make) } /// Style to follow when normalizing a use tree. @@ -250,241 +256,216 @@ impl From<MergeBehavior> for NormalizationStyle { /// - `foo::{bar::Qux, bar::{self}}` -> `{foo::bar::{self, Qux}}` /// - `foo::bar::{self}` -> `{foo::bar}` /// - `foo::bar` -> `{foo::bar}` -pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> Option<ast::Use> { - let use_item = use_item.clone_subtree().clone_for_update(); - try_normalize_use_tree_mut(&use_item.use_tree()?, style)?; - Some(use_item) +pub fn try_normalize_import( + make: &SyntaxFactory, + use_item: &ast::Use, + style: NormalizationStyle, +) -> Option<ast::Use> { + let use_tree = try_normalize_use_tree(use_item.use_tree()?, style, make)?; + + make_use_with_tree(use_item, use_tree) } -fn try_normalize_use_tree_mut(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { +fn try_normalize_use_tree( + use_tree: ast::UseTree, + style: NormalizationStyle, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { if style == NormalizationStyle::One { + let mut use_tree = use_tree; let mut modified = false; - modified |= use_tree.wrap_in_tree_list().is_some(); - modified |= recursive_normalize(use_tree, style).is_some(); - if !modified { - // Either the use tree was already normalized or its semantically empty. - return None; + if let Some(wrapped) = wrap_in_tree_list(&use_tree, make) { + use_tree = wrapped; + modified = true; } - } else { - recursive_normalize(use_tree, NormalizationStyle::Default)?; + if let Some(normalized) = recursive_normalize(use_tree.clone(), style, make) { + use_tree = normalized; + modified = true; + } + return modified.then_some(use_tree); } - Some(()) + + recursive_normalize(use_tree, NormalizationStyle::Default, make) } /// Recursively normalizes a use tree and its subtrees (if any). -fn recursive_normalize(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { +fn recursive_normalize( + use_tree: ast::UseTree, + style: NormalizationStyle, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { let use_tree_list = use_tree.use_tree_list()?; - let merge_subtree_into_parent_tree = |single_subtree: &ast::UseTree| { - let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); - - let merged_path = match (use_tree.path(), single_subtree.path()) { - // If the subtree is `{self}` then we cannot merge: `use - // foo::bar::{self}` is not equivalent to `use foo::bar`. See - // https://github.com/rust-lang/rust-analyzer/pull/17140#issuecomment-2079189725. - _ if subtree_is_only_self => None, - - (None, None) => None, - (Some(outer), None) => Some(outer), - (None, Some(inner)) => Some(inner), - (Some(outer), Some(inner)) => Some(make::path_concat(outer, inner).clone_for_update()), - }; - - if merged_path.is_some() - || single_subtree.use_tree_list().is_some() - || single_subtree.star_token().is_some() + let mut subtrees = use_tree_list.use_trees().collect::<Vec<_>>(); + if subtrees.len() == 1 { + if style == NormalizationStyle::One { + let subtree = subtrees.pop()?; + let normalized = recursive_normalize(subtree, NormalizationStyle::Default, make)?; + return with_use_tree_list(&use_tree, vec![normalized], make); + } + + let merged = merge_single_subtree_into_parent_tree(use_tree, make)?; + return Some(recursive_normalize(merged.clone(), style, make).unwrap_or(merged)); + } + + let mut modified = false; + let mut new_use_tree_list = Vec::new(); + for subtree in subtrees { + if one_style_tree_list(&subtree).is_some() { + let mut elements = Vec::new(); + flatten_one_style_tree(subtree, &mut elements, &mut modified, make); + new_use_tree_list.extend(elements); + modified = true; + } else if let Some(normalized) = + recursive_normalize(subtree.clone(), NormalizationStyle::Default, make) { - ted::remove_all_iter(use_tree.syntax().children_with_tokens()); - if let Some(path) = merged_path { - ted::insert_raw(Position::first_child_of(use_tree.syntax()), path.syntax()); - if single_subtree.use_tree_list().is_some() || single_subtree.star_token().is_some() + new_use_tree_list.push(normalized); + modified = true; + } else { + new_use_tree_list.push(subtree); + } + } + + let mut use_tree = + if modified { with_use_tree_list(&use_tree, new_use_tree_list, make)? } else { use_tree }; + + let mut use_tree_list = use_tree.use_tree_list()?.use_trees().collect::<Vec<_>>(); + let mut anchor_idx = 0; + let mut merged_any = false; + while anchor_idx < use_tree_list.len() { + let mut candidate_idx = anchor_idx + 1; + while candidate_idx < use_tree_list.len() { + if let Some(mut merged) = try_merge_trees_with_factory( + use_tree_list[anchor_idx].clone(), + use_tree_list[candidate_idx].clone(), + MergeBehavior::Crate, + make, + ) { + if let Some(normalized) = + recursive_normalize(merged.clone(), NormalizationStyle::Default, make) { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - make::token(T![::]), - ); + merged = normalized; } + + use_tree_list[anchor_idx] = merged; + use_tree_list.remove(candidate_idx); + merged_any = true; + } else { + candidate_idx += 1; } - if let Some(inner_use_tree_list) = single_subtree.use_tree_list() { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - inner_use_tree_list.syntax(), - ); - } else if single_subtree.star_token().is_some() { - ted::insert_raw(Position::last_child_of(use_tree.syntax()), make::token(T![*])); - } else if let Some(rename) = single_subtree.rename() { - ted::insert_raw( - Position::last_child_of(use_tree.syntax()), - make::tokens::single_space(), - ); - ted::insert_raw(Position::last_child_of(use_tree.syntax()), rename.syntax()); - } - Some(()) - } else { - // Bail on semantically empty use trees. - None } - }; - let one_style_tree_list = |subtree: &ast::UseTree| match ( - subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none(), - subtree.use_tree_list(), - ) { - (true, tree_list) => tree_list, - _ => None, - }; - let add_element_to_list = |elem: SyntaxElement, elements: &mut Vec<SyntaxElement>| { - if !elements.is_empty() { - elements.push(make::token(T![,]).into()); - elements.push(make::tokens::single_space().into()); + + anchor_idx += 1; + } + if merged_any { + use_tree = with_use_tree_list(&use_tree, use_tree_list, make)?; + modified = true; + } + + if style != NormalizationStyle::One { + let subtrees = use_tree.use_tree_list()?.use_trees().collect::<Vec<_>>(); + if subtrees.len() == 1 + && let Some(merged) = merge_single_subtree_into_parent_tree(use_tree.clone(), make) + { + use_tree = merged; + modified = true; } - elements.push(elem); - }; - if let Some((single_subtree,)) = use_tree_list.use_trees().collect_tuple() { - if style == NormalizationStyle::One { - // Only normalize descendant subtrees if the normalization style is "one". - recursive_normalize(&single_subtree, NormalizationStyle::Default)?; - } else { - // Otherwise, merge the single subtree into it's parent (if possible) - // and then normalize the result. - merge_subtree_into_parent_tree(&single_subtree)?; - recursive_normalize(use_tree, style); + } + + if let Some(list) = use_tree.use_tree_list() { + let mut use_tree_list = list.use_trees().collect::<Vec<_>>(); + if use_tree_list + .windows(2) + .any(|trees| use_tree_cmp_bin_search(&trees[0], &trees[1]).is_gt()) + { + use_tree_list.sort_unstable_by(use_tree_cmp_bin_search); + use_tree = with_use_tree_list(&use_tree, use_tree_list, make)?; + modified = true; } - } else { - // Tracks whether any changes have been made to the use tree. - let mut modified = false; + } - // Recursively un-nests (if necessary) and then normalizes each subtree in the tree list. - for subtree in use_tree_list.use_trees() { - if let Some(one_tree_list) = one_style_tree_list(&subtree) { - let mut elements = Vec::new(); - let mut one_tree_list_iter = one_tree_list.use_trees(); - let mut prev_skipped = Vec::new(); - loop { - let mut prev_skipped_iter = prev_skipped.into_iter(); - let mut curr_skipped = Vec::new(); - - while let Some(sub_sub_tree) = - one_tree_list_iter.next().or(prev_skipped_iter.next()) - { - if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) { - curr_skipped.extend(sub_one_tree_list.use_trees()); - } else { - modified |= - recursive_normalize(&sub_sub_tree, NormalizationStyle::Default) - .is_some(); - add_element_to_list( - sub_sub_tree.syntax().clone().into(), - &mut elements, - ); - } - } + modified.then_some(use_tree) +} - if curr_skipped.is_empty() { - // Un-nesting is complete. - break; - } - prev_skipped = curr_skipped; - } +fn flatten_one_style_tree( + subtree: ast::UseTree, + elements: &mut Vec<ast::UseTree>, + modified: &mut bool, + make: &SyntaxFactory, +) { + let Some(one_tree_list) = one_style_tree_list(&subtree) else { return }; + let mut one_tree_list_iter = one_tree_list.use_trees(); + let mut prev_skipped = Vec::new(); + loop { + let mut prev_skipped_iter = prev_skipped.into_iter(); + let mut curr_skipped = Vec::new(); - // Either removes the subtree (if its semantically empty) or replaces it with - // the un-nested elements. - if elements.is_empty() { - subtree.remove(); - } else { - ted::replace_with_many(subtree.syntax(), elements); - } - // Silence unused assignment warning on `modified`. - let _ = modified; - modified = true; + while let Some(sub_sub_tree) = + one_tree_list_iter.next().or_else(|| prev_skipped_iter.next()) + { + if let Some(sub_one_tree_list) = one_style_tree_list(&sub_sub_tree) { + curr_skipped.extend(sub_one_tree_list.use_trees()); + } else if let Some(normalized) = + recursive_normalize(sub_sub_tree.clone(), NormalizationStyle::Default, make) + { + *modified = true; + elements.push(normalized); } else { - modified |= recursive_normalize(&subtree, NormalizationStyle::Default).is_some(); + elements.push(sub_sub_tree); } } - // Merge all merge-able subtrees. - let mut tree_list_iter = use_tree_list.use_trees(); - let mut anchor = tree_list_iter.next()?; - let mut prev_skipped = Vec::new(); - loop { - let mut has_merged = false; - let mut prev_skipped_iter = prev_skipped.into_iter(); - let mut next_anchor = None; - let mut curr_skipped = Vec::new(); - - while let Some(candidate) = tree_list_iter.next().or(prev_skipped_iter.next()) { - let result = try_merge_trees_mut(&anchor, &candidate, MergeBehavior::Crate); - if result.is_some() { - // Remove merged subtree. - candidate.remove(); - has_merged = true; - } else if next_anchor.is_none() { - next_anchor = Some(candidate); - } else { - curr_skipped.push(candidate); - } - } - - if has_merged { - // Normalize the merge result. - recursive_normalize(&anchor, NormalizationStyle::Default); - modified = true; - } + if curr_skipped.is_empty() { + break; + } + prev_skipped = curr_skipped; + } +} - let (Some(next_anchor), true) = (next_anchor, !curr_skipped.is_empty()) else { - // Merging is complete. - break; - }; +fn merge_single_subtree_into_parent_tree( + use_tree: ast::UseTree, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { + let single_subtree = get_single_subtree(&use_tree)?; + let subtree_is_only_self = single_subtree.path().as_ref().is_some_and(path_is_self); + + let merged_path = match (use_tree.path(), single_subtree.path()) { + _ if subtree_is_only_self => None, + (None, None) => None, + (Some(outer), None) => Some(outer), + (None, Some(inner)) => Some(inner), + (Some(outer), Some(inner)) => Some(make.path_concat(outer, inner)), + }; - // Try to merge the remaining subtrees in the next iteration. - anchor = next_anchor; - prev_skipped = curr_skipped; - } + let list = single_subtree.use_tree_list(); + let list_is_none = list.is_none(); + let star = single_subtree.star_token().is_some(); + if merged_path.is_some() || list.is_some() || star { + let rename = (!star && list_is_none).then(|| single_subtree.rename()).flatten(); + make_use_tree_from_parts(make, merged_path, list, rename, star) + } else { + None + } +} - let mut subtrees: Vec<_> = use_tree_list.use_trees().collect(); - // Merge the remaining subtree into its parent, if its only one and - // the normalization style is not "one". - if subtrees.len() == 1 && style != NormalizationStyle::One { - modified |= merge_subtree_into_parent_tree(&subtrees[0]).is_some(); - } - // Order the remaining subtrees (if necessary). - if subtrees.len() > 1 { - let mut did_sort = false; - subtrees.sort_unstable_by(|a, b| { - let order = use_tree_cmp_bin_search(a, b); - if !did_sort && order == Ordering::Less { - did_sort = true; - } - order - }); - if did_sort { - let start = use_tree_list - .l_curly_token() - .and_then(|l_curly| algo::non_trivia_sibling(l_curly.into(), Direction::Next)) - .filter(|it| it.kind() != T!['}']); - let end = use_tree_list - .r_curly_token() - .and_then(|r_curly| algo::non_trivia_sibling(r_curly.into(), Direction::Prev)) - .filter(|it| it.kind() != T!['{']); - if let Some((start, end)) = start.zip(end) { - // Attempt to insert elements while preserving preceding and trailing trivia. - let mut elements = Vec::new(); - for subtree in subtrees { - add_element_to_list(subtree.syntax().clone().into(), &mut elements); - } - ted::replace_all(start..=end, elements); - } else { - let new_use_tree_list = make::use_tree_list(subtrees).clone_for_update(); - ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax()); - } - modified = true; - } - } +fn one_style_tree_list(subtree: &ast::UseTree) -> Option<ast::UseTreeList> { + (subtree.path().is_none() && subtree.star_token().is_none() && subtree.rename().is_none()) + .then(|| subtree.use_tree_list()) + .flatten() +} - if !modified { - // Either the use tree was already normalized or its semantically empty. - return None; +fn remove_subtree_if_only_self( + use_tree: ast::UseTree, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { + let Some(single_subtree) = get_single_subtree(&use_tree) else { + return Some(use_tree); + }; + match (use_tree.path(), single_subtree.path()) { + (Some(path), Some(inner)) if path_is_self(&inner) => { + Some(make.use_tree(path, None, use_tree.rename(), false)) } + _ => Some(use_tree), } - Some(()) } /// Traverses both paths until they differ, returning the common prefix of both. @@ -513,10 +494,9 @@ pub fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { let lhs_is_simple_path = lhs.is_simple_path() && lhs.rename().is_none(); let rhs_is_simple_path = rhs.is_simple_path() && rhs.rename().is_none(); - match ( - lhs.path().as_ref().and_then(ast::Path::first_segment), - rhs.path().as_ref().and_then(ast::Path::first_segment), - ) { + let lhs_segment = lhs.path().and_then(|path| path.first_segment()); + let rhs_segment = rhs.path().and_then(|path| path.first_segment()); + match (lhs_segment, rhs_segment) { (None, None) => match (lhs_is_simple_path, rhs_is_simple_path) { (true, true) => Ordering::Equal, (true, false) => Ordering::Less, @@ -701,14 +681,164 @@ fn get_single_subtree(use_tree: &ast::UseTree) -> Option<ast::UseTree> { .map(|(single_subtree,)| single_subtree) } -fn remove_subtree_if_only_self(use_tree: &ast::UseTree) { - let Some(single_subtree) = get_single_subtree(use_tree) else { return }; - match (use_tree.path(), single_subtree.path()) { - (Some(_), Some(inner)) if path_is_self(&inner) => { - ted::remove_all_iter(single_subtree.syntax().children_with_tokens()); - } - _ => (), +fn make_use_with_tree(original: &ast::Use, use_tree: ast::UseTree) -> Option<ast::Use> { + let (editor, use_item) = SyntaxEditor::with_ast_node(original); + let original_tree = use_item.use_tree()?; + editor.replace(original_tree.syntax(), use_tree.syntax()); + let edit = editor.finish(); + ast::Use::cast(edit.new_root().clone()) +} + +fn make_use_tree_list( + make: &SyntaxFactory, + use_trees: Vec<ast::UseTree>, + style_source: Option<&ast::UseTreeList>, +) -> Option<ast::UseTreeList> { + let use_tree_list = make.use_tree_list(use_trees); + let Some(style_source) = style_source else { + return Some(use_tree_list); + }; + + let source_l_curly = style_source.l_curly_token()?; + let source_r_curly = style_source.r_curly_token()?; + + let leading_ws = source_l_curly.next_token().filter(|token| token.kind().is_trivia()); + + let trailing_ws = source_r_curly.prev_token().filter(|token| token.kind().is_trivia()); + + let source_trailing_token = trailing_ws + .as_ref() + .and_then(|token| token.prev_token()) + .or_else(|| source_r_curly.prev_token()); + + let source_has_trailing_comma = + source_trailing_token.is_some_and(|token| token.kind() == T![,]); + + let (editor, use_tree_list) = SyntaxEditor::with_ast_node(&use_tree_list); + let make = editor.make(); + + if let Some(leading_ws) = leading_ws { + editor.insert( + Position::after(use_tree_list.l_curly_token()?), + make.whitespace(leading_ws.text()), + ); + } + + let r_curly = use_tree_list.r_curly_token()?; + + let generated_has_trailing_comma = r_curly + .prev_token() + .and_then(|token| if token.kind().is_trivia() { token.prev_token() } else { Some(token) }) + .is_some_and(|token| token.kind() == T![,]); + + let mut trailing = Vec::new(); + + if source_has_trailing_comma + && !generated_has_trailing_comma + && use_tree_list.use_trees().next().is_some() + { + trailing.push(make.token(T![,]).into()); + } + + if let Some(trailing_ws) = trailing_ws { + trailing.push(make.whitespace(trailing_ws.text()).into()); + } + + if !trailing.is_empty() { + editor.insert_all(Position::before(r_curly), trailing); + } + + let edit = editor.finish(); + ast::UseTreeList::cast(edit.new_root().clone()) +} + +fn make_use_tree_from_list(make: &SyntaxFactory, list: ast::UseTreeList) -> Option<ast::UseTree> { + let placeholder = make.use_tree_glob(); + let (editor, use_tree) = SyntaxEditor::with_ast_node(&placeholder); + let first_child = use_tree.syntax().first_child_or_token()?; + let last_child = use_tree.syntax().last_child_or_token()?; + editor.replace_all(first_child..=last_child, vec![list.syntax().clone().into()]); + let edit = editor.finish(); + ast::UseTree::cast(edit.new_root().clone()) +} + +fn make_use_tree_from_parts( + make: &SyntaxFactory, + path: Option<ast::Path>, + list: Option<ast::UseTreeList>, + rename: Option<ast::Rename>, + star: bool, +) -> Option<ast::UseTree> { + match (path, list, star) { + (Some(path), list, star) => Some(make.use_tree(path, list, rename, star)), + (None, Some(list), false) if rename.is_none() => make_use_tree_from_list(make, list), + (None, None, true) if rename.is_none() => Some(make.use_tree_glob()), + (None, None, false) if rename.is_none() => None, + _ => None, + } +} + +fn with_use_tree_list( + use_tree: &ast::UseTree, + use_trees: Vec<ast::UseTree>, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { + let list = make_use_tree_list(make, use_trees, use_tree.use_tree_list().as_ref())?; + make_use_tree_from_parts( + make, + use_tree.path(), + Some(list), + use_tree.rename(), + use_tree.star_token().is_some(), + ) +} + +pub(crate) fn wrap_in_tree_list( + use_tree: &ast::UseTree, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { + if use_tree.path().is_none() + && use_tree.use_tree_list().is_some() + && use_tree.rename().is_none() + && use_tree.star_token().is_none() + { + return None; } + + let list = make_use_tree_list(make, vec![use_tree.clone()], None)?; + make_use_tree_from_list(make, list) +} + +fn split_prefix( + use_tree: &ast::UseTree, + prefix: &ast::Path, + make: &SyntaxFactory, +) -> Option<ast::UseTree> { + let path = use_tree.path()?; + if path == *prefix && use_tree.use_tree_list().is_some() { + return Some(use_tree.clone()); + } + + let suffix = if path == *prefix { + if use_tree.star_token().is_some() { + make.use_tree_glob() + } else { + let self_path = make.path_unqualified(make.path_segment_self()); + make.use_tree(self_path, None, use_tree.rename(), false) + } + } else { + let suffix_segments = path.segments().skip(prefix.segments().count()); + let suffix_path = make.path_from_segments(suffix_segments, false); + make.use_tree( + suffix_path, + use_tree.use_tree_list(), + use_tree.rename(), + use_tree.star_token().is_some(), + ) + }; + + let list = make_use_tree_list(make, vec![suffix], None)?; + Some(make.use_tree(prefix.clone(), Some(list), None, false)) } // Taken from rustfmt |