]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/assists/add_derive.rs
Parse correct AttrInput
[rust.git] / crates / ra_assists / src / assists / add_derive.rs
1 use hir::db::HirDatabase;
2 use ra_syntax::{
3     ast::{self, AstNode, AttrsOwner},
4     SyntaxKind::{COMMENT, WHITESPACE},
5     TextUnit,
6 };
7
8 use crate::{Assist, AssistCtx, AssistId};
9
10 pub(crate) fn add_derive(mut 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.add_action(AssistId("add_derive"), "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().text_range().end() - TextUnit::of_char(')'),
26         };
27         edit.target(nominal.syntax().text_range());
28         edit.set_cursor(offset)
29     });
30
31     ctx.build()
32 }
33
34 // Insert `derive` after doc comments.
35 fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
36     let non_ws_child = nominal
37         .syntax()
38         .children_with_tokens()
39         .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
40     Some(non_ws_child.text_range().start())
41 }
42
43 #[cfg(test)]
44 mod tests {
45     use super::*;
46     use crate::helpers::{check_assist, check_assist_target};
47
48     #[test]
49     fn add_derive_new() {
50         check_assist(
51             add_derive,
52             "struct Foo { a: i32, <|>}",
53             "#[derive(<|>)]\nstruct Foo { a: i32, }",
54         );
55         check_assist(
56             add_derive,
57             "struct Foo { <|> a: i32, }",
58             "#[derive(<|>)]\nstruct Foo {  a: i32, }",
59         );
60     }
61
62     #[test]
63     fn add_derive_existing() {
64         check_assist(
65             add_derive,
66             "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
67             "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
68         );
69     }
70
71     #[test]
72     fn add_derive_new_with_doc_comment() {
73         check_assist(
74             add_derive,
75             "
76 /// `Foo` is a pretty important struct.
77 /// It does stuff.
78 struct Foo { a: i32<|>, }
79             ",
80             "
81 /// `Foo` is a pretty important struct.
82 /// It does stuff.
83 #[derive(<|>)]
84 struct Foo { a: i32, }
85             ",
86         );
87     }
88
89     #[test]
90     fn add_derive_target() {
91         check_assist_target(
92             add_derive,
93             "
94 struct SomeThingIrrelevant;
95 /// `Foo` is a pretty important struct.
96 /// It does stuff.
97 struct Foo { a: i32<|>, }
98 struct EvenMoreIrrelevant;
99             ",
100             "/// `Foo` is a pretty important struct.
101 /// It does stuff.
102 struct Foo { a: i32, }",
103         );
104     }
105 }