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