Migrate AstNodeEdit
diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs
index bcd06c1e..d7b7e8d 100644
--- a/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -228,8 +228,7 @@
                     closure_body,
                     Some(ast::ElseBranch::Block(make.block_expr(None, Some(none_path)))),
                 )
-                .indent(mcall.indent_level())
-                .clone_for_update();
+                .indent(mcall.indent_level());
             editor.replace(mcall.syntax().clone(), if_expr.syntax().clone());
 
             editor.add_mappings(make.finish_with_mappings());
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index 71a61f2..bf1cfc7 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -117,7 +117,7 @@
 
     then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
 
-    let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
+    let then_block_items = then_block.dedent(IndentLevel(1));
 
     let end_of_then = then_block_items.syntax().last_child_or_token()?;
     let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
@@ -143,7 +143,7 @@
                         let cond = invert_boolean_expression_legacy(cond_expr);
                         make::expr_if(cond, then_branch, None).indent(if_indent_level)
                     };
-                    new_expr.syntax().clone_for_update()
+                    new_expr.syntax().clone()
                 }
                 Some(pat) => {
                     // If-let.
@@ -154,7 +154,7 @@
                         ast::make::tail_only_block_expr(early_expression),
                     );
                     let let_else_stmt = let_else_stmt.indent(if_indent_level);
-                    let_else_stmt.syntax().clone_for_update()
+                    let_else_stmt.syntax().clone()
                 }
             };
 
@@ -225,7 +225,7 @@
                     ast::make::tail_only_block_expr(early_expression),
                 );
                 let let_else_stmt = let_else_stmt.indent(let_indent_level);
-                let_else_stmt.syntax().clone_for_update()
+                let_else_stmt.syntax().clone()
             };
 
             ted::replace(let_stmt.syntax(), replacement)
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index 31e84e9..db2d316 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -7,7 +7,9 @@
     NodeOrToken, SyntaxKind, SyntaxNode, T,
     algo::ancestors_at_offset,
     ast::{
-        self, AstNode, edit::IndentLevel, edit_in_place::Indent, make,
+        self, AstNode,
+        edit::{AstNodeEdit, IndentLevel},
+        make,
         syntax_factory::SyntaxFactory,
     },
     syntax_editor::Position,
@@ -253,12 +255,11 @@
                             // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
                             editor.replace(expr_replace, name_expr.syntax());
                             make.block_expr([new_stmt], Some(to_wrap.clone()))
-                        };
+                        }
+                        // fixup indentation of block
+                        .indent_with_mapping(indent_to, &make);
 
                         editor.replace(to_wrap.syntax(), block.syntax());
-
-                        // fixup indentation of block
-                        block.indent(indent_to);
                     }
                 }
 
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index 848c638..2bdc85e 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -767,7 +767,7 @@
     )
     .clone_for_update();
 
-    Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update()))
+    Some(AssocItem::Fn(func.indent(edit::IndentLevel(1))))
 }
 
 fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<AssocItem> {
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index fbce1d3..35b77d8 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -236,7 +236,7 @@
                     ),
                     new_indent_level,
                 );
-                ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
+                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() {
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index c58bdd9..026209e 100644
--- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -139,7 +139,7 @@
         }
     };
     let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
-    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+    ted::replace(func.body()?.syntax(), body.syntax());
     Some(())
 }
 
@@ -248,7 +248,7 @@
 
             let body = make::block_expr(None, Some(match_expr.into()));
             let body = body.indent(ast::edit::IndentLevel(1));
-            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+            ted::replace(func.body()?.syntax(), body.syntax());
             Some(())
         }
 
@@ -296,7 +296,7 @@
             let method = make::name_ref("finish");
             let expr = make::expr_method_call(expr, method, make::arg_list(None)).into();
             let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
-            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+            ted::replace(func.body()?.syntax(), body.syntax());
             Some(())
         }
     }
@@ -342,7 +342,7 @@
                 }
             };
             let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
-            ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+            ted::replace(func.body()?.syntax(), body.syntax());
             Some(())
         }
     }
@@ -400,7 +400,7 @@
         },
     };
 
-    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+    ted::replace(func.body()?.syntax(), body.syntax());
     Some(())
 }
 
@@ -595,7 +595,7 @@
         },
     };
 
