Skip to content

Commit 070f91e

Browse files
committed
Add generate_impl_trait for generate_impl
1 parent cf969d2 commit 070f91e

File tree

3 files changed

+295
-3
lines changed

3 files changed

+295
-3
lines changed

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

Lines changed: 271 additions & 3 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
ted,
44
};
55

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

8-
fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) {
11+
fn insert_impl(impl_: ast::Impl, nominal: &impl Indent) {
912
let indent = nominal.indent_level();
13+
14+
impl_.indent(indent);
1015
ted::insert_all_raw(
1116
ted::Position::after(nominal.syntax()),
1217
vec![
@@ -113,6 +118,101 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
113118
)
114119
}
115120

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+
116216
#[cfg(test)]
117217
mod tests {
118218
use crate::tests::{check_assist, check_assist_target};
@@ -485,4 +585,172 @@ mod tests {
485585
"#,
486586
);
487587
}
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+
}
488756
}

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)