Unnamed repository; edit this file 'description' to name the repository.
Migrate remove unused imports to SYntaxEditor
| -rw-r--r-- | crates/ide-assists/src/handlers/remove_unused_imports.rs | 17 | ||||
| -rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 65 | ||||
| -rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 13 |
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)); } }; |