aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/add_derive.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/add_derive.rs')
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs120
1 files changed, 120 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
new file mode 100644
index 000000000..b0d1a0a80
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -0,0 +1,120 @@
1use ra_syntax::{
2 ast::{self, AstNode, AttrsOwner},
3 SyntaxKind::{COMMENT, WHITESPACE},
4 TextUnit,
5};
6
7use crate::{Assist, AssistCtx, AssistId};
8
9// Assist: add_derive
10//
11// Adds a new `#[derive()]` clause to a struct or enum.
12//
13// ```
14// struct Point {
15// x: u32,
16// y: u32,<|>
17// }
18// ```
19// ->
20// ```
21// #[derive()]
22// struct Point {
23// x: u32,
24// y: u32,
25// }
26// ```
27pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> {
28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
29 let node_start = derive_insertion_offset(&nominal)?;
30 ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| {
31 let derive_attr = nominal
32 .attrs()
33 .filter_map(|x| x.as_simple_call())
34 .filter(|(name, _arg)| name == "derive")
35 .map(|(_name, arg)| arg)
36 .next();
37 let offset = match derive_attr {
38 None => {
39 edit.insert(node_start, "#[derive()]\n");
40 node_start + TextUnit::of_str("#[derive(")
41 }
42 Some(tt) => tt.syntax().text_range().end() - TextUnit::of_char(')'),
43 };
44 edit.target(nominal.syntax().text_range());
45 edit.set_cursor(offset)
46 })
47}
48
49// Insert `derive` after doc comments.
50fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
51 let non_ws_child = nominal
52 .syntax()
53 .children_with_tokens()
54 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
55 Some(non_ws_child.text_range().start())
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::helpers::{check_assist, check_assist_target};
62
63 #[test]
64 fn add_derive_new() {
65 check_assist(
66 add_derive,
67 "struct Foo { a: i32, <|>}",
68 "#[derive(<|>)]\nstruct Foo { a: i32, }",
69 );
70 check_assist(
71 add_derive,
72 "struct Foo { <|> a: i32, }",
73 "#[derive(<|>)]\nstruct Foo { a: i32, }",
74 );
75 }
76
77 #[test]
78 fn add_derive_existing() {
79 check_assist(
80 add_derive,
81 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
82 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
83 );
84 }
85
86 #[test]
87 fn add_derive_new_with_doc_comment() {
88 check_assist(
89 add_derive,
90 "
91/// `Foo` is a pretty important struct.
92/// It does stuff.
93struct Foo { a: i32<|>, }
94 ",
95 "
96/// `Foo` is a pretty important struct.
97/// It does stuff.
98#[derive(<|>)]
99struct Foo { a: i32, }
100 ",
101 );
102 }
103
104 #[test]
105 fn add_derive_target() {
106 check_assist_target(
107 add_derive,
108 "
109struct SomeThingIrrelevant;
110/// `Foo` is a pretty important struct.
111/// It does stuff.
112struct Foo { a: i32<|>, }
113struct EvenMoreIrrelevant;
114 ",
115 "/// `Foo` is a pretty important struct.
116/// It does stuff.
117struct Foo { a: i32, }",
118 );
119 }
120}