]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/add_derive.rs
ea9707631db0b2815ab391ae847512d5a1eecd49
[rust.git] / crates / ra_assists / src / add_derive.rs
1 use hir::db::HirDatabase;
2 use ra_syntax::{
3     ast::{self, AstNode, AttrsOwner},
4     SyntaxKind::{WHITESPACE, COMMENT},
5     TextUnit,
6 };
7
8 use crate::{AssistCtx, Assist};
9
10 pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11     let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
12     let node_start = derive_insertion_offset(nominal)?;
13     ctx.build("add `#[derive]`", |edit| {
14         let derive_attr = nominal
15             .attrs()
16             .filter_map(|x| x.as_call())
17             .filter(|(name, _arg)| name == "derive")
18             .map(|(_name, arg)| arg)
19             .next();
20         let offset = match derive_attr {
21             None => {
22                 edit.insert(node_start, "#[derive()]\n");
23                 node_start + TextUnit::of_str("#[derive(")
24             }
25             Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
26         };
27         edit.target(nominal.syntax().range());
28         edit.set_cursor(offset)
29     })
30 }
31
32 // Insert `derive` after doc comments.
33 fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
34     let non_ws_child =
35         nominal.syntax().children().find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
36     Some(non_ws_child.range().start())
37 }
38
39 #[cfg(test)]
40 mod tests {
41     use super::*;
42     use crate::helpers::{check_assist, check_assist_target};
43
44     #[test]
45     fn add_derive_new() {
46         check_assist(
47             add_derive,
48             "struct Foo { a: i32, <|>}",
49             "#[derive(<|>)]\nstruct Foo { a: i32, }",
50         );
51         check_assist(
52             add_derive,
53             "struct Foo { <|> a: i32, }",
54             "#[derive(<|>)]\nstruct Foo {  a: i32, }",
55         );
56     }
57
58     #[test]
59     fn add_derive_existing() {
60         check_assist(
61             add_derive,
62             "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
63             "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
64         );
65     }
66
67     #[test]
68     fn add_derive_new_with_doc_comment() {
69         check_assist(
70             add_derive,
71             "
72 /// `Foo` is a pretty important struct.
73 /// It does stuff.
74 struct Foo { a: i32<|>, }
75             ",
76             "
77 /// `Foo` is a pretty important struct.
78 /// It does stuff.
79 #[derive(<|>)]
80 struct Foo { a: i32, }
81             ",
82         );
83     }
84
85     #[test]
86     fn add_derive_target() {
87         check_assist_target(
88             add_derive,
89             "
90 struct SomeThingIrrelevant;
91 /// `Foo` is a pretty important struct.
92 /// It does stuff.
93 struct Foo { a: i32<|>, }
94 struct EvenMoreIrrelevant;
95             ",
96             "/// `Foo` is a pretty important struct.
97 /// It does stuff.
98 struct Foo { a: i32, }",
99         );
100     }
101 }