about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHayashi Mikihiro <34ttrweoewiwe28@gmail.com>2025-07-27 13:24:08 +0900
committerHayashi Mikihiro <34ttrweoewiwe28@gmail.com>2025-07-27 22:56:24 +0900
commit8f898729220258b58d8044d4adaff14d45288fe6 (patch)
treebbf0a53c5b2c2aa6c04c62074b62486f9c992788
parent85486a7bff683fd9ec241a92ca75f38659b4e76c (diff)
downloadrust-8f898729220258b58d8044d4adaff14d45288fe6.tar.gz
rust-8f898729220258b58d8044d4adaff14d45288fe6.zip
Migrate `convert_tuple_struct_to_named_struct' assist to use `SyntaxEditor'
Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com>
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs103
1 files changed, 68 insertions, 35 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 6d729c9ea99..f4041f49419 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,7 +1,9 @@
 use either::Either;
+use hir::FileRangeWrapper;
 use ide_db::defs::{Definition, NameRefClass};
+use std::ops::RangeInclusive;
 use syntax::{
-    SyntaxKind, SyntaxNode, T,
+    SyntaxElement, SyntaxKind, SyntaxNode, T, TextSize,
     ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
     match_ast,
     syntax_editor::{Element, Position, SyntaxEditor},
@@ -80,8 +82,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
         |edit| {
             let names = generate_names(tuple_fields.fields());
             edit_field_references(ctx, edit, tuple_fields.fields(), &names);
-            edit_struct_references(ctx, edit, strukt_def, &names);
             let mut editor = edit.make_editor(syntax);
+            edit_struct_references(ctx, edit, strukt_def, &names);
             edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names);
             edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
@@ -142,27 +144,21 @@ fn edit_struct_references(
     };
     let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
 
-    let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
+    let edit_node = |node: SyntaxNode| -> Option<SyntaxNode> {
         match_ast! {
             match node {
                 ast::TupleStructPat(tuple_struct_pat) => {
-                    let file_range = ctx.sema.original_range_opt(&node)?;
-                    edit.edit_file(file_range.file_id.file_id(ctx.db()));
-                    edit.replace(
-                        file_range.range,
-                        ast::make::record_pat_with_fields(
-                            tuple_struct_pat.path()?,
-                            ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
-                                |(pat, name)| {
-                                    ast::make::record_pat_field(
-                                        ast::make::name_ref(&name.to_string()),
-                                        pat,
-                                    )
-                                },
-                            ), None),
-                        )
-                        .to_string(),
-                    );
+                    Some(ast::make::record_pat_with_fields(
+                        tuple_struct_pat.path()?,
+                        ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
+                            |(pat, name)| {
+                                ast::make::record_pat_field(
+                                    ast::make::name_ref(&name.to_string()),
+                                    pat,
+                                )
+                            },
+                        ), None),
+                    ).syntax().clone_for_update())
                 },
                 // for tuple struct creations like Foo(42)
                 ast::CallExpr(call_expr) => {
@@ -179,8 +175,7 @@ fn edit_struct_references(
 
                     let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
 
-                    edit.replace(
-                        ctx.sema.original_range(&node).range,
+                    Some(
                         ast::make::record_expr(
                             path,
                             ast::make::record_expr_field_list(arg_list.args().zip(names).map(
@@ -191,25 +186,58 @@ fn edit_struct_references(
                                     )
                                 },
                             )),
-                        )
-                        .to_string(),
-                    );
+                        ).syntax().clone_for_update()
+                    )
                 },
                 _ => return None,
             }
         }
-        Some(())
     };
 
     for (file_id, refs) in usages {
-        edit.edit_file(file_id.file_id(ctx.db()));
-        for r in refs {
-            for node in r.name.syntax().ancestors() {
-                if edit_node(edit, node).is_some() {
-                    break;
+        let source = ctx.sema.parse(file_id);
+        let source = source.syntax();
+
+        let mut editor = edit.make_editor(source);
+        for r in refs.iter().rev() {
+            if let Some((old_node, new_node)) = r
+                .name
+                .syntax()
+                .ancestors()
+                .find_map(|node| Some((node.clone(), edit_node(node.clone())?)))
+            {
+                if let Some(old_node) = ctx.sema.original_syntax_node_rooted(&old_node) {
+                    editor.replace(old_node, new_node);
+                } else {
+                    let FileRangeWrapper { file_id: _, range } = ctx.sema.original_range(&old_node);
+                    let parent = source.covering_element(range);
+                    match parent {
+                        SyntaxElement::Token(token) => {
+                            editor.replace(token, new_node.syntax_element());
+                        }
+                        SyntaxElement::Node(parent_node) => {
+                            // replace the part of macro
+                            // ```
+                            // foo!(a, Test::A(0));
+                            //     ^^^^^^^^^^^^^^^ // parent_node
+                            //         ^^^^^^^^^^  // replace_range
+                            // ```
+                            let start = parent_node
+                                .children_with_tokens()
+                                .find(|t| t.text_range().contains(range.start()));
+                            let end = parent_node
+                                .children_with_tokens()
+                                .find(|t| t.text_range().contains(range.end() - TextSize::new(1)));
+                            if let (Some(start), Some(end)) = (start, end) {
+                                let replace_range = RangeInclusive::new(start, end);
+                                editor.replace_all(replace_range, vec![new_node.into()]);
+                            }
+                        }
+                    }
                 }
             }
         }
+        edit.add_file_edits(file_id.file_id(ctx.db()), editor);
     }
 }
 
@@ -227,12 +255,17 @@ fn edit_field_references(
         let def = Definition::Field(field);
         let usages = def.usages(&ctx.sema).all();
         for (file_id, refs) in usages {
-            edit.edit_file(file_id.file_id(ctx.db()));
+            let source = ctx.sema.parse(file_id);
+            let source = source.syntax();
+            let mut editor = edit.make_editor(source);
             for r in refs {
-                if let Some(name_ref) = r.name.as_name_ref() {
-                    edit.replace(ctx.sema.original_range(name_ref.syntax()).range, name.text());
+                if let Some(name_ref) = r.name.as_name_ref()
+                    && let Some(original) = ctx.sema.original_ast_node(name_ref.clone())
+                {
+                    editor.replace(original.syntax(), name.syntax());
                 }
             }
+            edit.add_file_edits(file_id.file_id(ctx.db()), editor);
         }
     }
 }
@@ -242,7 +275,7 @@ fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Nam
         .enumerate()
         .map(|(i, _)| {
             let idx = i + 1;
-            ast::make::name(&format!("field{idx}"))
+            ast::make::name(&format!("field{idx}")).clone_for_update()
         })
         .collect()
 }