]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/handlers/add_impl.rs
Centralize fixture parsing for assists
[rust.git] / crates / ra_assists / src / handlers / add_impl.rs
1 use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2 use stdx::{format_to, SepBy};
3
4 use crate::{AssistContext, AssistId, Assists};
5
6 // Assist: add_impl
7 //
8 // Adds a new inherent impl for a type.
9 //
10 // ```
11 // struct Ctx<T: Clone> {
12 //     data: T,<|>
13 // }
14 // ```
15 // ->
16 // ```
17 // struct Ctx<T: Clone> {
18 //     data: T,
19 // }
20 //
21 // impl<T: Clone> Ctx<T> {
22 //     $0
23 // }
24 // ```
25 pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26     let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27     let name = nominal.name()?;
28     let target = nominal.syntax().text_range();
29     acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
30         let type_params = nominal.type_param_list();
31         let start_offset = nominal.syntax().text_range().end();
32         let mut buf = String::new();
33         buf.push_str("\n\nimpl");
34         if let Some(type_params) = &type_params {
35             format_to!(buf, "{}", type_params.syntax());
36         }
37         buf.push_str(" ");
38         buf.push_str(name.text().as_str());
39         if let Some(type_params) = type_params {
40             let lifetime_params = type_params
41                 .lifetime_params()
42                 .filter_map(|it| it.lifetime_token())
43                 .map(|it| it.text().clone());
44             let type_params =
45                 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
46
47             let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48             format_to!(buf, "<{}>", generic_params)
49         }
50         match ctx.config.snippet_cap {
51             Some(cap) => {
52                 buf.push_str(" {\n    $0\n}");
53                 edit.insert_snippet(cap, start_offset, buf);
54             }
55             None => {
56                 buf.push_str(" {\n}");
57                 edit.insert(start_offset, buf);
58             }
59         }
60     })
61 }
62
63 #[cfg(test)]
64 mod tests {
65     use crate::tests::{check_assist, check_assist_target};
66
67     use super::*;
68
69     #[test]
70     fn test_add_impl() {
71         check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n    $0\n}\n");
72         check_assist(
73             add_impl,
74             "struct Foo<T: Clone> {<|>}",
75             "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n    $0\n}",
76         );
77         check_assist(
78             add_impl,
79             "struct Foo<'a, T: Foo<'a>> {<|>}",
80             "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n    $0\n}",
81         );
82     }
83
84     #[test]
85     fn add_impl_target() {
86         check_assist_target(
87             add_impl,
88             "
89 struct SomeThingIrrelevant;
90 /// Has a lifetime parameter
91 struct Foo<'a, T: Foo<'a>> {<|>}
92 struct EvenMoreIrrelevant;
93 ",
94             "/// Has a lifetime parameter
95 struct Foo<'a, T: Foo<'a>> {}",
96         );
97     }
98 }