|
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 |
| -fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) { |
| 11 | +fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) { |
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,115 @@ 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 | +// fn foo(&self) -> i32 { |
| 145 | +// $0todo!() |
| 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 mut editor = edit.make_editor(trait_.syntax()); |
| 162 | + |
| 163 | + let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder())); |
| 164 | + let missing_items = utils::filter_assoc_items( |
| 165 | + &ctx.sema, |
| 166 | + &hir_trait.items(ctx.db()), |
| 167 | + DefaultMethods::No, |
| 168 | + IgnoreAssocItems::DocHiddenAttrPresent, |
| 169 | + ); |
| 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( |
| 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 | + let placeholder = edit.make_placeholder_snippet(cap); |
| 202 | + editor.add_annotation(generic.syntax(), placeholder); |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + if let Some(ty) = impl_.self_ty() { |
| 207 | + let placeholder = edit.make_placeholder_snippet(cap); |
| 208 | + editor.add_annotation(ty.syntax(), placeholder); |
| 209 | + } |
| 210 | + |
| 211 | + if let Some(expr) = |
| 212 | + impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr)) |
| 213 | + { |
| 214 | + let tabstop = edit.make_tabstop_before(cap); |
| 215 | + editor.add_annotation(expr.syntax(), tabstop); |
| 216 | + } else if let Some(l_curly) = |
| 217 | + impl_.assoc_item_list().and_then(|it| it.l_curly_token()) |
| 218 | + { |
| 219 | + let tabstop = edit.make_tabstop_after(cap); |
| 220 | + editor.add_annotation(l_curly, tabstop); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + insert_impl(&mut editor, &impl_, &trait_); |
| 225 | + edit.add_file_edits(ctx.vfs_file_id(), editor); |
| 226 | + }, |
| 227 | + ) |
| 228 | +} |
| 229 | + |
| 230 | +fn extract_expr(item: ast::AssocItem) -> Option<ast::Expr> { |
| 231 | + let ast::AssocItem::Fn(f) = item else { |
| 232 | + return None; |
| 233 | + }; |
| 234 | + f.body()?.tail_expr() |
| 235 | +} |
| 236 | + |
123 | 237 | #[cfg(test)]
|
124 | 238 | mod tests {
|
125 | 239 | use crate::tests::{check_assist, check_assist_target};
|
@@ -492,4 +606,209 @@ mod tests {
|
492 | 606 | "#,
|
493 | 607 | );
|
494 | 608 | }
|
| 609 | + |
| 610 | + #[test] |
| 611 | + fn test_add_impl_trait() { |
| 612 | + check_assist( |
| 613 | + generate_impl_trait, |
| 614 | + r#" |
| 615 | + trait $0Foo { |
| 616 | + fn foo(&self) -> i32; |
| 617 | +
|
| 618 | + fn bar(&self) -> i32 { |
| 619 | + self.foo() |
| 620 | + } |
| 621 | + } |
| 622 | + "#, |
| 623 | + r#" |
| 624 | + trait Foo { |
| 625 | + fn foo(&self) -> i32; |
| 626 | +
|
| 627 | + fn bar(&self) -> i32 { |
| 628 | + self.foo() |
| 629 | + } |
| 630 | + } |
| 631 | +
|
| 632 | + impl Foo for ${1:_} { |
| 633 | + fn foo(&self) -> i32 { |
| 634 | + $0todo!() |
| 635 | + } |
| 636 | + } |
| 637 | + "#, |
| 638 | + ); |
| 639 | + } |
| 640 | + |
| 641 | + #[test] |
| 642 | + fn test_add_impl_trait_use_generic() { |
| 643 | + check_assist( |
| 644 | + generate_impl_trait, |
| 645 | + r#" |
| 646 | + trait $0Foo<T> { |
| 647 | + fn foo(&self) -> T; |
| 648 | +
|
| 649 | + fn bar(&self) -> T { |
| 650 | + self.foo() |
| 651 | + } |
| 652 | + } |
| 653 | + "#, |
| 654 | + r#" |
| 655 | + trait Foo<T> { |
| 656 | + fn foo(&self) -> T; |
| 657 | +
|
| 658 | + fn bar(&self) -> T { |
| 659 | + self.foo() |
| 660 | + } |
| 661 | + } |
| 662 | +
|
| 663 | + impl Foo<${1:_}> for ${2:_} { |
| 664 | + fn foo(&self) -> _ { |
| 665 | + $0todo!() |
| 666 | + } |
| 667 | + } |
| 668 | + "#, |
| 669 | + ); |
| 670 | + check_assist( |
| 671 | + generate_impl_trait, |
| 672 | + r#" |
| 673 | + trait $0Foo<T, U> { |
| 674 | + fn foo(&self) -> T; |
| 675 | +
|
| 676 | + fn bar(&self) -> T { |
| 677 | + self.foo() |
| 678 | + } |
| 679 | + } |
| 680 | + "#, |
| 681 | + r#" |
| 682 | + trait Foo<T, U> { |
| 683 | + fn foo(&self) -> T; |
| 684 | +
|
| 685 | + fn bar(&self) -> T { |
| 686 | + self.foo() |
| 687 | + } |
| 688 | + } |
| 689 | +
|
| 690 | + impl Foo<${1:_}, ${2:_}> for ${3:_} { |
| 691 | + fn foo(&self) -> _ { |
| 692 | + $0todo!() |
| 693 | + } |
| 694 | + } |
| 695 | + "#, |
| 696 | + ); |
| 697 | + } |
| 698 | + |
| 699 | + #[test] |
| 700 | + fn test_add_impl_trait_docs() { |
| 701 | + check_assist( |
| 702 | + generate_impl_trait, |
| 703 | + r#" |
| 704 | + /// foo |
| 705 | + trait $0Foo { |
| 706 | + /// foo method |
| 707 | + fn foo(&self) -> i32; |
| 708 | +
|
| 709 | + fn bar(&self) -> i32 { |
| 710 | + self.foo() |
| 711 | + } |
| 712 | + } |
| 713 | + "#, |
| 714 | + r#" |
| 715 | + /// foo |
| 716 | + trait Foo { |
| 717 | + /// foo method |
| 718 | + fn foo(&self) -> i32; |
| 719 | +
|
| 720 | + fn bar(&self) -> i32 { |
| 721 | + self.foo() |
| 722 | + } |
| 723 | + } |
| 724 | +
|
| 725 | + impl Foo for ${1:_} { |
| 726 | + fn foo(&self) -> i32 { |
| 727 | + $0todo!() |
| 728 | + } |
| 729 | + } |
| 730 | + "#, |
| 731 | + ); |
| 732 | + } |
| 733 | + |
| 734 | + #[test] |
| 735 | + fn test_add_impl_trait_assoc_types() { |
| 736 | + check_assist( |
| 737 | + generate_impl_trait, |
| 738 | + r#" |
| 739 | + trait $0Foo { |
| 740 | + type Output; |
| 741 | +
|
| 742 | + fn foo(&self) -> Self::Output; |
| 743 | + } |
| 744 | + "#, |
| 745 | + r#" |
| 746 | + trait Foo { |
| 747 | + type Output; |
| 748 | +
|
| 749 | + fn foo(&self) -> Self::Output; |
| 750 | + } |
| 751 | +
|
| 752 | + impl Foo for ${1:_} { |
| 753 | + type Output; |
| 754 | +
|
| 755 | + fn foo(&self) -> Self::Output { |
| 756 | + $0todo!() |
| 757 | + } |
| 758 | + } |
| 759 | + "#, |
| 760 | + ); |
| 761 | + } |
| 762 | + |
| 763 | + #[test] |
| 764 | + fn test_add_impl_trait_indent() { |
| 765 | + check_assist( |
| 766 | + generate_impl_trait, |
| 767 | + r#" |
| 768 | + mod foo { |
| 769 | + mod bar { |
| 770 | + trait $0Foo { |
| 771 | + type Output; |
| 772 | +
|
| 773 | + fn foo(&self) -> Self::Output; |
| 774 | + } |
| 775 | + } |
| 776 | + } |
| 777 | + "#, |
| 778 | + r#" |
| 779 | + mod foo { |
| 780 | + mod bar { |
| 781 | + trait Foo { |
| 782 | + type Output; |
| 783 | +
|
| 784 | + fn foo(&self) -> Self::Output; |
| 785 | + } |
| 786 | +
|
| 787 | + impl Foo for ${1:_} { |
| 788 | + type Output; |
| 789 | +
|
| 790 | + fn foo(&self) -> Self::Output { |
| 791 | + $0todo!() |
| 792 | + } |
| 793 | + } |
| 794 | + } |
| 795 | + } |
| 796 | + "#, |
| 797 | + ); |
| 798 | + } |
| 799 | + |
| 800 | + #[test] |
| 801 | + fn test_add_impl_trait_empty() { |
| 802 | + check_assist( |
| 803 | + generate_impl_trait, |
| 804 | + r#" |
| 805 | + trait $0Foo {} |
| 806 | + "#, |
| 807 | + r#" |
| 808 | + trait Foo {} |
| 809 | +
|
| 810 | + impl Foo for ${1:_} {$0} |
| 811 | + "#, |
| 812 | + ); |
| 813 | + } |
495 | 814 | }
|
0 commit comments