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