From 1b4bfba213ee638990bcc654468ef300cfeec265 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 8 Jul 2025 16:12:58 +0900 Subject: [PATCH 1/2] Migrate `pull_assignment_up` assist to `SyntaxEditor` Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../src/handlers/generate_getter_or_setter.rs | 2 +- .../src/handlers/pull_assignment_up.rs | 42 ++++++++++--------- crates/syntax/src/ast/make.rs | 2 +- .../src/ast/syntax_factory/constructors.rs | 13 ++++++ 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index c7e5e41aac4c..20ee9253d379 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -294,7 +294,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI let self_expr = make::ext::expr_self(); let lhs = make::expr_field(self_expr, field_name); let rhs = make::expr_path(make::ext::ident_path(field_name)); - let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs)); + let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into()); let body = make::block_expr([assign_stmt.into()], None); // Make the setter fn diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index 5f626d295711..2209b784d9be 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -1,7 +1,7 @@ use syntax::{ - AstNode, - ast::{self, make}, - ted, + AstNode, TextRange, + algo::find_node_at_range, + ast::{self, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -66,33 +66,37 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> return None; } } - + let target = tgt.syntax().text_range(); acc.add( AssistId::refactor_extract("pull_assignment_up"), "Pull assignment up", - tgt.syntax().text_range(), + target, move |edit| { - let assignments: Vec<_> = collector - .assignments - .into_iter() - .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update())) - .collect(); - - let tgt = edit.make_mut(tgt); - - for (stmt, rhs) in assignments { + let mut editor = edit.make_editor(tgt.syntax()); + let mut new_target_end = target.end(); + for (stmt, rhs) in collector.assignments { let mut stmt = stmt.syntax().clone(); if let Some(parent) = stmt.parent() { if ast::ExprStmt::cast(parent.clone()).is_some() { stmt = parent.clone(); } } - ted::replace(stmt, rhs.syntax()); + let diff = stmt.text_range().len() - rhs.syntax().text_range().len(); + new_target_end -= diff; + editor.replace(stmt, rhs.syntax()); } - let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone()); - let assign_stmt = make::expr_stmt(assign_expr); - - ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update()); + let new_tgt_root = editor.finish().new_root().clone(); + let new_target_range = TextRange::new(target.start(), new_target_end); + let new_tgt = find_node_at_range::(&new_tgt_root, new_target_range).unwrap(); + + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(tgt.syntax()); + let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone()); + let assign_stmt = make.expr_stmt(assign_expr.into()); + + editor.replace(tgt.syntax(), assign_stmt.syntax()); + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 309332873cb8..d67f24fda96a 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -680,7 +680,7 @@ pub fn expr_tuple(elements: impl IntoIterator) -> ast::TupleEx let expr = elements.into_iter().format(", "); expr_from_text(&format!("({expr})")) } -pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { +pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { expr_from_text(&format!("{lhs} = {rhs}")) } fn expr_from_text + AstNode>(text: &str) -> E { diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 17cc5f9c0570..1ba610731512 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -440,6 +440,19 @@ impl SyntaxFactory { ast } + pub fn expr_assignment(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { + let ast = make::expr_assignment(lhs.clone(), rhs.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lhs.syntax().clone(), ast.lhs().unwrap().syntax().clone()); + builder.map_node(rhs.syntax().clone(), ast.rhs().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr { let ast::Expr::BinExpr(ast) = make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update() From 24eeab0e93af0f17bbca7d94b88a7ad8406bc809 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 8 Jul 2025 23:19:46 +0900 Subject: [PATCH 2/2] pull up editor Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../src/handlers/pull_assignment_up.rs | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index 2209b784d9be..25eb3fdd5f02 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -1,7 +1,8 @@ use syntax::{ - AstNode, TextRange, + AstNode, algo::find_node_at_range, ast::{self, syntax_factory::SyntaxFactory}, + syntax_editor::SyntaxEditor, }; use crate::{ @@ -67,28 +68,43 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> } } let target = tgt.syntax().text_range(); + + let edit_tgt = tgt.syntax().clone_subtree(); + let assignments: Vec<_> = collector + .assignments + .into_iter() + .filter_map(|(stmt, rhs)| { + Some(( + find_node_at_range::( + &edit_tgt, + stmt.syntax().text_range() - target.start(), + )?, + find_node_at_range::( + &edit_tgt, + rhs.syntax().text_range() - target.start(), + )?, + )) + }) + .collect(); + + let mut editor = SyntaxEditor::new(edit_tgt); + + for (stmt, rhs) in assignments { + let mut stmt = stmt.syntax().clone(); + if let Some(parent) = stmt.parent() { + if ast::ExprStmt::cast(parent.clone()).is_some() { + stmt = parent.clone(); + } + } + editor.replace(stmt, rhs.syntax()); + } + let new_tgt_root = editor.finish().new_root().clone(); + let new_tgt = ast::Expr::cast(new_tgt_root)?; acc.add( AssistId::refactor_extract("pull_assignment_up"), "Pull assignment up", target, move |edit| { - let mut editor = edit.make_editor(tgt.syntax()); - let mut new_target_end = target.end(); - for (stmt, rhs) in collector.assignments { - let mut stmt = stmt.syntax().clone(); - if let Some(parent) = stmt.parent() { - if ast::ExprStmt::cast(parent.clone()).is_some() { - stmt = parent.clone(); - } - } - let diff = stmt.text_range().len() - rhs.syntax().text_range().len(); - new_target_end -= diff; - editor.replace(stmt, rhs.syntax()); - } - let new_tgt_root = editor.finish().new_root().clone(); - let new_target_range = TextRange::new(target.start(), new_target_end); - let new_tgt = find_node_at_range::(&new_tgt_root, new_target_range).unwrap(); - let make = SyntaxFactory::with_mappings(); let mut editor = edit.make_editor(tgt.syntax()); let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone());