aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs587
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs12
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs39
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs46
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs18
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs229
-rw-r--r--crates/ra_ide/src/completion/completion_config.rs35
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs31
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs38
-rw-r--r--crates/ra_ide/src/completion/presentation.rs419
14 files changed, 1378 insertions, 116 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
new file mode 100644
index 000000000..b405042e8
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -0,0 +1,587 @@
1//! Completion for attributes
2//!
3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes.
5
6use super::completion_context::CompletionContext;
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions};
8use ra_syntax::{
9 ast::{Attr, AttrKind},
10 AstNode,
11};
12
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
14 if !ctx.is_attribute {
15 return;
16 }
17
18 let is_inner = ctx
19 .original_token
20 .ancestors()
21 .find_map(Attr::cast)
22 .map(|attr| attr.kind() == AttrKind::Inner)
23 .unwrap_or(false);
24
25 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new(
27 CompletionKind::Attribute,
28 ctx.source_range(),
29 attr_completion.label,
30 )
31 .kind(CompletionItemKind::Attribute);
32
33 match (attr_completion.snippet, ctx.config.snippet_cap) {
34 (Some(snippet), Some(cap)) => {
35 item = item.insert_snippet(cap, snippet);
36 }
37 _ => {}
38 }
39
40 if is_inner || !attr_completion.should_be_inner {
41 acc.add(item);
42 }
43 }
44}
45
46struct AttrCompletion {
47 label: &'static str,
48 snippet: Option<&'static str>,
49 should_be_inner: bool,
50}
51
52const ATTRIBUTES: &[AttrCompletion] = &[
53 AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false },
54 AttrCompletion {
55 label: "cfg_attr",
56 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"),
57 should_be_inner: false,
58 },
59 AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false },
60 AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false },
61 AttrCompletion {
62 label: "deprecated",
63 snippet: Some(r#"deprecated = "${0:reason}""#),
64 should_be_inner: false,
65 },
66 AttrCompletion {
67 label: "derive",
68 snippet: Some(r#"derive(${0:Debug})"#),
69 should_be_inner: false,
70 },
71 AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
72 AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
73 AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false },
74 // FIXME: resolve through macro resolution?
75 AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true },
76 AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false },
77 AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false },
78 AttrCompletion {
79 label: "link_name",
80 snippet: Some(r#"link_name = "${0:symbol_name}""#),
81 should_be_inner: false,
82 },
83 AttrCompletion { label: "link", snippet: None, should_be_inner: false },
84 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false },
85 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false },
86 AttrCompletion {
87 label: "must_use",
88 snippet: Some(r#"must_use = "${0:reason}""#),
89 should_be_inner: false,
90 },
91 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false },
92 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true },
93 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false },
94 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true },
95 AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false },
96 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false },
97 AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false },
98 AttrCompletion {
99 label: "proc_macro_derive",
100 snippet: Some("proc_macro_derive(${0:Trait})"),
101 should_be_inner: false,
102 },
103 AttrCompletion {
104 label: "recursion_limit",
105 snippet: Some("recursion_limit = ${0:128}"),
106 should_be_inner: true,
107 },
108 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
109 AttrCompletion {
110 label: "should_panic",
111 snippet: Some(r#"expected = "${0:reason}""#),
112 should_be_inner: false,
113 },
114 AttrCompletion {
115 label: "target_feature",
116 snippet: Some("target_feature = \"${0:feature}\""),
117 should_be_inner: false,
118 },
119 AttrCompletion { label: "test", snippet: None, should_be_inner: false },
120 AttrCompletion { label: "used", snippet: None, should_be_inner: false },
121 AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
122 AttrCompletion {
123 label: "windows_subsystem",
124 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
125 should_be_inner: true,
126 },
127];
128
129#[cfg(test)]
130mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
132 use insta::assert_debug_snapshot;
133
134 fn do_attr_completion(code: &str) -> Vec<CompletionItem> {
135 do_completion(code, CompletionKind::Attribute)
136 }
137
138 #[test]
139 fn test_attribute_completion() {
140 assert_debug_snapshot!(
141 do_attr_completion(
142 r"
143 #[<|>]
144 ",
145 ),
146 @r###"
147 [
148 CompletionItem {
149 label: "allow",
150 source_range: [19; 19),
151 delete: [19; 19),
152 insert: "allow(${0:lint})",
153 kind: Attribute,
154 },
155 CompletionItem {
156 label: "cfg",
157 source_range: [19; 19),
158 delete: [19; 19),
159 insert: "cfg(${0:predicate})",
160 kind: Attribute,
161 },
162 CompletionItem {
163 label: "cfg_attr",
164 source_range: [19; 19),
165 delete: [19; 19),
166 insert: "cfg_attr(${1:predicate}, ${0:attr})",
167 kind: Attribute,
168 },
169 CompletionItem {
170 label: "deny",
171 source_range: [19; 19),
172 delete: [19; 19),
173 insert: "deny(${0:lint})",
174 kind: Attribute,
175 },
176 CompletionItem {
177 label: "deprecated",
178 source_range: [19; 19),
179 delete: [19; 19),
180 insert: "deprecated = \"${0:reason}\"",
181 kind: Attribute,
182 },
183 CompletionItem {
184 label: "derive",
185 source_range: [19; 19),
186 delete: [19; 19),
187 insert: "derive(${0:Debug})",
188 kind: Attribute,
189 },
190 CompletionItem {
191 label: "doc",
192 source_range: [19; 19),
193 delete: [19; 19),
194 insert: "doc = \"${0:docs}\"",
195 kind: Attribute,
196 },
197 CompletionItem {
198 label: "forbid",
199 source_range: [19; 19),
200 delete: [19; 19),
201 insert: "forbid(${0:lint})",
202 kind: Attribute,
203 },
204 CompletionItem {
205 label: "ignore",
206 source_range: [19; 19),
207 delete: [19; 19),
208 insert: "ignore(${0:lint})",
209 kind: Attribute,
210 },
211 CompletionItem {
212 label: "inline",
213 source_range: [19; 19),
214 delete: [19; 19),
215 insert: "inline(${0:lint})",
216 kind: Attribute,
217 },
218 CompletionItem {
219 label: "link",
220 source_range: [19; 19),
221 delete: [19; 19),
222 insert: "link",
223 kind: Attribute,
224 },
225 CompletionItem {
226 label: "link_name",
227 source_range: [19; 19),
228 delete: [19; 19),
229 insert: "link_name = \"${0:symbol_name}\"",
230 kind: Attribute,
231 },
232 CompletionItem {
233 label: "macro_export",
234 source_range: [19; 19),
235 delete: [19; 19),
236 insert: "macro_export",
237 kind: Attribute,
238 },
239 CompletionItem {
240 label: "macro_use",
241 source_range: [19; 19),
242 delete: [19; 19),
243 insert: "macro_use",
244 kind: Attribute,
245 },
246 CompletionItem {
247 label: "must_use",
248 source_range: [19; 19),
249 delete: [19; 19),
250 insert: "must_use = \"${0:reason}\"",
251 kind: Attribute,
252 },
253 CompletionItem {
254 label: "no_mangle",
255 source_range: [19; 19),
256 delete: [19; 19),
257 insert: "no_mangle",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "non_exhaustive",
262 source_range: [19; 19),
263 delete: [19; 19),
264 insert: "non_exhaustive",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "path",
269 source_range: [19; 19),
270 delete: [19; 19),
271 insert: "path =\"${0:path}\"",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "proc_macro",
276 source_range: [19; 19),
277 delete: [19; 19),
278 insert: "proc_macro",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "proc_macro_attribute",
283 source_range: [19; 19),
284 delete: [19; 19),
285 insert: "proc_macro_attribute",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "proc_macro_derive",
290 source_range: [19; 19),
291 delete: [19; 19),
292 insert: "proc_macro_derive(${0:Trait})",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "repr",
297 source_range: [19; 19),
298 delete: [19; 19),
299 insert: "repr(${0:C})",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "should_panic",
304 source_range: [19; 19),
305 delete: [19; 19),
306 insert: "expected = \"${0:reason}\"",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "target_feature",
311 source_range: [19; 19),
312 delete: [19; 19),
313 insert: "target_feature = \"${0:feature}\"",
314 kind: Attribute,
315 },
316 CompletionItem {
317 label: "test",
318 source_range: [19; 19),
319 delete: [19; 19),
320 insert: "test",
321 kind: Attribute,
322 },
323 CompletionItem {
324 label: "used",
325 source_range: [19; 19),
326 delete: [19; 19),
327 insert: "used",
328 kind: Attribute,
329 },
330 CompletionItem {
331 label: "warn",
332 source_range: [19; 19),
333 delete: [19; 19),
334 insert: "warn(${0:lint})",
335 kind: Attribute,
336 },
337 ]
338 "###
339 );
340 }
341
342 #[test]
343 fn test_inner_attribute_completion() {
344 assert_debug_snapshot!(
345 do_attr_completion(
346 r"
347 #![<|>]
348 ",
349 ),
350 @r###"
351 [
352 CompletionItem {
353 label: "allow",
354 source_range: [20; 20),
355 delete: [20; 20),
356 insert: "allow(${0:lint})",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "cfg",
361 source_range: [20; 20),
362 delete: [20; 20),
363 insert: "cfg(${0:predicate})",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "cfg_attr",
368 source_range: [20; 20),
369 delete: [20; 20),
370 insert: "cfg_attr(${1:predicate}, ${0:attr})",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "deny",
375 source_range: [20; 20),
376 delete: [20; 20),
377 insert: "deny(${0:lint})",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "deprecated",
382 source_range: [20; 20),
383 delete: [20; 20),
384 insert: "deprecated = \"${0:reason}\"",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "derive",
389 source_range: [20; 20),
390 delete: [20; 20),
391 insert: "derive(${0:Debug})",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "doc",
396 source_range: [20; 20),
397 delete: [20; 20),
398 insert: "doc = \"${0:docs}\"",
399 kind: Attribute,
400 },
401 CompletionItem {
402 label: "feature",
403 source_range: [20; 20),
404 delete: [20; 20),
405 insert: "feature(${0:flag})",
406 kind: Attribute,
407 },
408 CompletionItem {
409 label: "forbid",
410 source_range: [20; 20),
411 delete: [20; 20),
412 insert: "forbid(${0:lint})",
413 kind: Attribute,
414 },
415 CompletionItem {
416 label: "global_allocator",
417 source_range: [20; 20),
418 delete: [20; 20),
419 insert: "global_allocator",
420 kind: Attribute,
421 },
422 CompletionItem {
423 label: "ignore",
424 source_range: [20; 20),
425 delete: [20; 20),
426 insert: "ignore(${0:lint})",
427 kind: Attribute,
428 },
429 CompletionItem {
430 label: "inline",
431 source_range: [20; 20),
432 delete: [20; 20),
433 insert: "inline(${0:lint})",
434 kind: Attribute,
435 },
436 CompletionItem {
437 label: "link",
438 source_range: [20; 20),
439 delete: [20; 20),
440 insert: "link",
441 kind: Attribute,
442 },
443 CompletionItem {
444 label: "link_name",
445 source_range: [20; 20),
446 delete: [20; 20),
447 insert: "link_name = \"${0:symbol_name}\"",
448 kind: Attribute,
449 },
450 CompletionItem {
451 label: "macro_export",
452 source_range: [20; 20),
453 delete: [20; 20),
454 insert: "macro_export",
455 kind: Attribute,
456 },
457 CompletionItem {
458 label: "macro_use",
459 source_range: [20; 20),
460 delete: [20; 20),
461 insert: "macro_use",
462 kind: Attribute,
463 },
464 CompletionItem {
465 label: "must_use",
466 source_range: [20; 20),
467 delete: [20; 20),
468 insert: "must_use = \"${0:reason}\"",
469 kind: Attribute,
470 },
471 CompletionItem {
472 label: "no_mangle",
473 source_range: [20; 20),
474 delete: [20; 20),
475 insert: "no_mangle",
476 kind: Attribute,
477 },
478 CompletionItem {
479 label: "no_std",
480 source_range: [20; 20),
481 delete: [20; 20),
482 insert: "no_std",
483 kind: Attribute,
484 },
485 CompletionItem {
486 label: "non_exhaustive",
487 source_range: [20; 20),
488 delete: [20; 20),
489 insert: "non_exhaustive",
490 kind: Attribute,
491 },
492 CompletionItem {
493 label: "panic_handler",
494 source_range: [20; 20),
495 delete: [20; 20),
496 insert: "panic_handler",
497 kind: Attribute,
498 },
499 CompletionItem {
500 label: "path",
501 source_range: [20; 20),
502 delete: [20; 20),
503 insert: "path =\"${0:path}\"",
504 kind: Attribute,
505 },
506 CompletionItem {
507 label: "proc_macro",
508 source_range: [20; 20),
509 delete: [20; 20),
510 insert: "proc_macro",
511 kind: Attribute,
512 },
513 CompletionItem {
514 label: "proc_macro_attribute",
515 source_range: [20; 20),
516 delete: [20; 20),
517 insert: "proc_macro_attribute",
518 kind: Attribute,
519 },
520 CompletionItem {
521 label: "proc_macro_derive",
522 source_range: [20; 20),
523 delete: [20; 20),
524 insert: "proc_macro_derive(${0:Trait})",
525 kind: Attribute,
526 },
527 CompletionItem {
528 label: "recursion_limit",
529 source_range: [20; 20),
530 delete: [20; 20),
531 insert: "recursion_limit = ${0:128}",
532 kind: Attribute,
533 },
534 CompletionItem {
535 label: "repr",
536 source_range: [20; 20),
537 delete: [20; 20),
538 insert: "repr(${0:C})",
539 kind: Attribute,
540 },
541 CompletionItem {
542 label: "should_panic",
543 source_range: [20; 20),
544 delete: [20; 20),
545 insert: "expected = \"${0:reason}\"",
546 kind: Attribute,
547 },
548 CompletionItem {
549 label: "target_feature",
550 source_range: [20; 20),
551 delete: [20; 20),
552 insert: "target_feature = \"${0:feature}\"",
553 kind: Attribute,
554 },
555 CompletionItem {
556 label: "test",
557 source_range: [20; 20),
558 delete: [20; 20),
559 insert: "test",
560 kind: Attribute,
561 },
562 CompletionItem {
563 label: "used",
564 source_range: [20; 20),
565 delete: [20; 20),
566 insert: "used",
567 kind: Attribute,
568 },
569 CompletionItem {
570 label: "warn",
571 source_range: [20; 20),
572 delete: [20; 20),
573 insert: "warn(${0:lint})",
574 kind: Attribute,
575 },
576 CompletionItem {
577 label: "windows_subsystem",
578 source_range: [20; 20),
579 delete: [20; 20),
580 insert: "windows_subsystem = \"${0:subsystem}\"",
581 kind: Attribute,
582 },
583 ]
584 "###
585 );
586 }
587}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index f433faef3..b93153b48 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -2,9 +2,11 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind;
6use crate::{ 5use crate::{
7 completion::{completion_context::CompletionContext, completion_item::Completions}, 6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
8 CompletionItem, 10 CompletionItem,
9}; 11};
10use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index 38f9c34e7..adefb290e 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -42,10 +42,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
42} 42}
43 43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword) 46 .kind(CompletionItemKind::Keyword);
47 .insert_snippet(snippet) 47
48 .build() 48 match ctx.config.snippet_cap {
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 }
52 .build()
49} 53}
50 54
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index 270e96df0..6000106d0 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -41,7 +41,7 @@ mod tests {
41 @r###" 41 @r###"
42 [ 42 [
43 CompletionItem { 43 CompletionItem {
44 label: "foo!", 44 label: "foo!(…)",
45 source_range: [46; 46), 45 source_range: [46; 46),
46 delete: [46; 46), 46 delete: [46; 46),
47 insert: "foo!($0)", 47 insert: "foo!($0)",
@@ -81,7 +81,7 @@ mod tests {
81 @r###" 81 @r###"
82 [ 82 [
83 CompletionItem { 83 CompletionItem {
84 label: "vec!", 84 label: "vec![…]",
85 source_range: [280; 280), 85 source_range: [280; 280),
86 delete: [280; 280), 86 delete: [280; 280),
87 insert: "vec![$0]", 87 insert: "vec![$0]",
@@ -118,7 +118,7 @@ mod tests {
118 @r###" 118 @r###"
119 [ 119 [
120 CompletionItem { 120 CompletionItem {
121 label: "foo!", 121 label: "foo! {…}",
122 source_range: [163; 163), 122 source_range: [163; 163),
123 delete: [163; 163), 123 delete: [163; 163),
124 insert: "foo! {$0}", 124 insert: "foo! {$0}",
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index a8b4ce114..218829b10 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -125,7 +125,7 @@ mod tests {
125 kind: Enum, 125 kind: Enum,
126 }, 126 },
127 CompletionItem { 127 CompletionItem {
128 label: "m!", 128 label: "m!(…)",
129 source_range: [151; 151), 129 source_range: [151; 151),
130 delete: [151; 151), 130 delete: [151; 151),
131 insert: "m!($0)", 131 insert: "m!($0)",
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 29c2881c6..8d397b0fe 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -6,6 +6,7 @@ use ra_syntax::{
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
9use crate::{ 10use crate::{
10 completion::{ 11 completion::{
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
@@ -32,9 +33,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
32 None => return, 33 None => return,
33 }; 34 };
34 35
36 let cap = match ctx.config.snippet_cap {
37 Some(it) => it,
38 None => return,
39 };
40
35 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 41 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
36 postfix_snippet( 42 postfix_snippet(
37 ctx, 43 ctx,
44 cap,
38 &dot_receiver, 45 &dot_receiver,
39 "if", 46 "if",
40 "if expr {}", 47 "if expr {}",
@@ -43,6 +50,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
43 .add_to(acc); 50 .add_to(acc);
44 postfix_snippet( 51 postfix_snippet(
45 ctx, 52 ctx,
53 cap,
46 &dot_receiver, 54 &dot_receiver,
47 "while", 55 "while",
48 "while expr {}", 56 "while expr {}",
@@ -52,11 +60,20 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
52 } 60 }
53 61
54 // !&&&42 is a compiler error, ergo process it before considering the references 62 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc);
56 65
57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 66 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc); 67 .add_to(acc);
68 postfix_snippet(
69 ctx,
70 cap,
71 &dot_receiver,
72 "refm",
73 "&mut expr",
74 &format!("&mut {}", receiver_text),
75 )
76 .add_to(acc);
60 77
61 // The rest of the postfix completions create an expression that moves an argument, 78 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation 79 // so it's better to consider references now to avoid breaking the compilation
@@ -66,6 +83,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
66 83
67 postfix_snippet( 84 postfix_snippet(
68 ctx, 85 ctx,
86 cap,
69 &dot_receiver, 87 &dot_receiver,
70 "match", 88 "match",
71 "match expr {}", 89 "match expr {}",
@@ -75,6 +93,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
75 93
76 postfix_snippet( 94 postfix_snippet(
77 ctx, 95 ctx,
96 cap,
78 &dot_receiver, 97 &dot_receiver,
79 "box", 98 "box",
80 "Box::new(expr)", 99 "Box::new(expr)",
@@ -82,8 +101,15 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
82 ) 101 )
83 .add_to(acc); 102 .add_to(acc);
84 103
85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)) 104 postfix_snippet(
86 .add_to(acc); 105 ctx,
106 cap,
107 &dot_receiver,
108 "dbg",
109 "dbg!(expr)",
110 &format!("dbg!({})", receiver_text),
111 )
112 .add_to(acc);
87} 113}
88 114
89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 115fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@@ -108,6 +134,7 @@ fn include_references(initial_element: &ast::Expr) -> ast::Expr {
108 134
109fn postfix_snippet( 135fn postfix_snippet(
110 ctx: &CompletionContext, 136 ctx: &CompletionContext,
137 cap: SnippetCap,
111 receiver: &ast::Expr, 138 receiver: &ast::Expr,
112 label: &str, 139 label: &str,
113 detail: &str, 140 detail: &str,
@@ -121,7 +148,7 @@ fn postfix_snippet(
121 }; 148 };
122 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 149 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
123 .detail(detail) 150 .detail(detail)
124 .snippet_edit(edit) 151 .snippet_edit(cap, edit)
125} 152}
126 153
127#[cfg(test)] 154#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index d98523406..5a5139e14 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -57,9 +57,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
57 } 57 }
58 match item { 58 match item {
59 hir::AssocItem::Function(func) => { 59 hir::AssocItem::Function(func) => {
60 if !func.has_self_param(ctx.db) { 60 acc.add_function(ctx, func, None);
61 acc.add_function(ctx, func, None);
62 }
63 } 61 }
64 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 62 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
65 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), 63 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
@@ -86,9 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
86 } 84 }
87 match item { 85 match item {
88 hir::AssocItem::Function(func) => { 86 hir::AssocItem::Function(func) => {
89 if !func.has_self_param(ctx.db) { 87 acc.add_function(ctx, func, None);
90 acc.add_function(ctx, func, None);
91 }
92 } 88 }
93 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), 89 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
94 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), 90 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
@@ -483,6 +479,42 @@ mod tests {
483 } 479 }
484 480
485 #[test] 481 #[test]
482 fn completes_struct_associated_method_with_self() {
483 assert_debug_snapshot!(
484 do_reference_completion(
485 "
486 //- /lib.rs
487 /// A Struct
488 struct S;
489
490 impl S {
491 /// An associated method
492 fn m(&self) { }
493 }
494
495 fn foo() { let _ = S::<|> }
496 "
497 ),
498 @r###"
499 [
500 CompletionItem {
501 label: "m()",
502 source_range: [105; 105),
503 delete: [105; 105),
504 insert: "m()$0",
505 kind: Method,
506 lookup: "m",
507 detail: "fn m(&self)",
508 documentation: Documentation(
509 "An associated method",
510 ),
511 },
512 ]
513 "###
514 );
515 }
516
517 #[test]
486 fn completes_struct_associated_const() { 518 fn completes_struct_associated_const() {
487 assert_debug_snapshot!( 519 assert_debug_snapshot!(
488 do_reference_completion( 520 do_reference_completion(
@@ -869,7 +901,7 @@ mod tests {
869 @r###" 901 @r###"
870 [ 902 [
871 CompletionItem { 903 CompletionItem {
872 label: "foo!", 904 label: "foo!(…)",
873 source_range: [179; 179), 905 source_range: [179; 179),
874 delete: [179; 179), 906 delete: [179; 179),
875 insert: "foo!($0)", 907 insert: "foo!($0)",
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index f731e9b9a..4bccfbfed 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::completion::{ 3use crate::completion::{
4 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 4 completion_config::SnippetCap, completion_item::Builder, CompletionContext, CompletionItem,
5 CompletionKind, Completions, 5 CompletionItemKind, CompletionKind, Completions,
6}; 6};
7 7
8fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder { 8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 9 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label)
10 .insert_snippet(snippet) 10 .insert_snippet(cap, snippet)
11 .kind(CompletionItemKind::Snippet) 11 .kind(CompletionItemKind::Snippet)
12} 12}
13 13
@@ -15,17 +15,27 @@ pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { 15 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
16 return; 16 return;
17 } 17 }
18 let cap = match ctx.config.snippet_cap {
19 Some(it) => it,
20 None => return,
21 };
18 22
19 snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 23 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
20 snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 24 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
21} 25}
22 26
23pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 27pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
24 if !ctx.is_new_item { 28 if !ctx.is_new_item {
25 return; 29 return;
26 } 30 }
31 let cap = match ctx.config.snippet_cap {
32 Some(it) => it,
33 None => return,
34 };
35
27 snippet( 36 snippet(
28 ctx, 37 ctx,
38 cap,
29 "Test function", 39 "Test function",
30 "\ 40 "\
31#[test] 41#[test]
@@ -36,8 +46,8 @@ fn ${1:feature}() {
36 .lookup_by("tfn") 46 .lookup_by("tfn")
37 .add_to(acc); 47 .add_to(acc);
38 48
39 snippet(ctx, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 49 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc);
40 snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); 50 snippet(ctx, cap, "pub(crate)", "pub(crate) $0").add_to(acc);
41} 51}
42 52
43#[cfg(test)] 53#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 2ec0e7ce9..c39943252 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -122,7 +122,7 @@ fn add_function_impl(
122 ctx: &CompletionContext, 122 ctx: &CompletionContext,
123 func: &hir::Function, 123 func: &hir::Function,
124) { 124) {
125 let display = FunctionSignature::from_hir(ctx.db, *func); 125 let signature = FunctionSignature::from_hir(ctx.db, *func);
126 126
127 let fn_name = func.name(ctx.db).to_string(); 127 let fn_name = func.name(ctx.db).to_string();
128 128
@@ -141,12 +141,20 @@ fn add_function_impl(
141 } else { 141 } else {
142 CompletionItemKind::Function 142 CompletionItemKind::Function
143 }; 143 };
144
145 let snippet = format!("{} {{\n $0\n}}", display);
146
147 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); 144 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
148 145
149 builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); 146 match ctx.config.snippet_cap {
147 Some(cap) => {
148 let snippet = format!("{} {{\n $0\n}}", signature);
149 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
150 }
151 None => {
152 let header = format!("{} {{", signature);
153 builder.text_edit(TextEdit::replace(range, header))
154 }
155 }
156 .kind(completion_kind)
157 .add_to(acc);
150} 158}
151 159
152fn add_type_alias_impl( 160fn add_type_alias_impl(
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index ad5fdcc4e..638f86eda 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -4,20 +4,23 @@ use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::tested_by;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef};
7use ra_syntax::AstNode; 8use ra_syntax::AstNode;
8 9
9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !ctx.is_trivial_path { 11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const)
11 return;
12 }
13
14 if ctx.is_pat_binding_or_const
15 || ctx.record_lit_syntax.is_some() 12 || ctx.record_lit_syntax.is_some()
16 || ctx.record_pat_syntax.is_some() 13 || ctx.record_pat_syntax.is_some()
17 { 14 {
18 return; 15 return;
19 } 16 }
20 17
18 complete_enum_variants(acc, ctx);
19
20 if ctx.is_pat_binding_or_const {
21 return;
22 }
23
21 ctx.scope().process_all_names(&mut |name, res| { 24 ctx.scope().process_all_names(&mut |name, res| {
22 if ctx.use_item_syntax.is_some() { 25 if ctx.use_item_syntax.is_some() {
23 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 26 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
@@ -31,6 +34,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
31 }); 34 });
32} 35}
33 36
37fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) {
38 if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) {
39 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
40 let variants = enum_data.variants(ctx.db);
41 let module = enum_data.module(ctx.db);
42 for variant in variants {
43 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
44 // Variants with trivial paths are already added by the existing completion logic,
45 // so we should avoid adding these twice
46 if path.segments.len() > 1 {
47 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
48 }
49 }
50 }
51 }
52 }
53}
54
34#[cfg(test)] 55#[cfg(test)]
35mod tests { 56mod tests {
36 use insta::assert_debug_snapshot; 57 use insta::assert_debug_snapshot;
@@ -82,7 +103,7 @@ mod tests {
82 } 103 }
83 " 104 "
84 ), 105 ),
85 @r###"[]"### 106 @"[]"
86 ); 107 );
87 } 108 }
88 109
@@ -712,7 +733,7 @@ mod tests {
712 @r###" 733 @r###"
713 [ 734 [
714 CompletionItem { 735 CompletionItem {
715 label: "bar!", 736 label: "bar!(…)",
716 source_range: [252; 252), 737 source_range: [252; 252),
717 delete: [252; 252), 738 delete: [252; 252),
718 insert: "bar!($0)", 739 insert: "bar!($0)",
@@ -720,7 +741,7 @@ mod tests {
720 detail: "macro_rules! bar", 741 detail: "macro_rules! bar",
721 }, 742 },
722 CompletionItem { 743 CompletionItem {
723 label: "baz!", 744 label: "baz!(…)",
724 source_range: [252; 252), 745 source_range: [252; 252),
725 delete: [252; 252), 746 delete: [252; 252),
726 insert: "baz!($0)", 747 insert: "baz!($0)",
@@ -728,7 +749,7 @@ mod tests {
728 detail: "#[macro_export]\nmacro_rules! baz", 749 detail: "#[macro_export]\nmacro_rules! baz",
729 }, 750 },
730 CompletionItem { 751 CompletionItem {
731 label: "foo!", 752 label: "foo!(…)",
732 source_range: [252; 252), 753 source_range: [252; 252),
733 delete: [252; 252), 754 delete: [252; 252),
734 insert: "foo!($0)", 755 insert: "foo!($0)",
@@ -781,7 +802,7 @@ mod tests {
781 @r###" 802 @r###"
782 [ 803 [
783 CompletionItem { 804 CompletionItem {
784 label: "foo!", 805 label: "foo!(…)",
785 source_range: [49; 49), 806 source_range: [49; 49),
786 delete: [49; 49), 807 delete: [49; 49),
787 insert: "foo!($0)", 808 insert: "foo!($0)",
@@ -820,7 +841,7 @@ mod tests {
820 @r###" 841 @r###"
821 [ 842 [
822 CompletionItem { 843 CompletionItem {
823 label: "foo!", 844 label: "foo!(…)",
824 source_range: [57; 57), 845 source_range: [57; 57),
825 delete: [57; 57), 846 delete: [57; 57),
826 insert: "foo!($0)", 847 insert: "foo!($0)",
@@ -859,7 +880,7 @@ mod tests {
859 @r###" 880 @r###"
860 [ 881 [
861 CompletionItem { 882 CompletionItem {
862 label: "foo!", 883 label: "foo!(…)",
863 source_range: [50; 50), 884 source_range: [50; 50),
864 delete: [50; 50), 885 delete: [50; 50),
865 insert: "foo!($0)", 886 insert: "foo!($0)",
@@ -932,7 +953,7 @@ mod tests {
932 @r###" 953 @r###"
933 [ 954 [
934 CompletionItem { 955 CompletionItem {
935 label: "m!", 956 label: "m!(…)",
936 source_range: [145; 145), 957 source_range: [145; 145),
937 delete: [145; 145), 958 delete: [145; 145),
938 insert: "m!($0)", 959 insert: "m!($0)",
@@ -985,7 +1006,7 @@ mod tests {
985 @r###" 1006 @r###"
986 [ 1007 [
987 CompletionItem { 1008 CompletionItem {
988 label: "m!", 1009 label: "m!(…)",
989 source_range: [145; 146), 1010 source_range: [145; 146),
990 delete: [145; 146), 1011 delete: [145; 146),
991 insert: "m!($0)", 1012 insert: "m!($0)",
@@ -1038,7 +1059,7 @@ mod tests {
1038 @r###" 1059 @r###"
1039 [ 1060 [
1040 CompletionItem { 1061 CompletionItem {
1041 label: "m!", 1062 label: "m!(…)",
1042 source_range: [145; 146), 1063 source_range: [145; 146),
1043 delete: [145; 146), 1064 delete: [145; 146),
1044 insert: "m!($0)", 1065 insert: "m!($0)",
@@ -1109,4 +1130,182 @@ mod tests {
1109 "### 1130 "###
1110 ); 1131 );
1111 } 1132 }
1133 #[test]
1134 fn completes_enum_variant_matcharm() {
1135 assert_debug_snapshot!(
1136 do_reference_completion(
1137 r"
1138 enum Foo {
1139 Bar,
1140 Baz,
1141 Quux
1142 }
1143
1144 fn main() {
1145 let foo = Foo::Quux;
1146
1147 match foo {
1148 Qu<|>
1149 }
1150 }
1151 "
1152 ),
1153 @r###"
1154 [
1155 CompletionItem {
1156 label: "Foo",
1157 source_range: [248; 250),
1158 delete: [248; 250),
1159 insert: "Foo",
1160 kind: Enum,
1161 },
1162 CompletionItem {
1163 label: "Foo::Bar",
1164 source_range: [248; 250),
1165 delete: [248; 250),
1166 insert: "Foo::Bar",
1167 kind: EnumVariant,
1168 detail: "()",
1169 },
1170 CompletionItem {
1171 label: "Foo::Baz",
1172 source_range: [248; 250),
1173 delete: [248; 250),
1174 insert: "Foo::Baz",
1175 kind: EnumVariant,
1176 detail: "()",
1177 },
1178 CompletionItem {
1179 label: "Foo::Quux",
1180 source_range: [248; 250),
1181 delete: [248; 250),
1182 insert: "Foo::Quux",
1183 kind: EnumVariant,
1184 detail: "()",
1185 },
1186 ]
1187 "###
1188 )
1189 }
1190
1191 #[test]
1192 fn completes_enum_variant_iflet() {
1193 assert_debug_snapshot!(
1194 do_reference_completion(
1195 r"
1196 enum Foo {
1197 Bar,
1198 Baz,
1199 Quux
1200 }
1201
1202 fn main() {
1203 let foo = Foo::Quux;
1204
1205 if let Qu<|> = foo {
1206
1207 }
1208 }
1209 "
1210 ),
1211 @r###"
1212 [
1213 CompletionItem {
1214 label: "Foo",
1215 source_range: [219; 221),
1216 delete: [219; 221),
1217 insert: "Foo",
1218 kind: Enum,
1219 },
1220 CompletionItem {
1221 label: "Foo::Bar",
1222 source_range: [219; 221),
1223 delete: [219; 221),
1224 insert: "Foo::Bar",
1225 kind: EnumVariant,
1226 detail: "()",
1227 },
1228 CompletionItem {
1229 label: "Foo::Baz",
1230 source_range: [219; 221),
1231 delete: [219; 221),
1232 insert: "Foo::Baz",
1233 kind: EnumVariant,
1234 detail: "()",
1235 },
1236 CompletionItem {
1237 label: "Foo::Quux",
1238 source_range: [219; 221),
1239 delete: [219; 221),
1240 insert: "Foo::Quux",
1241 kind: EnumVariant,
1242 detail: "()",
1243 },
1244 ]
1245 "###
1246 )
1247 }
1248
1249 #[test]
1250 fn completes_enum_variant_basic_expr() {
1251 assert_debug_snapshot!(
1252 do_reference_completion(
1253 r"
1254 enum Foo {
1255 Bar,
1256 Baz,
1257 Quux
1258 }
1259
1260 fn main() {
1261 let foo: Foo = Q<|>
1262 }
1263 "
1264 ),
1265 @r###"
1266 [
1267 CompletionItem {
1268 label: "Foo",
1269 source_range: [185; 186),
1270 delete: [185; 186),
1271 insert: "Foo",
1272 kind: Enum,
1273 },
1274 CompletionItem {
1275 label: "Foo::Bar",
1276 source_range: [185; 186),
1277 delete: [185; 186),
1278 insert: "Foo::Bar",
1279 kind: EnumVariant,
1280 detail: "()",
1281 },
1282 CompletionItem {
1283 label: "Foo::Baz",
1284 source_range: [185; 186),
1285 delete: [185; 186),
1286 insert: "Foo::Baz",
1287 kind: EnumVariant,
1288 detail: "()",
1289 },
1290 CompletionItem {
1291 label: "Foo::Quux",
1292 source_range: [185; 186),
1293 delete: [185; 186),
1294 insert: "Foo::Quux",
1295 kind: EnumVariant,
1296 detail: "()",
1297 },
1298 CompletionItem {
1299 label: "main()",
1300 source_range: [185; 186),
1301 delete: [185; 186),
1302 insert: "main()$0",
1303 kind: Function,
1304 lookup: "main",
1305 detail: "fn main()",
1306 },
1307 ]
1308 "###
1309 )
1310 }
1112} 1311}
diff --git a/crates/ra_ide/src/completion/completion_config.rs b/crates/ra_ide/src/completion/completion_config.rs
new file mode 100644
index 000000000..71b49ace8
--- /dev/null
+++ b/crates/ra_ide/src/completion/completion_config.rs
@@ -0,0 +1,35 @@
1//! Settings for tweaking completion.
2//!
3//! The fun thing here is `SnippetCap` -- this type can only be created in this
4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to.
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CompletionConfig {
9 pub enable_postfix_completions: bool,
10 pub add_call_parenthesis: bool,
11 pub add_call_argument_snippets: bool,
12 pub snippet_cap: Option<SnippetCap>,
13}
14
15impl CompletionConfig {
16 pub fn allow_snippets(&mut self, yes: bool) {
17 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
18 }
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct SnippetCap {
23 _private: (),
24}
25
26impl Default for CompletionConfig {
27 fn default() -> Self {
28 CompletionConfig {
29 enable_postfix_completions: true,
30 add_call_parenthesis: true,
31 add_call_argument_snippets: true,
32 snippet_cap: Some(SnippetCap { _private: () }),
33 }
34 }
35}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 8b3401595..37880448a 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Semantics, SemanticsScope}; 3use hir::{Semantics, SemanticsScope, Type};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -31,7 +31,10 @@ pub(crate) struct CompletionContext<'a> {
31 pub(super) use_item_syntax: Option<ast::UseItem>, 31 pub(super) use_item_syntax: Option<ast::UseItem>,
32 pub(super) record_lit_syntax: Option<ast::RecordLit>, 32 pub(super) record_lit_syntax: Option<ast::RecordLit>,
33 pub(super) record_pat_syntax: Option<ast::RecordPat>, 33 pub(super) record_pat_syntax: Option<ast::RecordPat>,
34 pub(super) record_field_syntax: Option<ast::RecordField>,
34 pub(super) impl_def: Option<ast::ImplDef>, 35 pub(super) impl_def: Option<ast::ImplDef>,
36 /// FIXME: `ActiveParameter` is string-based, which is very wrong
37 pub(super) active_parameter: Option<ActiveParameter>,
35 pub(super) is_param: bool, 38 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 39 /// If a name-binding or reference to a const in a pattern.
37 /// Irrefutable patterns (like let) are excluded. 40 /// Irrefutable patterns (like let) are excluded.
@@ -54,6 +57,7 @@ pub(crate) struct CompletionContext<'a> {
54 pub(super) is_macro_call: bool, 57 pub(super) is_macro_call: bool,
55 pub(super) is_path_type: bool, 58 pub(super) is_path_type: bool,
56 pub(super) has_type_args: bool, 59 pub(super) has_type_args: bool,
60 pub(super) is_attribute: bool,
57} 61}
58 62
59impl<'a> CompletionContext<'a> { 63impl<'a> CompletionContext<'a> {
@@ -94,7 +98,9 @@ impl<'a> CompletionContext<'a> {
94 use_item_syntax: None, 98 use_item_syntax: None,
95 record_lit_syntax: None, 99 record_lit_syntax: None,
96 record_pat_syntax: None, 100 record_pat_syntax: None,
101 record_field_syntax: None,
97 impl_def: None, 102 impl_def: None,
103 active_parameter: ActiveParameter::at(db, position),
98 is_param: false, 104 is_param: false,
99 is_pat_binding_or_const: false, 105 is_pat_binding_or_const: false,
100 is_trivial_path: false, 106 is_trivial_path: false,
@@ -108,6 +114,7 @@ impl<'a> CompletionContext<'a> {
108 is_path_type: false, 114 is_path_type: false,
109 has_type_args: false, 115 has_type_args: false,
110 dot_receiver_is_ambiguous_float_literal: false, 116 dot_receiver_is_ambiguous_float_literal: false,
117 is_attribute: false,
111 }; 118 };
112 119
113 let mut original_file = original_file.syntax().clone(); 120 let mut original_file = original_file.syntax().clone();
@@ -168,6 +175,17 @@ impl<'a> CompletionContext<'a> {
168 self.sema.scope_at_offset(&self.token.parent(), self.offset) 175 self.sema.scope_at_offset(&self.token.parent(), self.offset)
169 } 176 }
170 177
178 pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
179 for ancestor in node.ancestors() {
180 if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
181 return self.sema.type_of_pat(&pat);
182 } else if let Some(expr) = ast::Expr::cast(ancestor) {
183 return self.sema.type_of_expr(&expr);
184 }
185 }
186 None
187 }
188
171 fn fill( 189 fn fill(
172 &mut self, 190 &mut self,
173 original_file: &SyntaxNode, 191 original_file: &SyntaxNode,
@@ -268,6 +286,14 @@ impl<'a> CompletionContext<'a> {
268 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 286 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
269 .find_map(ast::FnDef::cast); 287 .find_map(ast::FnDef::cast);
270 288
289 self.record_field_syntax = self
290 .sema
291 .ancestors_with_macros(self.token.parent())
292 .take_while(|it| {
293 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
294 })
295 .find_map(ast::RecordField::cast);
296
271 let parent = match name_ref.syntax().parent() { 297 let parent = match name_ref.syntax().parent() {
272 Some(it) => it, 298 Some(it) => it,
273 None => return, 299 None => return,
@@ -282,6 +308,7 @@ impl<'a> CompletionContext<'a> {
282 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 308 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
283 .is_some(); 309 .is_some();
284 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 310 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
311 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
285 312
286 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 313 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
287 self.has_type_args = segment.type_arg_list().is_some(); 314 self.has_type_args = segment.type_arg_list().is_some();
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index bc0f1aff5..5936fb8f7 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -2,6 +2,7 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use super::completion_config::SnippetCap;
5use hir::Documentation; 6use hir::Documentation;
6use ra_syntax::TextRange; 7use ra_syntax::TextRange;
7use ra_text_edit::TextEdit; 8use ra_text_edit::TextEdit;
@@ -51,6 +52,9 @@ pub struct CompletionItem {
51 /// If completing a function call, ask the editor to show parameter popup 52 /// If completing a function call, ask the editor to show parameter popup
52 /// after completion. 53 /// after completion.
53 trigger_call_info: bool, 54 trigger_call_info: bool,
55
56 /// Score is useful to pre select or display in better order completion items
57 score: Option<CompletionScore>,
54} 58}
55 59
56// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 60// We use custom debug for CompletionItem to make `insta`'s diffs more readable.
@@ -80,6 +84,9 @@ impl fmt::Debug for CompletionItem {
80 if self.deprecated { 84 if self.deprecated {
81 s.field("deprecated", &true); 85 s.field("deprecated", &true);
82 } 86 }
87 if let Some(score) = &self.score {
88 s.field("score", score);
89 }
83 if self.trigger_call_info { 90 if self.trigger_call_info {
84 s.field("trigger_call_info", &true); 91 s.field("trigger_call_info", &true);
85 } 92 }
@@ -87,6 +94,14 @@ impl fmt::Debug for CompletionItem {
87 } 94 }
88} 95}
89 96
97#[derive(Debug, Clone, Copy)]
98pub enum CompletionScore {
99 /// If only type match
100 TypeMatch,
101 /// If type and name match
102 TypeAndNameMatch,
103}
104
90#[derive(Debug, Clone, Copy, PartialEq, Eq)] 105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum CompletionItemKind { 106pub enum CompletionItemKind {
92 Snippet, 107 Snippet,
@@ -106,6 +121,7 @@ pub enum CompletionItemKind {
106 Method, 121 Method,
107 TypeParam, 122 TypeParam,
108 Macro, 123 Macro,
124 Attribute,
109} 125}
110 126
111#[derive(Debug, PartialEq, Eq, Copy, Clone)] 127#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -119,6 +135,7 @@ pub(crate) enum CompletionKind {
119 Snippet, 135 Snippet,
120 Postfix, 136 Postfix,
121 BuiltinType, 137 BuiltinType,
138 Attribute,
122} 139}
123 140
124#[derive(Debug, PartialEq, Eq, Copy, Clone)] 141#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -147,6 +164,7 @@ impl CompletionItem {
147 text_edit: None, 164 text_edit: None,
148 deprecated: None, 165 deprecated: None,
149 trigger_call_info: None, 166 trigger_call_info: None,
167 score: None,
150 } 168 }
151 } 169 }
152 /// What user sees in pop-up in the UI. 170 /// What user sees in pop-up in the UI.
@@ -175,7 +193,7 @@ impl CompletionItem {
175 } 193 }
176 /// What string is used for filtering. 194 /// What string is used for filtering.
177 pub fn lookup(&self) -> &str { 195 pub fn lookup(&self) -> &str {
178 self.lookup.as_deref().unwrap_or_else(|| self.label()) 196 self.lookup.as_deref().unwrap_or(&self.label)
179 } 197 }
180 198
181 pub fn kind(&self) -> Option<CompletionItemKind> { 199 pub fn kind(&self) -> Option<CompletionItemKind> {
@@ -186,6 +204,10 @@ impl CompletionItem {
186 self.deprecated 204 self.deprecated
187 } 205 }
188 206
207 pub fn score(&self) -> Option<CompletionScore> {
208 self.score
209 }
210
189 pub fn trigger_call_info(&self) -> bool { 211 pub fn trigger_call_info(&self) -> bool {
190 self.trigger_call_info 212 self.trigger_call_info
191 } 213 }
@@ -206,6 +228,7 @@ pub(crate) struct Builder {
206 text_edit: Option<TextEdit>, 228 text_edit: Option<TextEdit>,
207 deprecated: Option<bool>, 229 deprecated: Option<bool>,
208 trigger_call_info: Option<bool>, 230 trigger_call_info: Option<bool>,
231 score: Option<CompletionScore>,
209} 232}
210 233
211impl Builder { 234impl Builder {
@@ -235,6 +258,7 @@ impl Builder {
235 completion_kind: self.completion_kind, 258 completion_kind: self.completion_kind,
236 deprecated: self.deprecated.unwrap_or(false), 259 deprecated: self.deprecated.unwrap_or(false),
237 trigger_call_info: self.trigger_call_info.unwrap_or(false), 260 trigger_call_info: self.trigger_call_info.unwrap_or(false),
261 score: self.score,
238 } 262 }
239 } 263 }
240 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 264 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -249,7 +273,11 @@ impl Builder {
249 self.insert_text = Some(insert_text.into()); 273 self.insert_text = Some(insert_text.into());
250 self 274 self
251 } 275 }
252 pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { 276 pub(crate) fn insert_snippet(
277 mut self,
278 _cap: SnippetCap,
279 snippet: impl Into<String>,
280 ) -> Builder {
253 self.insert_text_format = InsertTextFormat::Snippet; 281 self.insert_text_format = InsertTextFormat::Snippet;
254 self.insert_text(snippet) 282 self.insert_text(snippet)
255 } 283 }
@@ -261,7 +289,7 @@ impl Builder {
261 self.text_edit = Some(edit); 289 self.text_edit = Some(edit);
262 self 290 self
263 } 291 }
264 pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { 292 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder {
265 self.insert_text_format = InsertTextFormat::Snippet; 293 self.insert_text_format = InsertTextFormat::Snippet;
266 self.text_edit(edit) 294 self.text_edit(edit)
267 } 295 }
@@ -285,6 +313,10 @@ impl Builder {
285 self.deprecated = Some(deprecated); 313 self.deprecated = Some(deprecated);
286 self 314 self
287 } 315 }
316 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
317 self.score = Some(score);
318 self
319 }
288 pub(crate) fn trigger_call_info(mut self) -> Builder { 320 pub(crate) fn trigger_call_info(mut self) -> Builder {
289 self.trigger_call_info = Some(true); 321 self.trigger_call_info = Some(true);
290 self 322 self
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 55f75b15a..6a6ddc7bd 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -11,7 +11,7 @@ use crate::{
11 CompletionKind, Completions, 11 CompletionKind, Completions,
12 }, 12 },
13 display::{const_label, macro_label, type_label, FunctionSignature}, 13 display::{const_label, macro_label, type_label, FunctionSignature},
14 RootDatabase, 14 CompletionScore, RootDatabase,
15}; 15};
16 16
17impl Completions { 17impl Completions {
@@ -22,16 +22,20 @@ impl Completions {
22 ty: &Type, 22 ty: &Type,
23 ) { 23 ) {
24 let is_deprecated = is_deprecated(field, ctx.db); 24 let is_deprecated = is_deprecated(field, ctx.db);
25 CompletionItem::new( 25 let ty = ty.display(ctx.db).to_string();
26 CompletionKind::Reference, 26 let name = field.name(ctx.db);
27 ctx.source_range(), 27 let mut completion_item =
28 field.name(ctx.db).to_string(), 28 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
29 ) 29 .kind(CompletionItemKind::Field)
30 .kind(CompletionItemKind::Field) 30 .detail(ty.clone())
31 .detail(ty.display(ctx.db).to_string()) 31 .set_documentation(field.docs(ctx.db))
32 .set_documentation(field.docs(ctx.db)) 32 .set_deprecated(is_deprecated);
33 .set_deprecated(is_deprecated) 33
34 .add_to(self); 34 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
35 completion_item = completion_item.set_score(score);
36 }
37
38 completion_item.add_to(self);
35 } 39 }
36 40
37 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { 41 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
@@ -110,44 +114,23 @@ impl Completions {
110 114
111 // Add `<>` for generic types 115 // Add `<>` for generic types
112 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
113 let has_non_default_type_params = match resolution { 117 if let Some(cap) = ctx.config.snippet_cap {
114 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 118 let has_non_default_type_params = match resolution {
115 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 119 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
116 _ => false, 120 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
117 }; 121 _ => false,
118 if has_non_default_type_params { 122 };
119 tested_by!(inserts_angle_brackets_for_generics); 123 if has_non_default_type_params {
120 completion_item = completion_item 124 tested_by!(inserts_angle_brackets_for_generics);
121 .lookup_by(local_name.clone()) 125 completion_item = completion_item
122 .label(format!("{}<…>", local_name)) 126 .lookup_by(local_name.clone())
123 .insert_snippet(format!("{}<$0>", local_name)); 127 .label(format!("{}<…>", local_name))
124 } 128 .insert_snippet(cap, format!("{}<$0>", local_name));
125 }
126
127 completion_item.kind(kind).set_documentation(docs).add_to(self)
128 }
129
130 fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
131 let mut votes = [0, 0, 0];
132 for (idx, s) in docs.match_indices(&macro_name) {
133 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
134 // Ensure to match the full word
135 if after.starts_with('!')
136 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
137 {
138 // It may have spaces before the braces like `foo! {}`
139 match after[1..].chars().find(|&c| !c.is_whitespace()) {
140 Some('{') => votes[0] += 1,
141 Some('[') => votes[1] += 1,
142 Some('(') => votes[2] += 1,
143 _ => {}
144 } 129 }
145 } 130 }
146 } 131 }
147 132
148 // Insert a space before `{}`. 133 completion_item.kind(kind).set_documentation(docs).add_to(self)
149 // We prefer the last one when some votes equal.
150 *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1
151 } 134 }
152 135
153 pub(crate) fn add_macro( 136 pub(crate) fn add_macro(
@@ -156,6 +139,12 @@ impl Completions {
156 name: Option<String>, 139 name: Option<String>,
157 macro_: hir::MacroDef, 140 macro_: hir::MacroDef,
158 ) { 141 ) {
142 // FIXME: Currently proc-macro do not have ast-node,
143 // such that it does not have source
144 if macro_.is_proc_macro() {
145 return;
146 }
147
159 let name = match name { 148 let name = match name {
160 Some(it) => it, 149 Some(it) => it,
161 None => return, 150 None => return,
@@ -165,22 +154,31 @@ impl Completions {
165 let detail = macro_label(&ast_node); 154 let detail = macro_label(&ast_node);
166 155
167 let docs = macro_.docs(ctx.db); 156 let docs = macro_.docs(ctx.db);
168 let macro_declaration = format!("{}!", name);
169 157
170 let mut builder = 158 let mut builder = CompletionItem::new(
171 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), &macro_declaration) 159 CompletionKind::Reference,
172 .kind(CompletionItemKind::Macro) 160 ctx.source_range(),
173 .set_documentation(docs.clone()) 161 &format!("{}!", name),
174 .set_deprecated(is_deprecated(macro_, ctx.db)) 162 )
175 .detail(detail); 163 .kind(CompletionItemKind::Macro)
164 .set_documentation(docs.clone())
165 .set_deprecated(is_deprecated(macro_, ctx.db))
166 .detail(detail);
176 167
177 builder = if ctx.use_item_syntax.is_some() || ctx.is_macro_call { 168 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
178 tested_by!(dont_insert_macro_call_parens_unncessary); 169 builder = match ctx.config.snippet_cap {
179 builder.insert_text(name) 170 Some(cap) if needs_bang => {
180 } else { 171 let docs = docs.as_ref().map_or("", |s| s.as_str());
181 let macro_braces_to_insert = 172 let (bra, ket) = guess_macro_braces(&name, docs);
182 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); 173 builder
183 builder.insert_snippet(macro_declaration + macro_braces_to_insert) 174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket))
176 }
177 None if needs_bang => builder.insert_text(format!("{}!", name)),
178 _ => {
179 tested_by!(dont_insert_macro_call_parens_unncessary);
180 builder.insert_text(name)
181 }
184 }; 182 };
185 183
186 self.add(builder); 184 self.add(builder);
@@ -294,6 +292,42 @@ impl Completions {
294 } 292 }
295} 293}
296 294
295pub(crate) fn compute_score(
296 ctx: &CompletionContext,
297 // FIXME: this definitely should be a `Type`
298 ty: &str,
299 name: &str,
300) -> Option<CompletionScore> {
301 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
302 tested_by!(test_struct_field_completion_in_record_lit);
303 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
304 (
305 struct_field.name(ctx.db).to_string(),
306 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
307 )
308 } else if let Some(active_parameter) = &ctx.active_parameter {
309 tested_by!(test_struct_field_completion_in_func_call);
310 (active_parameter.name.clone(), active_parameter.ty.clone())
311 } else {
312 return None;
313 };
314
315 // Compute score
316 // For the same type
317 if &active_type != ty {
318 return None;
319 }
320
321 let mut res = CompletionScore::TypeMatch;
322
323 // If same type + same name then go top position
324 if active_name == name {
325 res = CompletionScore::TypeAndNameMatch
326 }
327
328 Some(res)
329}
330
297enum Params { 331enum Params {
298 Named(Vec<String>), 332 Named(Vec<String>),
299 Anonymous(usize), 333 Anonymous(usize),
@@ -320,6 +354,10 @@ impl Builder {
320 if ctx.use_item_syntax.is_some() || ctx.is_call { 354 if ctx.use_item_syntax.is_some() || ctx.is_call {
321 return self; 355 return self;
322 } 356 }
357 let cap = match ctx.config.snippet_cap {
358 Some(it) => it,
359 None => return self,
360 };
323 // If not an import, add parenthesis automatically. 361 // If not an import, add parenthesis automatically.
324 tested_by!(inserts_parens_for_function_calls); 362 tested_by!(inserts_parens_for_function_calls);
325 363
@@ -341,7 +379,7 @@ impl Builder {
341 379
342 (snippet, format!("{}(…)", name)) 380 (snippet, format!("{}(…)", name))
343 }; 381 };
344 self.lookup_by(name).label(label).insert_snippet(snippet) 382 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
345 } 383 }
346} 384}
347 385
@@ -349,6 +387,34 @@ fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
349 node.attrs(db).by_key("deprecated").exists() 387 node.attrs(db).by_key("deprecated").exists()
350} 388}
351 389
390fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
391 let mut votes = [0, 0, 0];
392 for (idx, s) in docs.match_indices(&macro_name) {
393 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
394 // Ensure to match the full word
395 if after.starts_with('!')
396 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
397 {
398 // It may have spaces before the braces like `foo! {}`
399 match after[1..].chars().find(|&c| !c.is_whitespace()) {
400 Some('{') => votes[0] += 1,
401 Some('[') => votes[1] += 1,
402 Some('(') => votes[2] += 1,
403 _ => {}
404 }
405 }
406 }
407
408 // Insert a space before `{}`.
409 // We prefer the last one when some votes equal.
410 let (_vote, (bra, ket)) = votes
411 .iter()
412 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
413 .max_by_key(|&(&vote, _)| vote)
414 .unwrap();
415 (*bra, *ket)
416}
417
352#[cfg(test)] 418#[cfg(test)]
353mod tests { 419mod tests {
354 use insta::assert_debug_snapshot; 420 use insta::assert_debug_snapshot;
@@ -1025,4 +1091,237 @@ mod tests {
1025 "### 1091 "###
1026 ); 1092 );
1027 } 1093 }
1094
1095 #[test]
1096 fn test_struct_field_completion_in_func_call() {
1097 covers!(test_struct_field_completion_in_func_call);
1098 assert_debug_snapshot!(
1099 do_reference_completion(
1100 r"
1101 struct A { another_field: i64, the_field: u32, my_string: String }
1102 fn test(my_param: u32) -> u32 { my_param }
1103 fn foo(a: A) {
1104 test(a.<|>)
1105 }
1106 ",
1107 ),
1108 @r###"
1109 [
1110 CompletionItem {
1111 label: "another_field",
1112 source_range: [201; 201),
1113 delete: [201; 201),
1114 insert: "another_field",
1115 kind: Field,
1116 detail: "i64",
1117 },
1118 CompletionItem {
1119 label: "my_string",
1120 source_range: [201; 201),
1121 delete: [201; 201),
1122 insert: "my_string",
1123 kind: Field,
1124 detail: "{unknown}",
1125 },
1126 CompletionItem {
1127 label: "the_field",
1128 source_range: [201; 201),
1129 delete: [201; 201),
1130 insert: "the_field",
1131 kind: Field,
1132 detail: "u32",
1133 score: TypeMatch,
1134 },
1135 ]
1136 "###
1137 );
1138 }
1139
1140 #[test]
1141 fn test_struct_field_completion_in_func_call_with_type_and_name() {
1142 assert_debug_snapshot!(
1143 do_reference_completion(
1144 r"
1145 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1146 fn test(the_field: u32) -> u32 { the_field }
1147 fn foo(a: A) {
1148 test(a.<|>)
1149 }
1150 ",
1151 ),
1152 @r###"
1153 [
1154 CompletionItem {
1155 label: "another_field",
1156 source_range: [208; 208),
1157 delete: [208; 208),
1158 insert: "another_field",
1159 kind: Field,
1160 detail: "i64",
1161 },
1162 CompletionItem {
1163 label: "another_good_type",
1164 source_range: [208; 208),
1165 delete: [208; 208),
1166 insert: "another_good_type",
1167 kind: Field,
1168 detail: "u32",
1169 score: TypeMatch,
1170 },
1171 CompletionItem {
1172 label: "the_field",
1173 source_range: [208; 208),
1174 delete: [208; 208),
1175 insert: "the_field",
1176 kind: Field,
1177 detail: "u32",
1178 score: TypeAndNameMatch,
1179 },
1180 ]
1181 "###
1182 );
1183 }
1184
1185 #[test]
1186 fn test_struct_field_completion_in_record_lit() {
1187 covers!(test_struct_field_completion_in_func_call);
1188 assert_debug_snapshot!(
1189 do_reference_completion(
1190 r"
1191 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1192 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1193 fn foo(a: A) {
1194 let b = B {
1195 the_field: a.<|>
1196 };
1197 }
1198 ",
1199 ),
1200 @r###"
1201 [
1202 CompletionItem {
1203 label: "another_field",
1204 source_range: [270; 270),
1205 delete: [270; 270),
1206 insert: "another_field",
1207 kind: Field,
1208 detail: "i64",
1209 },
1210 CompletionItem {
1211 label: "another_good_type",
1212 source_range: [270; 270),
1213 delete: [270; 270),
1214 insert: "another_good_type",
1215 kind: Field,
1216 detail: "u32",
1217 score: TypeMatch,
1218 },
1219 CompletionItem {
1220 label: "the_field",
1221 source_range: [270; 270),
1222 delete: [270; 270),
1223 insert: "the_field",
1224 kind: Field,
1225 detail: "u32",
1226 score: TypeAndNameMatch,
1227 },
1228 ]
1229 "###
1230 );
1231 }
1232
1233 #[test]
1234 fn test_struct_field_completion_in_record_lit_and_fn_call() {
1235 assert_debug_snapshot!(
1236 do_reference_completion(
1237 r"
1238 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1239 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1240 fn test(the_field: i64) -> i64 { the_field }
1241 fn foo(a: A) {
1242 let b = B {
1243 the_field: test(a.<|>)
1244 };
1245 }
1246 ",
1247 ),
1248 @r###"
1249 [
1250 CompletionItem {
1251 label: "another_field",
1252 source_range: [336; 336),
1253 delete: [336; 336),
1254 insert: "another_field",
1255 kind: Field,
1256 detail: "i64",
1257 score: TypeMatch,
1258 },
1259 CompletionItem {
1260 label: "another_good_type",
1261 source_range: [336; 336),
1262 delete: [336; 336),
1263 insert: "another_good_type",
1264 kind: Field,
1265 detail: "u32",
1266 },
1267 CompletionItem {
1268 label: "the_field",
1269 source_range: [336; 336),
1270 delete: [336; 336),
1271 insert: "the_field",
1272 kind: Field,
1273 detail: "u32",
1274 },
1275 ]
1276 "###
1277 );
1278 }
1279
1280 #[test]
1281 fn test_struct_field_completion_in_fn_call_and_record_lit() {
1282 assert_debug_snapshot!(
1283 do_reference_completion(
1284 r"
1285 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
1286 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
1287 fn test(the_field: i64) -> i64 { the_field }
1288 fn foo(a: A) {
1289 test(B {
1290 the_field: a.<|>
1291 });
1292 }
1293 ",
1294 ),
1295 @r###"
1296 [
1297 CompletionItem {
1298 label: "another_field",
1299 source_range: [328; 328),
1300 delete: [328; 328),
1301 insert: "another_field",
1302 kind: Field,
1303 detail: "i64",
1304 },
1305 CompletionItem {
1306 label: "another_good_type",
1307 source_range: [328; 328),
1308 delete: [328; 328),
1309 insert: "another_good_type",
1310 kind: Field,
1311 detail: "u32",
1312 score: TypeMatch,
1313 },
1314 CompletionItem {
1315 label: "the_field",
1316 source_range: [328; 328),
1317 delete: [328; 328),
1318 insert: "the_field",
1319 kind: Field,
1320 detail: "u32",
1321 score: TypeAndNameMatch,
1322 },
1323 ]
1324 "###
1325 );
1326 }
1028} 1327}