about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs57
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs26
3 files changed, 67 insertions, 26 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 15c7a6a3fc2..c81a3a6f08d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -23,12 +23,11 @@ use syntax::{
     ast::{
         self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
         edit::{AstNodeEdit, IndentLevel},
-        edit_in_place::{AttrsOwnerEdit, Removable},
+        edit_in_place::AttrsOwnerEdit,
         make,
         syntax_factory::SyntaxFactory,
     },
-    syntax_editor::SyntaxEditor,
-    ted,
+    syntax_editor::{Removable, SyntaxEditor},
 };
 
 use crate::{
@@ -207,7 +206,7 @@ pub fn add_trait_assoc_items_to_impl(
                         stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
                     }
                 }
-                original_item.clone_for_update()
+                original_item
             }
             .reset_indent();
 
@@ -221,31 +220,37 @@ pub fn add_trait_assoc_items_to_impl(
             cloned_item.remove_attrs_and_docs();
             cloned_item
         })
-        .map(|item| {
-            match &item {
-                ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
-                    let body = AstNodeEdit::indent(
-                        &make::block_expr(
-                            None,
-                            Some(match config.expr_fill_default {
-                                ExprFillDefaultMode::Todo => make::ext::expr_todo(),
-                                ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
-                                ExprFillDefaultMode::Default => make::ext::expr_todo(),
-                            }),
-                        ),
-                        IndentLevel::single(),
-                    );
-                    ted::replace(fn_.get_or_create_body().syntax(), body.syntax());
-                }
-                ast::AssocItem::TypeAlias(type_alias) => {
-                    if let Some(type_bound_list) = type_alias.type_bound_list() {
-                        type_bound_list.remove()
-                    }
+        .filter_map(|item| match item {
+            ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
+                let fn_ = fn_.clone_subtree();
+                let new_body = &make::block_expr(
+                    None,
+                    Some(match config.expr_fill_default {
+                        ExprFillDefaultMode::Todo => make::ext::expr_todo(),
+                        ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+                        ExprFillDefaultMode::Default => make::ext::expr_todo(),
+                    }),
+                );
+                let new_body = AstNodeEdit::indent(new_body, IndentLevel::single());
+                let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone());
+                fn_.replace_or_insert_body(&mut fn_editor, new_body);
+                let new_fn_ = fn_editor.finish().new_root().clone();
+                ast::AssocItem::cast(new_fn_)
+            }
+            ast::AssocItem::TypeAlias(type_alias) => {
+                let type_alias = type_alias.clone_subtree();
+                if let Some(type_bound_list) = type_alias.type_bound_list() {
+                    let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone());
+                    type_bound_list.remove(&mut type_alias_editor);
+                    let type_alias = type_alias_editor.finish().new_root().clone();
+                    ast::AssocItem::cast(type_alias)
+                } else {
+                    Some(ast::AssocItem::TypeAlias(type_alias))
                 }
-                _ => {}
             }
-            AstNodeEdit::indent(&item, new_indent_level)
+            item => Some(item),
         })
+        .map(|item| AstNodeEdit::indent(&item, new_indent_level))
         .collect()
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 5107754b182..147b54c21a5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -83,6 +83,16 @@ impl SyntaxEditor {
         self.changes.push(Change::Replace(element.syntax_element(), None));
     }
 
+    pub fn delete_all(&mut self, range: RangeInclusive<SyntaxElement>) {
+        if range.start() == range.end() {
+            self.delete(range.start());
+            return;
+        }
+
+        debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root));
+        self.changes.push(Change::ReplaceAll(range, Vec::new()))
+    }
+
     pub fn replace(&mut self, old: impl Element, new: impl Element) {
         let old = old.syntax_element();
         debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
index 840e7697979..9090f7c9eb1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
@@ -153,6 +153,23 @@ impl ast::VariantList {
     }
 }
 
+impl ast::Fn {
+    pub fn replace_or_insert_body(&self, editor: &mut SyntaxEditor, body: ast::BlockExpr) {
+        if let Some(old_body) = self.body() {
+            editor.replace(old_body.syntax(), body.syntax());
+        } else {
+            let single_space = make::tokens::single_space();
+            let elements = vec![single_space.into(), body.syntax().clone().into()];
+
+            if let Some(semicolon) = self.semicolon_token() {
+                editor.replace_with_many(semicolon, elements);
+            } else {
+                editor.insert_all(Position::last_child_of(self.syntax()), elements);
+            }
+        }
+    }
+}
+
 fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> {
     let make = SyntaxFactory::without_mappings();
     let l = node
@@ -184,6 +201,15 @@ pub trait Removable: AstNode {
     fn remove(&self, editor: &mut SyntaxEditor);
 }
 
+impl Removable for ast::TypeBoundList {
+    fn remove(&self, editor: &mut SyntaxEditor) {
+        match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
+            Some(colon) => editor.delete_all(colon..=self.syntax().clone().into()),
+            None => editor.delete(self.syntax()),
+        }
+    }
+}
+
 impl Removable for ast::Use {
     fn remove(&self, editor: &mut SyntaxEditor) {
         let make = SyntaxFactory::without_mappings();