Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/merge_imports.rs')
-rw-r--r--crates/ide-assists/src/handlers/merge_imports.rs139
1 files changed, 58 insertions, 81 deletions
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index dc40a6a640..5d81f49b18 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -1,10 +1,12 @@
-use either::Either;
use ide_db::imports::{
insert_use::{ImportGranularity, InsertUseConfig},
merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees},
};
use syntax::{
- AstNode, SyntaxElement, SyntaxNode, algo::neighbor, ast, match_ast, syntax_editor::Removable,
+ AstNode, SyntaxElement,
+ algo::neighbor,
+ ast, match_ast,
+ syntax_editor::{Removable, SyntaxEditor},
};
use crate::{
@@ -13,8 +15,6 @@ use crate::{
utils::next_prev,
};
-use Edit::*;
-
// Assist: merge_imports
//
// Merges neighbor imports with a common prefix.
@@ -28,16 +28,17 @@ use Edit::*;
// use std::{fmt::Formatter, io};
// ```
pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
- let (target, edits) = if ctx.has_empty_selection() {
+ let (target, editor) = if ctx.has_empty_selection() {
// Merge a neighbor
cov_mark::hit!(merge_with_use_item_neighbors);
let tree = ctx.find_node_at_offset::<ast::UseTree>()?.top_use_tree();
let target = tree.syntax().text_range();
let use_item = tree.syntax().parent().and_then(ast::Use::cast)?;
- let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
- let edits = use_item.try_merge_from(&mut neighbor, &ctx.config.insert_use);
- (target, edits?)
+ let neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir))?;
+ let (editor, _) = SyntaxEditor::new(use_item.syntax().parent()?.ancestors().last()?);
+ merge_uses(use_item, vec![neighbor], &ctx.config.insert_use, &editor)?;
+ (target, editor)
} else {
// Merge selected
let selection_range = ctx.selection_trimmed();
@@ -50,104 +51,80 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> O
});
let first_selected = selected_nodes.next()?;
- let edits = match_ast! {
+ let (editor, _) = SyntaxEditor::new(parent_node.ancestors().last().unwrap());
+ match_ast! {
match first_selected {
ast::Use(use_item) => {
cov_mark::hit!(merge_with_selected_use_item_neighbors);
- use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast), &ctx.config.insert_use)
+ merge_uses(
+ use_item,
+ selected_nodes.filter_map(ast::Use::cast).collect(),
+ &ctx.config.insert_use,
+ &editor,
+ )?;
},
ast::UseTree(use_tree) => {
cov_mark::hit!(merge_with_selected_use_tree_neighbors);
- use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast), &ctx.config.insert_use)
+ merge_use_trees(
+ use_tree,
+ selected_nodes.filter_map(ast::UseTree::cast).collect(),
+ &editor,
+ )?;
},
_ => return None,
}
- };
- (selection_range, edits?)
- };
-
- let parent_node = match ctx.covering_element() {
- SyntaxElement::Node(n) => n,
- SyntaxElement::Token(t) => t.parent()?,
+ }
+ (selection_range, editor)
};
acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| {
- let editor = builder.make_editor(&parent_node);
-
- for edit in edits {
- match edit {
- Remove(it) => {
- let node = it.as_ref();
- if let Some(left) = node.left() {
- left.remove(&editor);
- } else if let Some(right) = node.right() {
- right.remove(&editor);
- }
- }
- Replace(old, new) => {
- editor.replace(old, &new);
- }
- }
- }
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
-trait Merge: AstNode + Clone {
- fn try_merge_from(
- self,
- items: &mut dyn Iterator<Item = Self>,
- cfg: &InsertUseConfig,
- ) -> Option<Vec<Edit>> {
- let mut edits = Vec::new();
- let mut merged = self.clone();
- for item in items {
- merged = merged.try_merge(&item, cfg)?;
- edits.push(Edit::Remove(item.into_either()));
- }
- if !edits.is_empty() {
- edits.push(Edit::replace(self, merged));
- Some(edits)
- } else {
- None
- }
+fn merge_uses(
+ first: ast::Use,
+ rest: Vec<ast::Use>,
+ cfg: &InsertUseConfig,
+ editor: &SyntaxEditor,
+) -> Option<()> {
+ if rest.is_empty() {
+ return None;
}
- fn try_merge(&self, other: &Self, cfg: &InsertUseConfig) -> Option<Self>;
- fn into_either(self) -> Either<ast::Use, ast::UseTree>;
-}
-impl Merge for ast::Use {
- fn try_merge(&self, other: &Self, cfg: &InsertUseConfig) -> Option<Self> {
- let mb = match cfg.granularity {
- ImportGranularity::One => MergeBehavior::One,
- _ => MergeBehavior::Crate,
- };
- try_merge_imports(self, other, mb)
+ let mb = match cfg.granularity {
+ ImportGranularity::One => MergeBehavior::One,
+ _ => MergeBehavior::Crate,
+ };
+ let mut merged = first.clone();
+ for item in &rest {
+ merged = try_merge_imports(editor.make(), &merged, item, mb)?;
}
- fn into_either(self) -> Either<ast::Use, ast::UseTree> {
- Either::Left(self)
+ for item in rest {
+ item.remove(editor);
}
+ editor.replace(first.syntax(), merged.syntax());
+ Some(())
}
-impl Merge for ast::UseTree {
- fn try_merge(&self, other: &Self, _: &InsertUseConfig) -> Option<Self> {
- try_merge_trees(self, other, MergeBehavior::Crate)
- }
- fn into_either(self) -> Either<ast::Use, ast::UseTree> {
- Either::Right(self)
+fn merge_use_trees(
+ first: ast::UseTree,
+ rest: Vec<ast::UseTree>,
+ editor: &SyntaxEditor,
+) -> Option<()> {
+ if rest.is_empty() {
+ return None;
}
-}
-
-#[derive(Debug)]
-enum Edit {
- Remove(Either<ast::Use, ast::UseTree>),
- Replace(SyntaxNode, SyntaxNode),
-}
-impl Edit {
- fn replace(old: impl AstNode, new: impl AstNode) -> Self {
- Edit::Replace(old.syntax().clone(), new.syntax().clone())
+ let mut merged = first.clone();
+ for item in &rest {
+ merged = try_merge_trees(editor.make(), &merged, item, MergeBehavior::Crate)?;
+ }
+ for item in rest {
+ item.remove(editor);
}
+ editor.replace(first.syntax(), merged.syntax());
+ Some(())
}
#[cfg(test)]