Unnamed repository; edit this file 'description' to name the repository.
Migrate remove unused imports to SYntaxEditor
bit-aloo 8 days ago
parent 87d7d78 · commit f627fc7
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_imports.rs17
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs65
-rw-r--r--crates/syntax/src/ast/node_ext.rs13
3 files changed, 63 insertions, 32 deletions
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index c90623ceed..2958acc478 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -111,19 +111,24 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_, '
is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u)
}
})
- .peekable();
+ .collect::<Vec<_>>();
- // Peek so we terminate early if an unused use is found. Only do the rest of the work if the user selects the assist.
- if unused.peek().is_some() {
+ // Terminate early unless an unused use is found. Only do the rest of the work if the user selects the assist.
+ if !unused.is_empty() {
acc.add(
AssistId::quick_fix("remove_unused_imports"),
"Remove all unused imports",
selected_el.text_range(),
|builder| {
- let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect();
- for node in unused {
- node.remove_recursive();
+ let editor = builder.make_editor(&selected_el);
+ unused.sort_by_key(|use_tree| use_tree.syntax().text_range().start());
+ for node in &unused {
+ editor.delete(node.syntax());
}
+ for node in unused.iter().cloned() {
+ node.remove_recursive(&editor);
+ }
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
} else {
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 9f12f94ce1..4a8c9d450c 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -56,24 +56,41 @@ impl Removable for ast::UseTree {
}
impl ast::UseTree {
+ /// Editor variant of UseTree remove
+ fn remove_with_editor(&self, editor: &SyntaxEditor) {
+ for dir in [Direction::Next, Direction::Prev] {
+ if let Some(next_use_tree) = neighbor(self, dir) {
+ let separators = self
+ .syntax()
+ .siblings_with_tokens(dir)
+ .skip(1)
+ .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
+ for separator in separators {
+ editor.delete(separator);
+ }
+ break;
+ }
+ }
+ editor.delete(self.syntax());
+ }
+
/// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty.
- pub fn remove_recursive(self) {
+ pub fn remove_recursive(self, editor: &SyntaxEditor) {
let parent = self.syntax().parent();
- self.remove();
-
if let Some(u) = parent.clone().and_then(ast::Use::cast) {
- if u.use_tree().is_none() {
- u.remove();
- }
+ u.remove(editor);
} else if let Some(u) = parent.and_then(ast::UseTreeList::cast) {
- if u.use_trees().next().is_none() {
- let parent = u.syntax().parent().and_then(ast::UseTree::cast);
- if let Some(u) = parent {
- u.remove_recursive();
- }
+ if u.use_trees().nth(1).is_none()
+ || u.use_trees().all(|use_tree| {
+ use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax())
+ })
+ {
+ u.parent_use_tree().remove_recursive(editor);
+ return;
}
- u.remove_unnecessary_braces();
+ self.remove_with_editor(editor);
+ u.remove_unnecessary_braces(editor);
}
}
@@ -224,8 +241,9 @@ impl ast::UseTreeList {
}
}
-impl Removable for ast::Use {
- fn remove(&self) {
+impl ast::Use {
+ fn remove(&self, editor: &SyntaxEditor) {
+ let make = editor.make();
let next_ws = self
.syntax()
.next_sibling_or_token()
@@ -234,10 +252,17 @@ impl Removable for ast::Use {
if let Some(next_ws) = next_ws {
let ws_text = next_ws.syntax().text();
if let Some(rest) = ws_text.strip_prefix('\n') {
- if rest.is_empty() {
- ted::remove(next_ws.syntax());
+ let next_use_removed = next_ws
+ .syntax()
+ .next_sibling_or_token()
+ .and_then(|it| it.into_node())
+ .and_then(ast::Use::cast)
+ .and_then(|use_| use_.use_tree())
+ .is_some_and(|use_tree| editor.deleted(use_tree.syntax()));
+ if rest.is_empty() || next_use_removed {
+ editor.delete(next_ws.syntax());
} else {
- ted::replace(next_ws.syntax(), make::tokens::whitespace(rest));
+ editor.replace(next_ws.syntax(), make.whitespace(rest));
}
}
}
@@ -251,13 +276,13 @@ impl Removable for ast::Use {
let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0);
let rest = &ws_text[0..prev_newline];
if rest.is_empty() {
- ted::remove(prev_ws.syntax());
+ editor.delete(prev_ws.syntax());
} else {
- ted::replace(prev_ws.syntax(), make::tokens::whitespace(rest));
+ editor.replace(prev_ws.syntax(), make.whitespace(rest));
}
}
- ted::remove(self.syntax());
+ editor.delete(self.syntax());
}
}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 751f8d7e1c..1eb658f4b8 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -16,7 +16,7 @@ use crate::{
self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
HasTypeBounds, SyntaxNode, support,
},
- ted,
+ syntax_editor::SyntaxEditor,
};
use super::{GenericParam, RangeItem, RangeOp};
@@ -454,11 +454,12 @@ impl ast::UseTreeList {
}
/// Remove the unnecessary braces in current `UseTreeList`
- pub fn remove_unnecessary_braces(mut self) {
+ pub fn remove_unnecessary_braces(mut self, editor: &SyntaxEditor) {
// Returns true iff there is a single subtree and it is not the self keyword. The braces in
// `use x::{self};` are necessary and so we should not remove them.
let has_single_subtree_that_is_not_self = |u: &ast::UseTreeList| {
- if let Some((single_subtree,)) = u.use_trees().collect_tuple() {
+ let use_trees = u.use_trees().filter(|use_tree| !editor.deleted(use_tree.syntax()));
+ if let Some((single_subtree,)) = use_trees.collect_tuple() {
// We have a single subtree, check whether it is self.
let is_self = single_subtree.path().as_ref().is_some_and(|path| {
@@ -476,12 +477,12 @@ impl ast::UseTreeList {
let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| {
if has_single_subtree_that_is_not_self(u) {
if let Some(a) = u.l_curly_token() {
- ted::remove(a)
+ editor.delete(a)
}
if let Some(a) = u.r_curly_token() {
- ted::remove(a)
+ editor.delete(a)
}
- u.comma().for_each(ted::remove);
+ u.comma().for_each(|u| editor.delete(u));
}
};