|
1 | 1 | use syntax::{
|
2 |
| - ast::{self, AstNode, HasName, edit_in_place::Indent, make}, |
| 2 | + ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make}, |
3 | 3 | syntax_editor::{Position, SyntaxEditor},
|
4 | 4 | };
|
5 | 5 |
|
6 |
| -use crate::{AssistContext, AssistId, Assists, utils}; |
| 6 | +use crate::{ |
| 7 | + AssistContext, AssistId, Assists, |
| 8 | + utils::{self, DefaultMethods, IgnoreAssocItems}, |
| 9 | +}; |
7 | 10 |
|
8 | 11 | fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
|
9 | 12 | let indent = nominal.indent_level();
|
| 13 | + |
| 14 | + impl_.indent(indent); |
10 | 15 | editor.insert_all(
|
11 | 16 | Position::after(nominal.syntax()),
|
12 | 17 | vec![
|
@@ -120,6 +125,101 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
120 | 125 | )
|
121 | 126 | }
|
122 | 127 |
|
| 128 | +// Assist: generate_impl_trait |
| 129 | +// |
| 130 | +// Adds this trait impl for a type. |
| 131 | +// |
| 132 | +// ``` |
| 133 | +// trait $0Foo { |
| 134 | +// fn foo(&self) -> i32; |
| 135 | +// } |
| 136 | +// ``` |
| 137 | +// -> |
| 138 | +// ``` |
| 139 | +// trait Foo { |
| 140 | +// fn foo(&self) -> i32; |
| 141 | +// } |
| 142 | +// |
| 143 | +// impl Foo for ${1:_} { |
| 144 | +// $0fn foo(&self) -> i32 { |
| 145 | +// todo!() |
| 146 | +// } |
| 147 | +// } |
| 148 | +// ``` |
| 149 | +pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { |
| 150 | + let name = ctx.find_node_at_offset::<ast::Name>()?; |
| 151 | + let trait_ = ast::Trait::cast(name.syntax().parent()?)?; |
| 152 | + let target_scope = ctx.sema.scope(trait_.syntax())?; |
| 153 | + let hir_trait = ctx.sema.to_def(&trait_)?; |
| 154 | + |
| 155 | + let target = trait_.syntax().text_range(); |
| 156 | + acc.add( |
| 157 | + AssistId::generate("generate_impl_trait"), |
| 158 | + format!("Generate `{name}` impl for type"), |
| 159 | + target, |
| 160 | + |edit| { |
| 161 | + let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder())); |
| 162 | + let missing_items = utils::filter_assoc_items( |
| 163 | + &ctx.sema, |
| 164 | + &hir_trait.items(ctx.db()), |
| 165 | + DefaultMethods::No, |
| 166 | + IgnoreAssocItems::DocHiddenAttrPresent, |
| 167 | + ); |
| 168 | + let impl_ = make::impl_trait( |
| 169 | + trait_.unsafe_token().is_some(), |
| 170 | + None, |
| 171 | + trait_.generic_param_list().map(|list| { |
| 172 | + make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone())) |
| 173 | + }), |
| 174 | + None, |
| 175 | + None, |
| 176 | + false, |
| 177 | + make::ty(&name.text()), |
| 178 | + make::ty_placeholder(), |
| 179 | + None, |
| 180 | + None, |
| 181 | + None, |
| 182 | + ) |
| 183 | + .clone_for_update(); |
| 184 | + |
| 185 | + let trait_ = edit.make_mut(trait_); |
| 186 | + |
| 187 | + if !missing_items.is_empty() { |
| 188 | + utils::add_trait_assoc_items_to_impl( |
| 189 | + &ctx.sema, |
| 190 | + ctx.config, |
| 191 | + &missing_items, |
| 192 | + hir_trait, |
| 193 | + &impl_, |
| 194 | + &target_scope, |
| 195 | + ); |
| 196 | + } |
| 197 | + |
| 198 | + if let Some(cap) = ctx.config.snippet_cap { |
| 199 | + if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { |
| 200 | + for generic in generics.generic_args() { |
| 201 | + edit.add_placeholder_snippet(cap, generic); |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + if let Some(ty) = impl_.self_ty() { |
| 206 | + edit.add_placeholder_snippet(cap, ty); |
| 207 | + } |
| 208 | + |
| 209 | + if let Some(item) = impl_.assoc_item_list().and_then(|it| it.assoc_items().next()) { |
| 210 | + edit.add_tabstop_before(cap, item); |
| 211 | + } else if let Some(l_curly) = |
| 212 | + impl_.assoc_item_list().and_then(|it| it.l_curly_token()) |
| 213 | + { |
| 214 | + edit.add_tabstop_after_token(cap, l_curly); |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + insert_impl(impl_, &trait_); |
| 219 | + }, |
| 220 | + ) |
| 221 | +} |
| 222 | + |
123 | 223 | #[cfg(test)]
|
124 | 224 | mod tests {
|
125 | 225 | use crate::tests::{check_assist, check_assist_target};
|
@@ -492,4 +592,172 @@ mod tests {
|
492 | 592 | "#,
|
493 | 593 | );
|
494 | 594 | }
|
| 595 | + |
| 596 | + #[test] |
| 597 | + fn test_add_impl_trait() { |
| 598 | + check_assist( |
| 599 | + generate_impl_trait, |
| 600 | + r#" |
| 601 | + trait $0Foo { |
| 602 | + fn foo(&self) -> i32; |
| 603 | +
|
| 604 | + fn bar(&self) -> i32 { |
| 605 | + self.foo() |
| 606 | + } |
| 607 | + } |
| 608 | + "#, |
| 609 | + r#" |
| 610 | + trait Foo { |
| 611 | + fn foo(&self) -> i32; |
| 612 | +
|
| 613 | + fn bar(&self) -> i32 { |
| 614 | + self.foo() |
| 615 | + } |
| 616 | + } |
| 617 | +
|
| 618 | + impl Foo for ${1:_} { |
| 619 | + $0fn foo(&self) -> i32 { |
| 620 | + todo!() |
| 621 | + } |
| 622 | + } |
| 623 | + "#, |
| 624 | + ); |
| 625 | + } |
| 626 | + |
| 627 | + #[test] |
| 628 | + fn test_add_impl_trait_use_generic() { |
| 629 | + check_assist( |
| 630 | + generate_impl_trait, |
| 631 | + r#" |
| 632 | + trait $0Foo<T> { |
| 633 | + fn foo(&self) -> T; |
| 634 | +
|
| 635 | + fn bar(&self) -> T { |
| 636 | + self.foo() |
| 637 | + } |
| 638 | + } |
| 639 | + "#, |
| 640 | + r#" |
| 641 | + trait Foo<T> { |
| 642 | + fn foo(&self) -> T; |
| 643 | +
|
| 644 | + fn bar(&self) -> T { |
| 645 | + self.foo() |
| 646 | + } |
| 647 | + } |
| 648 | +
|
| 649 | + impl Foo<${1:_}> for ${2:_} { |
| 650 | + $0fn foo(&self) -> _ { |
| 651 | + todo!() |
| 652 | + } |
| 653 | + } |
| 654 | + "#, |
| 655 | + ); |
| 656 | + check_assist( |
| 657 | + generate_impl_trait, |
| 658 | + r#" |
| 659 | + trait $0Foo<T, U> { |
| 660 | + fn foo(&self) -> T; |
| 661 | +
|
| 662 | + fn bar(&self) -> T { |
| 663 | + self.foo() |
| 664 | + } |
| 665 | + } |
| 666 | + "#, |
| 667 | + r#" |
| 668 | + trait Foo<T, U> { |
| 669 | + fn foo(&self) -> T; |
| 670 | +
|
| 671 | + fn bar(&self) -> T { |
| 672 | + self.foo() |
| 673 | + } |
| 674 | + } |
| 675 | +
|
| 676 | + impl Foo<${1:_}, ${2:_}> for ${3:_} { |
| 677 | + $0fn foo(&self) -> _ { |
| 678 | + todo!() |
| 679 | + } |
| 680 | + } |
| 681 | + "#, |
| 682 | + ); |
| 683 | + } |
| 684 | + |
| 685 | + #[test] |
| 686 | + fn test_add_impl_trait_docs() { |
| 687 | + check_assist( |
| 688 | + generate_impl_trait, |
| 689 | + r#" |
| 690 | + /// foo |
| 691 | + trait $0Foo { |
| 692 | + /// foo method |
| 693 | + fn foo(&self) -> i32; |
| 694 | +
|
| 695 | + fn bar(&self) -> i32 { |
| 696 | + self.foo() |
| 697 | + } |
| 698 | + } |
| 699 | + "#, |
| 700 | + r#" |
| 701 | + /// foo |
| 702 | + trait Foo { |
| 703 | + /// foo method |
| 704 | + fn foo(&self) -> i32; |
| 705 | +
|
| 706 | + fn bar(&self) -> i32 { |
| 707 | + self.foo() |
| 708 | + } |
| 709 | + } |
| 710 | +
|
| 711 | + impl Foo for ${1:_} { |
| 712 | + $0fn foo(&self) -> i32 { |
| 713 | + todo!() |
| 714 | + } |
| 715 | + } |
| 716 | + "#, |
| 717 | + ); |
| 718 | + } |
| 719 | + |
| 720 | + #[test] |
| 721 | + fn test_add_impl_trait_assoc_types() { |
| 722 | + check_assist( |
| 723 | + generate_impl_trait, |
| 724 | + r#" |
| 725 | + trait $0Foo { |
| 726 | + type Output; |
| 727 | +
|
| 728 | + fn foo(&self) -> Self::Output; |
| 729 | + } |
| 730 | + "#, |
| 731 | + r#" |
| 732 | + trait Foo { |
| 733 | + type Output; |
| 734 | +
|
| 735 | + fn foo(&self) -> Self::Output; |
| 736 | + } |
| 737 | +
|
| 738 | + impl Foo for ${1:_} { |
| 739 | + $0type Output; |
| 740 | +
|
| 741 | + fn foo(&self) -> Self::Output { |
| 742 | + todo!() |
| 743 | + } |
| 744 | + } |
| 745 | + "#, |
| 746 | + ); |
| 747 | + } |
| 748 | + |
| 749 | + #[test] |
| 750 | + fn test_add_impl_trait_empty() { |
| 751 | + check_assist( |
| 752 | + generate_impl_trait, |
| 753 | + r#" |
| 754 | + trait $0Foo {} |
| 755 | + "#, |
| 756 | + r#" |
| 757 | + trait Foo {} |
| 758 | +
|
| 759 | + impl Foo for ${1:_} {$0} |
| 760 | + "#, |
| 761 | + ); |
| 762 | + } |
495 | 763 | }
|
0 commit comments