aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/assists/add_derive.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src/assists/add_derive.rs')
-rw-r--r--crates/ra_editor/src/assists/add_derive.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/crates/ra_editor/src/assists/add_derive.rs b/crates/ra_editor/src/assists/add_derive.rs
new file mode 100644
index 000000000..33d9d2c31
--- /dev/null
+++ b/crates/ra_editor/src/assists/add_derive.rs
@@ -0,0 +1,97 @@
1use ra_text_edit::TextEditBuilder;
2use ra_syntax::{
3 ast::{self, AstNode, AttrsOwner},
4 SourceFileNode,
5 SyntaxKind::{WHITESPACE, COMMENT},
6 TextUnit,
7};
8
9use crate::{
10 find_node_at_offset,
11 assists::LocalEdit,
12};
13
14pub fn add_derive<'a>(
15 file: &'a SourceFileNode,
16 offset: TextUnit,
17) -> Option<impl FnOnce() -> LocalEdit + 'a> {
18 let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?;
19 let node_start = derive_insertion_offset(nominal)?;
20 return Some(move || {
21 let derive_attr = nominal
22 .attrs()
23 .filter_map(|x| x.as_call())
24 .filter(|(name, _arg)| name == "derive")
25 .map(|(_name, arg)| arg)
26 .next();
27 let mut edit = TextEditBuilder::new();
28 let offset = match derive_attr {
29 None => {
30 edit.insert(node_start, "#[derive()]\n".to_string());
31 node_start + TextUnit::of_str("#[derive(")
32 }
33 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
34 };
35 LocalEdit {
36 label: "add `#[derive]`".to_string(),
37 edit: edit.finish(),
38 cursor_position: Some(offset),
39 }
40 });
41
42 // Insert `derive` after doc comments.
43 fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> {
44 let non_ws_child = nominal
45 .syntax()
46 .children()
47 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
48 Some(non_ws_child.range().start())
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::test_utils::check_action;
56
57 #[test]
58 fn add_derive_new() {
59 check_action(
60 "struct Foo { a: i32, <|>}",
61 "#[derive(<|>)]\nstruct Foo { a: i32, }",
62 |file, off| add_derive(file, off).map(|f| f()),
63 );
64 check_action(
65 "struct Foo { <|> a: i32, }",
66 "#[derive(<|>)]\nstruct Foo { a: i32, }",
67 |file, off| add_derive(file, off).map(|f| f()),
68 );
69 }
70
71 #[test]
72 fn add_derive_existing() {
73 check_action(
74 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
75 "#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
76 |file, off| add_derive(file, off).map(|f| f()),
77 );
78 }
79
80 #[test]
81 fn add_derive_new_with_doc_comment() {
82 check_action(
83 "
84/// `Foo` is a pretty important struct.
85/// It does stuff.
86struct Foo { a: i32<|>, }
87 ",
88 "
89/// `Foo` is a pretty important struct.
90/// It does stuff.
91#[derive(<|>)]
92struct Foo { a: i32, }
93 ",
94 |file, off| add_derive(file, off).map(|f| f()),
95 );
96 }
97}