Skip to content

Migrate replace derive with manual impl and add_missing_impl_members to use SyntaxEditor #20293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 84 additions & 23 deletions crates/ide-assists/src/handlers/add_missing_impl_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use hir::HasSource;
use syntax::{
Edition,
ast::{self, AstNode, make},
syntax_editor::{Position, SyntaxEditor},
};

use crate::{
Expand Down Expand Up @@ -147,45 +148,78 @@ fn add_missing_impl_members_inner(

let target = impl_def.syntax().text_range();
acc.add(AssistId::quick_fix(assist_id), label, target, |edit| {
let new_impl_def = edit.make_mut(impl_def.clone());
let first_new_item = add_trait_assoc_items_to_impl(
let new_item = add_trait_assoc_items_to_impl(
&ctx.sema,
ctx.config,
&missing_items,
trait_,
&new_impl_def,
&impl_def,
&target_scope,
);

let Some((first_new_item, other_items)) = new_item.split_first() else {
return;
};

let mut first_new_item = if let DefaultMethods::No = mode
&& let ast::AssocItem::Fn(func) = &first_new_item
&& let Some(body) = try_gen_trait_body(
ctx,
func,
trait_ref,
&impl_def,
target_scope.krate().edition(ctx.sema.db),
)
&& let Some(func_body) = func.body()
{
let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree());
func_editor.replace(func_body.syntax(), body.syntax());
ast::AssocItem::cast(func_editor.finish().new_root().clone())
} else {
Some(first_new_item.clone())
};

let new_assoc_items = first_new_item
.clone()
.into_iter()
.chain(other_items.iter().cloned())
.map(either::Either::Right)
.collect::<Vec<_>>();

let mut editor = edit.make_editor(impl_def.syntax());
if let Some(assoc_item_list) = impl_def.assoc_item_list() {
let items = new_assoc_items.into_iter().filter_map(either::Either::right).collect();
assoc_item_list.add_items(&mut editor, items);
} else {
let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update();
editor.insert_all(
Position::after(impl_def.syntax()),
vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()],
);
first_new_item = assoc_item_list.assoc_items().next();
}

if let Some(cap) = ctx.config.snippet_cap {
let mut placeholder = None;
if let DefaultMethods::No = mode {
if let ast::AssocItem::Fn(func) = &first_new_item {
if try_gen_trait_body(
ctx,
func,
trait_ref,
&impl_def,
target_scope.krate().edition(ctx.sema.db),
)
.is_none()
if let Some(ast::AssocItem::Fn(func)) = &first_new_item {
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
&& m.syntax().text() == "todo!()"
{
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
{
if m.syntax().text() == "todo!()" {
placeholder = Some(m);
}
}
placeholder = Some(m);
}
}
}

if let Some(macro_call) = placeholder {
edit.add_placeholder_snippet(cap, macro_call);
} else {
edit.add_tabstop_before(cap, first_new_item);
let placeholder = edit.make_placeholder_snippet(cap);
editor.add_annotation(macro_call.syntax(), placeholder);
} else if let Some(first_new_item) = first_new_item {
let tabstop = edit.make_tabstop_before(cap);
editor.add_annotation(first_new_item.syntax(), tabstop);
};
};
edit.add_file_edits(ctx.vfs_file_id(), editor);
})
}

Expand All @@ -195,7 +229,7 @@ fn try_gen_trait_body(
trait_ref: hir::TraitRef<'_>,
impl_def: &ast::Impl,
edition: Edition,
) -> Option<()> {
) -> Option<ast::BlockExpr> {
let trait_path = make::ext::ident_path(
&trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(),
);
Expand Down Expand Up @@ -322,7 +356,7 @@ impl Foo for S {
}

#[test]
fn test_impl_def_without_braces() {
fn test_impl_def_without_braces_macro() {
check_assist(
add_missing_impl_members,
r#"
Expand All @@ -340,6 +374,33 @@ impl Foo for S {
);
}

#[test]
fn test_impl_def_without_braces_tabstop_first_item() {
check_assist(
add_missing_impl_members,
r#"
trait Foo {
type Output;
fn foo(&self);
}
struct S;
impl Foo for S { $0 }"#,
r#"
trait Foo {
type Output;
fn foo(&self);
}
struct S;
impl Foo for S {
$0type Output;

fn foo(&self) {
todo!()
}
}"#,
);
}

#[test]
fn fill_in_type_params_1() {
check_assist(
Expand Down
51 changes: 31 additions & 20 deletions crates/ide-assists/src/handlers/generate_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,33 +167,44 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
DefaultMethods::No,
IgnoreAssocItems::DocHiddenAttrPresent,
);
let impl_ = make::impl_trait(
trait_.unsafe_token().is_some(),
None,
trait_.generic_param_list().map(|list| {
make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
}),
None,
None,
false,
make::ty(&name.text()),
make::ty_placeholder(),
None,
None,
None,
)
.clone_for_update();

if !missing_items.is_empty() {
utils::add_trait_assoc_items_to_impl(

let trait_gen_args = trait_.generic_param_list().map(|list| {
make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
});

let make_impl_ = |body| {
make::impl_trait(
trait_.unsafe_token().is_some(),
None,
trait_gen_args.clone(),
None,
None,
false,
make::ty(&name.text()),
make::ty_placeholder(),
None,
None,
body,
)
.clone_for_update()
};

let impl_ = if missing_items.is_empty() {
make_impl_(None)
} else {
let impl_ = make_impl_(None);
let assoc_items = utils::add_trait_assoc_items_to_impl(
&ctx.sema,
ctx.config,
&missing_items,
hir_trait,
&impl_,
&target_scope,
);
}
let assoc_items = assoc_items.into_iter().map(either::Either::Right).collect();
let assoc_item_list = make::assoc_item_list(Some(assoc_items));
make_impl_(Some(assoc_item_list))
};

if let Some(cap) = ctx.config.snippet_cap {
if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
Expand Down
Loading
Loading