Skip to content

Commit 0b9114f

Browse files
committed
Add generate_impl_trait for generate_impl
1 parent 1520876 commit 0b9114f

File tree

3 files changed

+294
-2
lines changed

3 files changed

+294
-2
lines changed

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

Lines changed: 270 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use syntax::{
2-
ast::{self, AstNode, HasName, edit_in_place::Indent, make},
2+
ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make},
33
syntax_editor::{Position, SyntaxEditor},
44
};
55

6-
use crate::{AssistContext, AssistId, Assists, utils};
6+
use crate::{
7+
AssistContext, AssistId, Assists,
8+
utils::{self, DefaultMethods, IgnoreAssocItems},
9+
};
710

811
fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
912
let indent = nominal.indent_level();
13+
14+
impl_.indent(indent);
1015
editor.insert_all(
1116
Position::after(nominal.syntax()),
1217
vec![
@@ -120,6 +125,101 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
120125
)
121126
}
122127

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+
123223
#[cfg(test)]
124224
mod tests {
125225
use crate::tests::{check_assist, check_assist_target};
@@ -492,4 +592,172 @@ mod tests {
492592
"#,
493593
);
494594
}
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+
}
495763
}

crates/ide-assists/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ mod handlers {
301301
generate_function::generate_function,
302302
generate_impl::generate_impl,
303303
generate_impl::generate_trait_impl,
304+
generate_impl::generate_impl_trait,
304305
generate_is_empty_from_len::generate_is_empty_from_len,
305306
generate_mut_trait_impl::generate_mut_trait_impl,
306307
generate_new::generate_new,

crates/ide-assists/src/tests/generated.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,29 @@ impl<T: Clone> Ctx<T> {$0}
18801880
)
18811881
}
18821882

1883+
#[test]
1884+
fn doctest_generate_impl_trait() {
1885+
check_doc_test(
1886+
"generate_impl_trait",
1887+
r#####"
1888+
trait $0Foo {
1889+
fn foo(&self) -> i32;
1890+
}
1891+
"#####,
1892+
r#####"
1893+
trait Foo {
1894+
fn foo(&self) -> i32;
1895+
}
1896+
1897+
impl Foo for ${1:_} {
1898+
$0fn foo(&self) -> i32 {
1899+
todo!()
1900+
}
1901+
}
1902+
"#####,
1903+
)
1904+
}
1905+
18831906
#[test]
18841907
fn doctest_generate_is_empty_from_len() {
18851908
check_doc_test(

0 commit comments

Comments
 (0)