diff options
Diffstat (limited to 'crates/assists/src/handlers/generate_impl.rs')
-rw-r--r-- | crates/assists/src/handlers/generate_impl.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs new file mode 100644 index 000000000..9989109b5 --- /dev/null +++ b/crates/assists/src/handlers/generate_impl.rs | |||
@@ -0,0 +1,110 @@ | |||
1 | use itertools::Itertools; | ||
2 | use stdx::format_to; | ||
3 | use syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner}; | ||
4 | |||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
6 | |||
7 | // Assist: generate_impl | ||
8 | // | ||
9 | // Adds a new inherent impl for a type. | ||
10 | // | ||
11 | // ``` | ||
12 | // struct Ctx<T: Clone> { | ||
13 | // data: T,<|> | ||
14 | // } | ||
15 | // ``` | ||
16 | // -> | ||
17 | // ``` | ||
18 | // struct Ctx<T: Clone> { | ||
19 | // data: T, | ||
20 | // } | ||
21 | // | ||
22 | // impl<T: Clone> Ctx<T> { | ||
23 | // $0 | ||
24 | // } | ||
25 | // ``` | ||
26 | pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
27 | let nominal = ctx.find_node_at_offset::<ast::AdtDef>()?; | ||
28 | let name = nominal.name()?; | ||
29 | let target = nominal.syntax().text_range(); | ||
30 | acc.add( | ||
31 | AssistId("generate_impl", AssistKind::Generate), | ||
32 | format!("Generate impl for `{}`", name), | ||
33 | target, | ||
34 | |edit| { | ||
35 | let type_params = nominal.generic_param_list(); | ||
36 | let start_offset = nominal.syntax().text_range().end(); | ||
37 | let mut buf = String::new(); | ||
38 | buf.push_str("\n\nimpl"); | ||
39 | if let Some(type_params) = &type_params { | ||
40 | format_to!(buf, "{}", type_params.syntax()); | ||
41 | } | ||
42 | buf.push_str(" "); | ||
43 | buf.push_str(name.text().as_str()); | ||
44 | if let Some(type_params) = type_params { | ||
45 | let lifetime_params = type_params | ||
46 | .lifetime_params() | ||
47 | .filter_map(|it| it.lifetime_token()) | ||
48 | .map(|it| it.text().clone()); | ||
49 | let type_params = type_params | ||
50 | .type_params() | ||
51 | .filter_map(|it| it.name()) | ||
52 | .map(|it| it.text().clone()); | ||
53 | |||
54 | let generic_params = lifetime_params.chain(type_params).format(", "); | ||
55 | format_to!(buf, "<{}>", generic_params) | ||
56 | } | ||
57 | match ctx.config.snippet_cap { | ||
58 | Some(cap) => { | ||
59 | buf.push_str(" {\n $0\n}"); | ||
60 | edit.insert_snippet(cap, start_offset, buf); | ||
61 | } | ||
62 | None => { | ||
63 | buf.push_str(" {\n}"); | ||
64 | edit.insert(start_offset, buf); | ||
65 | } | ||
66 | } | ||
67 | }, | ||
68 | ) | ||
69 | } | ||
70 | |||
71 | #[cfg(test)] | ||
72 | mod tests { | ||
73 | use crate::tests::{check_assist, check_assist_target}; | ||
74 | |||
75 | use super::*; | ||
76 | |||
77 | #[test] | ||
78 | fn test_add_impl() { | ||
79 | check_assist( | ||
80 | generate_impl, | ||
81 | "struct Foo {<|>}\n", | ||
82 | "struct Foo {}\n\nimpl Foo {\n $0\n}\n", | ||
83 | ); | ||
84 | check_assist( | ||
85 | generate_impl, | ||
86 | "struct Foo<T: Clone> {<|>}", | ||
87 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}", | ||
88 | ); | ||
89 | check_assist( | ||
90 | generate_impl, | ||
91 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
92 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", | ||
93 | ); | ||
94 | } | ||
95 | |||
96 | #[test] | ||
97 | fn add_impl_target() { | ||
98 | check_assist_target( | ||
99 | generate_impl, | ||
100 | " | ||
101 | struct SomeThingIrrelevant; | ||
102 | /// Has a lifetime parameter | ||
103 | struct Foo<'a, T: Foo<'a>> {<|>} | ||
104 | struct EvenMoreIrrelevant; | ||
105 | ", | ||
106 | "/// Has a lifetime parameter | ||
107 | struct Foo<'a, T: Foo<'a>> {}", | ||
108 | ); | ||
109 | } | ||
110 | } | ||