-    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+    ted::replace(func.body()?.syntax(), body.syntax());
     Some(())
 }
 
@@ -686,7 +686,7 @@
         },
     };
 
-    ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+    ted::replace(func.body()?.syntax(), body.syntax());
     Some(())
 }
 
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 37cb4a4..d97fdec 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -6,9 +6,12 @@
 use crate::{
     AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
     ast::{self, AstNode, make},
+    syntax_editor::{SyntaxEditor, SyntaxMappingBuilder},
     ted,
 };
 
+use super::syntax_factory::SyntaxFactory;
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct IndentLevel(pub u8);
 
@@ -95,6 +98,24 @@
         }
     }
 
+    pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode {
+        let node = node.clone_subtree();
+        let mut editor = SyntaxEditor::new(node.clone());
+        let tokens = node
+            .preorder_with_tokens()
+            .filter_map(|event| match event {
+                rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
+                _ => None,
+            })
+            .filter_map(ast::Whitespace::cast)
+            .filter(|ws| ws.text().contains('\n'));
+        for ws in tokens {
+            let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax()));
+            editor.replace(ws.syntax(), &new_ws);
+        }
+        editor.finish().new_root().clone()
+    }
+
     pub(super) fn decrease_indent(self, node: &SyntaxNode) {
         let tokens = node.preorder_with_tokens().filter_map(|event| match event {
             rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
@@ -111,36 +132,54 @@
             }
         }
     }
+
+    pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode {
+        let node = node.clone_subtree();
+        let mut editor = SyntaxEditor::new(node.clone());
+        let tokens = node
+            .preorder_with_tokens()
+            .filter_map(|event| match event {
+                rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
+                _ => None,
+            })
+            .filter_map(ast::Whitespace::cast)
+            .filter(|ws| ws.text().contains('\n'));
+        for ws in tokens {
+            let new_ws =
+                make::tokens::whitespace(&ws.syntax().text().replace(&format!("\n{self}"), "\n"));
+            editor.replace(ws.syntax(), &new_ws);
+        }
+        editor.finish().new_root().clone()
+    }
 }
 
 fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
     iter::successors(Some(token), |token| token.prev_token())
 }
 
-/// Soft-deprecated in favor of mutable tree editing API `edit_in_place::Ident`.
 pub trait AstNodeEdit: AstNode + Clone + Sized {
     fn indent_level(&self) -> IndentLevel {
         IndentLevel::from_node(self.syntax())
     }
     #[must_use]
     fn indent(&self, level: IndentLevel) -> Self {
-        fn indent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode {
-            let res = node.clone_subtree().clone_for_update();
-            level.increase_indent(&res);
-            res.clone_subtree()
+        Self::cast(level.clone_increase_indent(self.syntax())).unwrap()
+    }
+    #[must_use]
+    fn indent_with_mapping(&self, level: IndentLevel, make: &SyntaxFactory) -> Self {
+        let new_node = self.indent(level);
+        if let Some(mut mapping) = make.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(new_node.syntax().clone());
+            for (old, new) in self.syntax().children().zip(new_node.syntax().children()) {
+                builder.map_node(old, new);
+            }
+            builder.finish(&mut mapping);
         }
-
-        Self::cast(indent_inner(self.syntax(), level)).unwrap()
+        new_node
     }
     #[must_use]
     fn dedent(&self, level: IndentLevel) -> Self {
-        fn dedent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode {
-            let res = node.clone_subtree().clone_for_update();
-            level.decrease_indent(&res);
-            res.clone_subtree()
-        }
-
-        Self::cast(dedent_inner(self.syntax(), level)).unwrap()
+        Self::cast(level.clone_decrease_indent(self.syntax())).unwrap()
     }
     #[must_use]
     fn reset_indent(&self) -> Self {
diff --git a/crates/syntax/src/ast/syntax_factory.rs b/crates/syntax/src/ast/syntax_factory.rs
index 7142e4f..f3ae754 100644
--- a/crates/syntax/src/ast/syntax_factory.rs
+++ b/crates/syntax/src/ast/syntax_factory.rs
@@ -38,7 +38,7 @@
         self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default()
     }
 
-    fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> {
+    pub(crate) fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> {
         self.mappings.as_ref().map(|it| it.borrow_mut())
     }
 }