diff options
Diffstat (limited to 'crates/ra_assists/src/add_derive.rs')
-rw-r--r-- | crates/ra_assists/src/add_derive.rs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/crates/ra_assists/src/add_derive.rs b/crates/ra_assists/src/add_derive.rs new file mode 100644 index 000000000..01a4079f6 --- /dev/null +++ b/crates/ra_assists/src/add_derive.rs | |||
@@ -0,0 +1,85 @@ | |||
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.set_cursor(offset) | ||
28 | }) | ||
29 | } | ||
30 | |||
31 | // Insert `derive` after doc comments. | ||
32 | fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> { | ||
33 | let non_ws_child = nominal | ||
34 | .syntax() | ||
35 | .children() | ||
36 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
37 | Some(non_ws_child.range().start()) | ||
38 | } | ||
39 | |||
40 | #[cfg(test)] | ||
41 | mod tests { | ||
42 | use super::*; | ||
43 | use crate::helpers::check_assist; | ||
44 | |||
45 | #[test] | ||
46 | fn add_derive_new() { | ||
47 | check_assist( | ||
48 | add_derive, | ||
49 | "struct Foo { a: i32, <|>}", | ||
50 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
51 | ); | ||
52 | check_assist( | ||
53 | add_derive, | ||
54 | "struct Foo { <|> a: i32, }", | ||
55 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn add_derive_existing() { | ||
61 | check_assist( | ||
62 | add_derive, | ||
63 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | ||
64 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | ||
65 | ); | ||
66 | } | ||
67 | |||
68 | #[test] | ||
69 | fn add_derive_new_with_doc_comment() { | ||
70 | check_assist( | ||
71 | add_derive, | ||
72 | " | ||
73 | /// `Foo` is a pretty important struct. | ||
74 | /// It does stuff. | ||
75 | struct Foo { a: i32<|>, } | ||
76 | ", | ||
77 | " | ||
78 | /// `Foo` is a pretty important struct. | ||
79 | /// It does stuff. | ||
80 | #[derive(<|>)] | ||
81 | struct Foo { a: i32, } | ||
82 | ", | ||
83 | ); | ||
84 | } | ||
85 | } | ||