Skip to content

Commit ecb15ca

Browse files
author
Mihail Mihov
committed
Add assist to generate trait impl's
1 parent 2656297 commit ecb15ca

File tree

3 files changed

+249
-0
lines changed

3 files changed

+249
-0
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
use syntax::ast::{self, AstNode, HasName};
2+
3+
use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
4+
5+
// Assist: generate_trait_impl
6+
//
7+
// Adds a new trait impl for a type.
8+
//
9+
// ```
10+
// struct $0Ctx<T: Clone> {
11+
// data: T,
12+
// }
13+
// ```
14+
// ->
15+
// ```
16+
// struct Ctx<T: Clone> {
17+
// data: T,
18+
// }
19+
//
20+
// impl<T: Clone> $0 for Ctx<T> {
21+
//
22+
// }
23+
// ```
24+
pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
25+
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
26+
let name = nominal.name()?;
27+
let target = nominal.syntax().text_range();
28+
29+
if let Some(_) = ctx.find_node_at_offset::<ast::RecordFieldList>() {
30+
return None;
31+
}
32+
33+
acc.add(
34+
AssistId("generate_trait_impl", AssistKind::Generate),
35+
format!("Generate trait impl for `{name}`"),
36+
target,
37+
|edit| {
38+
let start_offset = nominal.syntax().text_range().end();
39+
match ctx.config.snippet_cap {
40+
Some(cap) => {
41+
let snippet = generate_trait_impl_text(&nominal, "$0", "");
42+
edit.insert_snippet(cap, start_offset, snippet);
43+
}
44+
None => {
45+
let text = generate_trait_impl_text(&nominal, "", "");
46+
edit.insert(start_offset, text);
47+
}
48+
}
49+
},
50+
)
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use crate::tests::{check_assist, check_assist_target};
56+
57+
use super::*;
58+
59+
#[test]
60+
fn test_add_trait_impl() {
61+
check_assist(
62+
generate_trait_impl,
63+
r#"
64+
struct Foo$0 {}
65+
"#,
66+
r#"
67+
struct Foo {}
68+
69+
impl $0 for Foo {
70+
71+
}
72+
"#,
73+
);
74+
}
75+
76+
#[test]
77+
fn test_add_trait_impl_with_generics() {
78+
check_assist(
79+
generate_trait_impl,
80+
r#"
81+
struct Foo$0<T: Clone> {}
82+
"#,
83+
r#"
84+
struct Foo<T: Clone> {}
85+
86+
impl<T: Clone> $0 for Foo<T> {
87+
88+
}
89+
"#,
90+
);
91+
}
92+
93+
#[test]
94+
fn test_add_trait_impl_with_generics_and_lifetime_parameters() {
95+
check_assist(
96+
generate_trait_impl,
97+
r#"
98+
struct Foo<'a, T: Foo<'a>>$0 {}
99+
"#,
100+
r#"
101+
struct Foo<'a, T: Foo<'a>> {}
102+
103+
impl<'a, T: Foo<'a>> $0 for Foo<'a, T> {
104+
105+
}
106+
"#,
107+
);
108+
}
109+
110+
#[test]
111+
fn test_add_trait_impl_with_attributes() {
112+
check_assist(
113+
generate_trait_impl,
114+
r#"
115+
#[cfg(feature = "foo")]
116+
struct Foo<'a, T: Foo$0<'a>> {}
117+
"#,
118+
r#"
119+
#[cfg(feature = "foo")]
120+
struct Foo<'a, T: Foo<'a>> {}
121+
122+
#[cfg(feature = "foo")]
123+
impl<'a, T: Foo<'a>> $0 for Foo<'a, T> {
124+
125+
}
126+
"#,
127+
);
128+
}
129+
130+
#[test]
131+
fn test_add_trait_impl_with_default_generic() {
132+
check_assist(
133+
generate_trait_impl,
134+
r#"
135+
struct Defaulted$0<T = i32> {}
136+
"#,
137+
r#"
138+
struct Defaulted<T = i32> {}
139+
140+
impl<T> $0 for Defaulted<T> {
141+
142+
}
143+
"#,
144+
);
145+
}
146+
147+
#[test]
148+
fn test_add_trait_impl_with_constrained_default_generic() {
149+
check_assist(
150+
generate_trait_impl,
151+
r#"
152+
struct Defaulted$0<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
153+
"#,
154+
r#"
155+
struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
156+
157+
impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> $0 for Defaulted<'a, 'b, T, S> {
158+
159+
}
160+
"#,
161+
);
162+
}
163+
164+
#[test]
165+
fn test_add_trait_impl_with_const_defaulted_generic() {
166+
check_assist(
167+
generate_trait_impl,
168+
r#"
169+
struct Defaulted$0<const N: i32 = 0> {}
170+
"#,
171+
r#"
172+
struct Defaulted<const N: i32 = 0> {}
173+
174+
impl<const N: i32> $0 for Defaulted<N> {
175+
176+
}
177+
"#,
178+
);
179+
}
180+
181+
#[test]
182+
fn test_add_trait_impl_with_trait_constraint() {
183+
check_assist(
184+
generate_trait_impl,
185+
r#"
186+
pub trait Trait {}
187+
struct Struct$0<T>
188+
where
189+
T: Trait,
190+
{
191+
inner: T,
192+
}
193+
"#,
194+
r#"
195+
pub trait Trait {}
196+
struct Struct<T>
197+
where
198+
T: Trait,
199+
{
200+
inner: T,
201+
}
202+
203+
impl<T> $0 for Struct<T>
204+
where
205+
T: Trait,
206+
{
207+
208+
}
209+
"#,
210+
);
211+
}
212+
213+
#[test]
214+
fn add_trait_impl_target() {
215+
check_assist_target(
216+
generate_trait_impl,
217+
r#"
218+
struct SomeThingIrrelevant;
219+
/// Has a lifetime parameter
220+
struct Foo$0<'a, T: Foo<'a>> {}
221+
struct EvenMoreIrrelevant;
222+
"#,
223+
"/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}",
224+
);
225+
}
226+
}

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ mod handlers {
152152
mod generate_function;
153153
mod generate_getter;
154154
mod generate_impl;
155+
mod generate_trait_impl;
155156
mod generate_is_empty_from_len;
156157
mod generate_new;
157158
mod generate_setter;
@@ -247,6 +248,7 @@ mod handlers {
247248
generate_from_impl_for_enum::generate_from_impl_for_enum,
248249
generate_function::generate_function,
249250
generate_impl::generate_impl,
251+
generate_trait_impl::generate_trait_impl,
250252
generate_is_empty_from_len::generate_is_empty_from_len,
251253
generate_new::generate_new,
252254
inline_call::inline_call,

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,27 @@ impl Person {
13411341
)
13421342
}
13431343

1344+
#[test]
1345+
fn doctest_generate_trait_impl() {
1346+
check_doc_test(
1347+
"generate_trait_impl",
1348+
r#####"
1349+
struct $0Ctx<T: Clone> {
1350+
data: T,
1351+
}
1352+
"#####,
1353+
r#####"
1354+
struct Ctx<T: Clone> {
1355+
data: T,
1356+
}
1357+
1358+
impl<T: Clone> $0 for Ctx<T> {
1359+
1360+
}
1361+
"#####,
1362+
)
1363+
}
1364+
13441365
#[test]
13451366
fn doctest_inline_call() {
13461367
check_doc_test(

0 commit comments

Comments
 (0)