Skip to content

Commit 8f89872

Browse files
committed
Migrate convert_tuple_struct_to_named_struct' assist to use SyntaxEditor'
Signed-off-by: Hayashi Mikihiro <[email protected]>
1 parent 85486a7 commit 8f89872

File tree

1 file changed

+68
-35
lines changed

1 file changed

+68
-35
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use either::Either;
2+
use hir::FileRangeWrapper;
23
use ide_db::defs::{Definition, NameRefClass};
4+
use std::ops::RangeInclusive;
35
use syntax::{
4-
SyntaxKind, SyntaxNode, T,
6+
SyntaxElement, SyntaxKind, SyntaxNode, T, TextSize,
57
ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
68
match_ast,
79
syntax_editor::{Element, Position, SyntaxEditor},
@@ -80,8 +82,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
8082
|edit| {
8183
let names = generate_names(tuple_fields.fields());
8284
edit_field_references(ctx, edit, tuple_fields.fields(), &names);
83-
edit_struct_references(ctx, edit, strukt_def, &names);
8485
let mut editor = edit.make_editor(syntax);
86+
edit_struct_references(ctx, edit, strukt_def, &names);
8587
edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names);
8688
edit.add_file_edits(ctx.vfs_file_id(), editor);
8789
},
@@ -142,27 +144,21 @@ fn edit_struct_references(
142144
};
143145
let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
144146

145-
let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
147+
let edit_node = |node: SyntaxNode| -> Option<SyntaxNode> {
146148
match_ast! {
147149
match node {
148150
ast::TupleStructPat(tuple_struct_pat) => {
149-
let file_range = ctx.sema.original_range_opt(&node)?;
150-
edit.edit_file(file_range.file_id.file_id(ctx.db()));
151-
edit.replace(
152-
file_range.range,
153-
ast::make::record_pat_with_fields(
154-
tuple_struct_pat.path()?,
155-
ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
156-
|(pat, name)| {
157-
ast::make::record_pat_field(
158-
ast::make::name_ref(&name.to_string()),
159-
pat,
160-
)
161-
},
162-
), None),
163-
)
164-
.to_string(),
165-
);
151+
Some(ast::make::record_pat_with_fields(
152+
tuple_struct_pat.path()?,
153+
ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
154+
|(pat, name)| {
155+
ast::make::record_pat_field(
156+
ast::make::name_ref(&name.to_string()),
157+
pat,
158+
)
159+
},
160+
), None),
161+
).syntax().clone_for_update())
166162
},
167163
// for tuple struct creations like Foo(42)
168164
ast::CallExpr(call_expr) => {
@@ -179,8 +175,7 @@ fn edit_struct_references(
179175

180176
let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
181177

182-
edit.replace(
183-
ctx.sema.original_range(&node).range,
178+
Some(
184179
ast::make::record_expr(
185180
path,
186181
ast::make::record_expr_field_list(arg_list.args().zip(names).map(
@@ -191,25 +186,58 @@ fn edit_struct_references(
191186
)
192187
},
193188
)),
194-
)
195-
.to_string(),
196-
);
189+
).syntax().clone_for_update()
190+
)
197191
},
198192
_ => return None,
199193
}
200194
}
201-
Some(())
202195
};
203196

204197
for (file_id, refs) in usages {
205-
edit.edit_file(file_id.file_id(ctx.db()));
206-
for r in refs {
207-
for node in r.name.syntax().ancestors() {
208-
if edit_node(edit, node).is_some() {
209-
break;
198+
let source = ctx.sema.parse(file_id);
199+
let source = source.syntax();
200+
201+
let mut editor = edit.make_editor(source);
202+
for r in refs.iter().rev() {
203+
if let Some((old_node, new_node)) = r
204+
.name
205+
.syntax()
206+
.ancestors()
207+
.find_map(|node| Some((node.clone(), edit_node(node.clone())?)))
208+
{
209+
if let Some(old_node) = ctx.sema.original_syntax_node_rooted(&old_node) {
210+
editor.replace(old_node, new_node);
211+
} else {
212+
let FileRangeWrapper { file_id: _, range } = ctx.sema.original_range(&old_node);
213+
let parent = source.covering_element(range);
214+
match parent {
215+
SyntaxElement::Token(token) => {
216+
editor.replace(token, new_node.syntax_element());
217+
}
218+
SyntaxElement::Node(parent_node) => {
219+
// replace the part of macro
220+
// ```
221+
// foo!(a, Test::A(0));
222+
// ^^^^^^^^^^^^^^^ // parent_node
223+
// ^^^^^^^^^^ // replace_range
224+
// ```
225+
let start = parent_node
226+
.children_with_tokens()
227+
.find(|t| t.text_range().contains(range.start()));
228+
let end = parent_node
229+
.children_with_tokens()
230+
.find(|t| t.text_range().contains(range.end() - TextSize::new(1)));
231+
if let (Some(start), Some(end)) = (start, end) {
232+
let replace_range = RangeInclusive::new(start, end);
233+
editor.replace_all(replace_range, vec![new_node.into()]);
234+
}
235+
}
236+
}
210237
}
211238
}
212239
}
240+
edit.add_file_edits(file_id.file_id(ctx.db()), editor);
213241
}
214242
}
215243

@@ -227,12 +255,17 @@ fn edit_field_references(
227255
let def = Definition::Field(field);
228256
let usages = def.usages(&ctx.sema).all();
229257
for (file_id, refs) in usages {
230-
edit.edit_file(file_id.file_id(ctx.db()));
258+
let source = ctx.sema.parse(file_id);
259+
let source = source.syntax();
260+
let mut editor = edit.make_editor(source);
231261
for r in refs {
232-
if let Some(name_ref) = r.name.as_name_ref() {
233-
edit.replace(ctx.sema.original_range(name_ref.syntax()).range, name.text());
262+
if let Some(name_ref) = r.name.as_name_ref()
263+
&& let Some(original) = ctx.sema.original_ast_node(name_ref.clone())
264+
{
265+
editor.replace(original.syntax(), name.syntax());
234266
}
235267
}
268+
edit.add_file_edits(file_id.file_id(ctx.db()), editor);
236269
}
237270
}
238271
}
@@ -242,7 +275,7 @@ fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Nam
242275
.enumerate()
243276
.map(|(i, _)| {
244277
let idx = i + 1;
245-
ast::make::name(&format!("field{idx}"))
278+
ast::make::name(&format!("field{idx}")).clone_for_update()
246279
})
247280
.collect()
248281
}

0 commit comments

Comments
 (0)