aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-05-01 01:46:17 +0100
committerKirill Bulatov <[email protected]>2020-05-02 19:41:02 +0100
commit767bff89ededdff875b042bb37397b972e3a82f1 (patch)
treebc2d1cfa1211d471bfcb9abfb6ea0b0d0b57b300 /crates
parentc4b32d15340e1dae74d3eaa701ef92ae0f9e295e (diff)
Complete standard derives
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs255
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs6
2 files changed, 245 insertions, 16 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 8bf952798..8934b45fe 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -5,23 +5,26 @@
5 5
6use super::completion_context::CompletionContext; 6use super::completion_context::CompletionContext;
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions};
8use ast::AttrInput;
8use ra_syntax::{ 9use ra_syntax::{
9 ast::{Attr, AttrKind}, 10 ast::{self, AttrKind},
10 AstNode, 11 AstNode, SyntaxKind,
11}; 12};
13use rustc_hash::FxHashSet;
12 14
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { 15pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 if !ctx.is_attribute { 16 let attribute = ctx.attribute_under_caret.as_ref()?;
15 return;
16 }
17 17
18 let is_inner = ctx 18 match (attribute.path(), attribute.input()) {
19 .original_token 19 (Some(path), Some(AttrInput::TokenTree(token_tree))) if path.to_string() == "derive" => {
20 .ancestors() 20 complete_derive(acc, ctx, token_tree)
21 .find_map(Attr::cast) 21 }
22 .map(|attr| attr.kind() == AttrKind::Inner) 22 _ => complete_attribute_start(acc, ctx, attribute),
23 .unwrap_or(false); 23 }
24 Some(())
25}
24 26
27fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
25 for attr_completion in ATTRIBUTES { 28 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new( 29 let mut item = CompletionItem::new(
27 CompletionKind::Attribute, 30 CompletionKind::Attribute,
@@ -37,7 +40,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
37 _ => {} 40 _ => {}
38 } 41 }
39 42
40 if is_inner || !attr_completion.should_be_inner { 43 if attribute.kind() == AttrKind::Inner || !attr_completion.should_be_inner {
41 acc.add(item); 44 acc.add(item);
42 } 45 }
43 } 46 }
@@ -126,6 +129,68 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126 }, 129 },
127]; 130];
128 131
132fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
133 // TODO kb autodetect derive macros
134 // https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Find.20all.20possible.20derive.20macro.20values.3F/near/195955580
135
136 if let Ok(existing_derives) = parse_derive_input(derive_input) {
137 for derive_completion in DERIVE_COMPLETIONS
138 .into_iter()
139 .filter(|completion| !existing_derives.contains(completion.label))
140 {
141 let mut label = derive_completion.label.to_owned();
142 for dependency in derive_completion
143 .dependencies
144 .into_iter()
145 .filter(|&&dependency| !existing_derives.contains(dependency))
146 {
147 label.push_str(", ");
148 label.push_str(dependency);
149 }
150 let item = CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
151 .kind(CompletionItemKind::Attribute);
152 acc.add(item);
153 }
154 }
155}
156
157fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
158 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
159 (Some(left_paren), Some(right_paren))
160 if left_paren.kind() == SyntaxKind::L_PAREN
161 && right_paren.kind() == SyntaxKind::R_PAREN =>
162 {
163 Ok(derive_input
164 .syntax()
165 .children_with_tokens()
166 .filter_map(|child| child.into_token())
167 .skip_while(|child| child != &left_paren)
168 .take_while(|child| child != &right_paren)
169 .filter(|child| child.kind() == SyntaxKind::IDENT)
170 .map(|child| child.to_string())
171 .collect())
172 }
173 _ => Err(()),
174 }
175}
176
177struct DeriveCompletion {
178 label: &'static str,
179 dependencies: &'static [&'static str],
180}
181
182const DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
183 DeriveCompletion { label: "Clone", dependencies: &[] },
184 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
185 DeriveCompletion { label: "Debug", dependencies: &[] },
186 DeriveCompletion { label: "Default", dependencies: &[] },
187 DeriveCompletion { label: "Hash", dependencies: &[] },
188 DeriveCompletion { label: "PartialEq", dependencies: &[] },
189 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
190 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
191 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
192];
193
129#[cfg(test)] 194#[cfg(test)]
130mod tests { 195mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 196 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
@@ -136,6 +201,170 @@ mod tests {
136 } 201 }
137 202
138 #[test] 203 #[test]
204 fn empty_derive_completion() {
205 assert_debug_snapshot!(
206 do_attr_completion(
207 r"
208 #[derive(<|>)]
209 struct Test {}
210 ",
211 ),
212 @r###"
213 [
214 CompletionItem {
215 label: "Clone",
216 source_range: 30..30,
217 delete: 30..30,
218 insert: "Clone",
219 kind: Attribute,
220 },
221 CompletionItem {
222 label: "Copy, Clone",
223 source_range: 30..30,
224 delete: 30..30,
225 insert: "Copy, Clone",
226 kind: Attribute,
227 },
228 CompletionItem {
229 label: "Debug",
230 source_range: 30..30,
231 delete: 30..30,
232 insert: "Debug",
233 kind: Attribute,
234 },
235 CompletionItem {
236 label: "Default",
237 source_range: 30..30,
238 delete: 30..30,
239 insert: "Default",
240 kind: Attribute,
241 },
242 CompletionItem {
243 label: "Eq, PartialEq",
244 source_range: 30..30,
245 delete: 30..30,
246 insert: "Eq, PartialEq",
247 kind: Attribute,
248 },
249 CompletionItem {
250 label: "Hash",
251 source_range: 30..30,
252 delete: 30..30,
253 insert: "Hash",
254 kind: Attribute,
255 },
256 CompletionItem {
257 label: "Ord, PartialOrd, Eq, PartialEq",
258 source_range: 30..30,
259 delete: 30..30,
260 insert: "Ord, PartialOrd, Eq, PartialEq",
261 kind: Attribute,
262 },
263 CompletionItem {
264 label: "PartialEq",
265 source_range: 30..30,
266 delete: 30..30,
267 insert: "PartialEq",
268 kind: Attribute,
269 },
270 CompletionItem {
271 label: "PartialOrd, PartialEq",
272 source_range: 30..30,
273 delete: 30..30,
274 insert: "PartialOrd, PartialEq",
275 kind: Attribute,
276 },
277 ]
278 "###
279 );
280 }
281
282 #[test]
283 fn no_completion_for_incorrect_derive() {
284 assert_debug_snapshot!(
285 do_attr_completion(
286 r"
287 #[derive{<|>)]
288 struct Test {}
289 ",
290 ),
291 @"[]"
292 );
293 }
294
295 #[test]
296 fn derive_with_input_completion() {
297 assert_debug_snapshot!(
298 do_attr_completion(
299 r"
300 #[derive(Whatever, PartialEq, <|>)]
301 struct Test {}
302 ",
303 ),
304 @r###"
305 [
306 CompletionItem {
307 label: "Clone",
308 source_range: 51..51,
309 delete: 51..51,
310 insert: "Clone",
311 kind: Attribute,
312 },
313 CompletionItem {
314 label: "Copy, Clone",
315 source_range: 51..51,
316 delete: 51..51,
317 insert: "Copy, Clone",
318 kind: Attribute,
319 },
320 CompletionItem {
321 label: "Debug",
322 source_range: 51..51,
323 delete: 51..51,
324 insert: "Debug",
325 kind: Attribute,
326 },
327 CompletionItem {
328 label: "Default",
329 source_range: 51..51,
330 delete: 51..51,
331 insert: "Default",
332 kind: Attribute,
333 },
334 CompletionItem {
335 label: "Eq",
336 source_range: 51..51,
337 delete: 51..51,
338 insert: "Eq",
339 kind: Attribute,
340 },
341 CompletionItem {
342 label: "Hash",
343 source_range: 51..51,
344 delete: 51..51,
345 insert: "Hash",
346 kind: Attribute,
347 },
348 CompletionItem {
349 label: "Ord, PartialOrd, Eq",
350 source_range: 51..51,
351 delete: 51..51,
352 insert: "Ord, PartialOrd, Eq",
353 kind: Attribute,
354 },
355 CompletionItem {
356 label: "PartialOrd",
357 source_range: 51..51,
358 delete: 51..51,
359 insert: "PartialOrd",
360 kind: Attribute,
361 },
362 ]
363 "###
364 );
365 }
366
367 #[test]
139 fn test_attribute_completion() { 368 fn test_attribute_completion() {
140 assert_debug_snapshot!( 369 assert_debug_snapshot!(
141 do_attr_completion( 370 do_attr_completion(
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c529752d4..dd87bd119 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -58,7 +58,7 @@ pub(crate) struct CompletionContext<'a> {
58 pub(super) is_macro_call: bool, 58 pub(super) is_macro_call: bool,
59 pub(super) is_path_type: bool, 59 pub(super) is_path_type: bool,
60 pub(super) has_type_args: bool, 60 pub(super) has_type_args: bool,
61 pub(super) is_attribute: bool, 61 pub(super) attribute_under_caret: Option<ast::Attr>,
62} 62}
63 63
64impl<'a> CompletionContext<'a> { 64impl<'a> CompletionContext<'a> {
@@ -116,7 +116,7 @@ impl<'a> CompletionContext<'a> {
116 is_path_type: false, 116 is_path_type: false,
117 has_type_args: false, 117 has_type_args: false,
118 dot_receiver_is_ambiguous_float_literal: false, 118 dot_receiver_is_ambiguous_float_literal: false,
119 is_attribute: false, 119 attribute_under_caret: None,
120 }; 120 };
121 121
122 let mut original_file = original_file.syntax().clone(); 122 let mut original_file = original_file.syntax().clone();
@@ -200,6 +200,7 @@ impl<'a> CompletionContext<'a> {
200 Some(ty) 200 Some(ty)
201 }) 201 })
202 .flatten(); 202 .flatten();
203 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
203 204
204 // First, let's try to complete a reference to some declaration. 205 // First, let's try to complete a reference to some declaration.
205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 206 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
@@ -318,7 +319,6 @@ impl<'a> CompletionContext<'a> {
318 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 319 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
319 .is_some(); 320 .is_some();
320 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 321 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
321 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
322 322
323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
324 self.has_type_args = segment.type_arg_list().is_some(); 324 self.has_type_args = segment.type_arg_list().is_some();