aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assists/add_custom_impl.rs206
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/lib.rs2
3 files changed, 227 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs
new file mode 100644
index 000000000..037306fd6
--- /dev/null
+++ b/crates/ra_assists/src/assists/add_custom_impl.rs
@@ -0,0 +1,206 @@
1//! FIXME: write short doc here
2
3use crate::{Assist, AssistCtx, AssistId};
4use hir::db::HirDatabase;
5use join_to_string::join;
6use ra_syntax::{
7 ast::{self, AstNode},
8 Direction, SmolStr,
9 SyntaxKind::{IDENT, WHITESPACE},
10 TextRange, TextUnit,
11};
12
13const DERIVE_TRAIT: &'static str = "derive";
14
15// Assist: add_custom_impl
16//
17// Adds impl block for derived trait.
18//
19// ```
20// #[derive(Deb<|>ug, Display)]
21// struct S;
22// ```
23// ->
24// ```
25// #[derive(Display)]
26// struct S;
27//
28// impl Debug for S {
29//
30// }
31// ```
32pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
33 let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
34 let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
35
36 let attr_name = attr
37 .syntax()
38 .descendants_with_tokens()
39 .filter(|t| t.kind() == IDENT)
40 .find_map(|i| i.into_token())
41 .filter(|t| *t.text() == DERIVE_TRAIT)?
42 .text()
43 .clone();
44
45 let trait_token =
46 ctx.token_at_offset().filter(|t| t.kind() == IDENT && *t.text() != attr_name).next()?;
47
48 let annotated = attr.syntax().siblings(Direction::Next).find_map(|s| ast::Name::cast(s))?;
49 let annotated_name = annotated.syntax().text().to_string();
50 let start_offset = annotated.syntax().parent()?.text_range().end();
51
52 ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| {
53 edit.target(attr.syntax().text_range());
54
55 let new_attr_input = input
56 .syntax()
57 .descendants_with_tokens()
58 .filter(|t| t.kind() == IDENT)
59 .filter_map(|t| t.into_token().map(|t| t.text().clone()))
60 .filter(|t| t != trait_token.text())
61 .collect::<Vec<SmolStr>>();
62 let has_more_derives = new_attr_input.len() > 0;
63 let new_attr_input =
64 join(new_attr_input.iter()).separator(", ").surround_with("(", ")").to_string();
65 let new_attr_input_len = new_attr_input.len();
66
67 let mut buf = String::new();
68 buf.push_str("\n\nimpl ");
69 buf.push_str(trait_token.text().as_str());
70 buf.push_str(" for ");
71 buf.push_str(annotated_name.as_str());
72 buf.push_str(" {\n");
73
74 let cursor_delta = if has_more_derives {
75 edit.replace(input.syntax().text_range(), new_attr_input);
76 input.syntax().text_range().len() - TextUnit::from_usize(new_attr_input_len)
77 } else {
78 let attr_range = attr.syntax().text_range();
79 edit.delete(attr_range);
80
81 let line_break_range = attr
82 .syntax()
83 .next_sibling_or_token()
84 .filter(|t| t.kind() == WHITESPACE)
85 .map(|t| t.text_range())
86 .unwrap_or(TextRange::from_to(TextUnit::from(0), TextUnit::from(0)));
87 edit.delete(line_break_range);
88
89 attr_range.len() + line_break_range.len()
90 };
91
92 edit.set_cursor(start_offset + TextUnit::of_str(&buf) - cursor_delta);
93 buf.push_str("\n}");
94 edit.insert(start_offset, buf);
95 })
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::helpers::{check_assist, check_assist_not_applicable};
102
103 #[test]
104 fn add_custom_impl_for_unique_input() {
105 check_assist(
106 add_custom_impl,
107 "
108#[derive(Debu<|>g)]
109struct Foo {
110 bar: String,
111}
112 ",
113 "
114struct Foo {
115 bar: String,
116}
117
118impl Debug for Foo {
119<|>
120}
121 ",
122 )
123 }
124
125 #[test]
126 fn add_custom_impl_for_with_visibility_modifier() {
127 check_assist(
128 add_custom_impl,
129 "
130#[derive(Debug<|>)]
131pub struct Foo {
132 bar: String,
133}
134 ",
135 "
136pub struct Foo {
137 bar: String,
138}
139
140impl Debug for Foo {
141<|>
142}
143 ",
144 )
145 }
146
147 #[test]
148 fn add_custom_impl_when_multiple_inputs() {
149 check_assist(
150 add_custom_impl,
151 "
152#[derive(Display, Debug<|>, Serialize)]
153struct Foo {}
154 ",
155 "
156#[derive(Display, Serialize)]
157struct Foo {}
158
159impl Debug for Foo {
160<|>
161}
162 ",
163 )
164 }
165
166 #[test]
167 fn test_ignore_derive_macro_without_input() {
168 check_assist_not_applicable(
169 add_custom_impl,
170 "
171#[derive(<|>)]
172struct Foo {}
173 ",
174 )
175 }
176
177 #[test]
178 fn test_ignore_if_cursor_on_param() {
179 check_assist_not_applicable(
180 add_custom_impl,
181 "
182#[derive<|>(Debug)]
183struct Foo {}
184 ",
185 );
186
187 check_assist_not_applicable(
188 add_custom_impl,
189 "
190#[derive(Debug)<|>]
191struct Foo {}
192 ",
193 )
194 }
195
196 #[test]
197 fn test_ignore_if_not_derive() {
198 check_assist_not_applicable(
199 add_custom_impl,
200 "
201#[allow(non_camel_<|>case_types)]
202struct Foo {}
203 ",
204 )
205 }
206}
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 3c716c2d1..4586eeb59 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -3,6 +3,25 @@
3use super::check; 3use super::check;
4 4
5#[test] 5#[test]
6fn doctest_add_custom_impl() {
7 check(
8 "add_custom_impl",
9 r#####"
10#[derive(Deb<|>ug, Display)]
11struct S;
12"#####,
13 r#####"
14#[derive(Display)]
15struct S;
16
17impl Debug for S {
18
19}
20"#####,
21 )
22}
23
24#[test]
6fn doctest_add_derive() { 25fn doctest_add_derive() {
7 check( 26 check(
8 "add_derive", 27 "add_derive",
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index a372bd8b9..98fb20b22 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -95,6 +95,7 @@ mod assists {
95 mod add_derive; 95 mod add_derive;
96 mod add_explicit_type; 96 mod add_explicit_type;
97 mod add_impl; 97 mod add_impl;
98 mod add_custom_impl;
98 mod add_new; 99 mod add_new;
99 mod apply_demorgan; 100 mod apply_demorgan;
100 mod invert_if; 101 mod invert_if;
@@ -121,6 +122,7 @@ mod assists {
121 add_derive::add_derive, 122 add_derive::add_derive,
122 add_explicit_type::add_explicit_type, 123 add_explicit_type::add_explicit_type,
123 add_impl::add_impl, 124 add_impl::add_impl,
125 add_custom_impl::add_custom_impl,
124 add_new::add_new, 126 add_new::add_new,
125 apply_demorgan::apply_demorgan, 127 apply_demorgan::apply_demorgan,
126 invert_if::invert_if, 128 invert_if::invert_if,