Skip to content

Commit 51e77c9

Browse files
authored
Merge pull request #20293 from Hmikihiro/migrate_replace_derive_with_manual_impl
Migrate `replace derive with manual impl` and `add_missing_impl_members` to use `SyntaxEditor`
2 parents 48ccbe0 + 82dfdac commit 51e77c9

File tree

7 files changed

+319
-238
lines changed

7 files changed

+319
-238
lines changed

crates/ide-assists/src/handlers/add_missing_impl_members.rs

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use hir::HasSource;
22
use syntax::{
33
Edition,
44
ast::{self, AstNode, make},
5+
syntax_editor::{Position, SyntaxEditor},
56
};
67

78
use crate::{
@@ -147,45 +148,78 @@ fn add_missing_impl_members_inner(
147148

148149
let target = impl_def.syntax().text_range();
149150
acc.add(AssistId::quick_fix(assist_id), label, target, |edit| {
150-
let new_impl_def = edit.make_mut(impl_def.clone());
151-
let first_new_item = add_trait_assoc_items_to_impl(
151+
let new_item = add_trait_assoc_items_to_impl(
152152
&ctx.sema,
153153
ctx.config,
154154
&missing_items,
155155
trait_,
156-
&new_impl_def,
156+
&impl_def,
157157
&target_scope,
158158
);
159159

160+
let Some((first_new_item, other_items)) = new_item.split_first() else {
161+
return;
162+
};
163+
164+
let mut first_new_item = if let DefaultMethods::No = mode
165+
&& let ast::AssocItem::Fn(func) = &first_new_item
166+
&& let Some(body) = try_gen_trait_body(
167+
ctx,
168+
func,
169+
trait_ref,
170+
&impl_def,
171+
target_scope.krate().edition(ctx.sema.db),
172+
)
173+
&& let Some(func_body) = func.body()
174+
{
175+
let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree());
176+
func_editor.replace(func_body.syntax(), body.syntax());
177+
ast::AssocItem::cast(func_editor.finish().new_root().clone())
178+
} else {
179+
Some(first_new_item.clone())
180+
};
181+
182+
let new_assoc_items = first_new_item
183+
.clone()
184+
.into_iter()
185+
.chain(other_items.iter().cloned())
186+
.map(either::Either::Right)
187+
.collect::<Vec<_>>();
188+
189+
let mut editor = edit.make_editor(impl_def.syntax());
190+
if let Some(assoc_item_list) = impl_def.assoc_item_list() {
191+
let items = new_assoc_items.into_iter().filter_map(either::Either::right).collect();
192+
assoc_item_list.add_items(&mut editor, items);
193+
} else {
194+
let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update();
195+
editor.insert_all(
196+
Position::after(impl_def.syntax()),
197+
vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()],
198+
);
199+
first_new_item = assoc_item_list.assoc_items().next();
200+
}
201+
160202
if let Some(cap) = ctx.config.snippet_cap {
161203
let mut placeholder = None;
162204
if let DefaultMethods::No = mode {
163-
if let ast::AssocItem::Fn(func) = &first_new_item {
164-
if try_gen_trait_body(
165-
ctx,
166-
func,
167-
trait_ref,
168-
&impl_def,
169-
target_scope.krate().edition(ctx.sema.db),
170-
)
171-
.is_none()
205+
if let Some(ast::AssocItem::Fn(func)) = &first_new_item {
206+
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
207+
&& m.syntax().text() == "todo!()"
172208
{
173-
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
174-
{
175-
if m.syntax().text() == "todo!()" {
176-
placeholder = Some(m);
177-
}
178-
}
209+
placeholder = Some(m);
179210
}
180211
}
181212
}
182213

183214
if let Some(macro_call) = placeholder {
184-
edit.add_placeholder_snippet(cap, macro_call);
185-
} else {
186-
edit.add_tabstop_before(cap, first_new_item);
215+
let placeholder = edit.make_placeholder_snippet(cap);
216+
editor.add_annotation(macro_call.syntax(), placeholder);
217+
} else if let Some(first_new_item) = first_new_item {
218+
let tabstop = edit.make_tabstop_before(cap);
219+
editor.add_annotation(first_new_item.syntax(), tabstop);
187220
};
188221
};
222+
edit.add_file_edits(ctx.vfs_file_id(), editor);
189223
})
190224
}
191225

@@ -195,7 +229,7 @@ fn try_gen_trait_body(
195229
trait_ref: hir::TraitRef<'_>,
196230
impl_def: &ast::Impl,
197231
edition: Edition,
198-
) -> Option<()> {
232+
) -> Option<ast::BlockExpr> {
199233
let trait_path = make::ext::ident_path(
200234
&trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(),
201235
);
@@ -322,7 +356,7 @@ impl Foo for S {
322356
}
323357

324358
#[test]
325-
fn test_impl_def_without_braces() {
359+
fn test_impl_def_without_braces_macro() {
326360
check_assist(
327361
add_missing_impl_members,
328362
r#"
@@ -340,6 +374,33 @@ impl Foo for S {
340374
);
341375
}
342376

377+
#[test]
378+
fn test_impl_def_without_braces_tabstop_first_item() {
379+
check_assist(
380+
add_missing_impl_members,
381+
r#"
382+
trait Foo {
383+
type Output;
384+
fn foo(&self);
385+
}
386+
struct S;
387+
impl Foo for S { $0 }"#,
388+
r#"
389+
trait Foo {
390+
type Output;
391+
fn foo(&self);
392+
}
393+
struct S;
394+
impl Foo for S {
395+
$0type Output;
396+
397+
fn foo(&self) {
398+
todo!()
399+
}
400+
}"#,
401+
);
402+
}
403+
343404
#[test]
344405
fn fill_in_type_params_1() {
345406
check_assist(

crates/ide-assists/src/handlers/generate_impl.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -167,33 +167,44 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
167167
DefaultMethods::No,
168168
IgnoreAssocItems::DocHiddenAttrPresent,
169169
);
170-
let impl_ = make::impl_trait(
171-
trait_.unsafe_token().is_some(),
172-
None,
173-
trait_.generic_param_list().map(|list| {
174-
make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
175-
}),
176-
None,
177-
None,
178-
false,
179-
make::ty(&name.text()),
180-
make::ty_placeholder(),
181-
None,
182-
None,
183-
None,
184-
)
185-
.clone_for_update();
186-
187-
if !missing_items.is_empty() {
188-
utils::add_trait_assoc_items_to_impl(
170+
171+
let trait_gen_args = trait_.generic_param_list().map(|list| {
172+
make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
173+
});
174+
175+
let make_impl_ = |body| {
176+
make::impl_trait(
177+
trait_.unsafe_token().is_some(),
178+
None,
179+
trait_gen_args.clone(),
180+
None,
181+
None,
182+
false,
183+
make::ty(&name.text()),
184+
make::ty_placeholder(),
185+
None,
186+
None,
187+
body,
188+
)
189+
.clone_for_update()
190+
};
191+
192+
let impl_ = if missing_items.is_empty() {
193+
make_impl_(None)
194+
} else {
195+
let impl_ = make_impl_(None);
196+
let assoc_items = utils::add_trait_assoc_items_to_impl(
189197
&ctx.sema,
190198
ctx.config,
191199
&missing_items,
192200
hir_trait,
193201
&impl_,
194202
&target_scope,
195203
);
196-
}
204+
let assoc_items = assoc_items.into_iter().map(either::Either::Right).collect();
205+
let assoc_item_list = make::assoc_item_list(Some(assoc_items));
206+
make_impl_(Some(assoc_item_list))
207+
};
197208

198209
if let Some(cap) = ctx.config.snippet_cap {
199210
if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {

0 commit comments

Comments
 (0)