aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/complete_scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/complete_scope.rs')
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs876
1 files changed, 876 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
new file mode 100644
index 000000000..d5739b58a
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -0,0 +1,876 @@
1//! FIXME: write short doc here
2
3use ra_assists::auto_import_text_edit;
4use ra_syntax::{ast, AstNode, SmolStr};
5use ra_text_edit::TextEditBuilder;
6use rustc_hash::FxHashMap;
7
8use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9
10pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
11 if !ctx.is_trivial_path {
12 return;
13 }
14
15 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
16 acc.add_resolution(ctx, name.to_string(), &res)
17 });
18
19 // auto-import
20 // We fetch ident from the original file, because we need to pre-filter auto-imports
21 if ast::NameRef::cast(ctx.token.parent()).is_some() {
22 let import_resolver = ImportResolver::new();
23 let import_names = import_resolver.all_names(ctx.token.text());
24 import_names.into_iter().for_each(|(name, path)| {
25 let edit = {
26 let mut builder = TextEditBuilder::default();
27 builder.replace(ctx.source_range(), name.to_string());
28 auto_import_text_edit(
29 &ctx.token.parent(),
30 &ctx.token.parent(),
31 &path,
32 &mut builder,
33 );
34 builder.finish()
35 };
36
37 // Hack: copied this check form conv.rs beacause auto import can produce edits
38 // that invalidate assert in conv_with.
39 if edit
40 .as_atoms()
41 .iter()
42 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
43 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
44 {
45 CompletionItem::new(
46 CompletionKind::Reference,
47 ctx.source_range(),
48 build_import_label(&name, &path),
49 )
50 .text_edit(edit)
51 .add_to(acc);
52 }
53 });
54 }
55}
56
57fn build_import_label(name: &str, path: &[SmolStr]) -> String {
58 let mut buf = String::with_capacity(64);
59 buf.push_str(name);
60 buf.push_str(" (");
61 fmt_import_path(path, &mut buf);
62 buf.push_str(")");
63 buf
64}
65
66fn fmt_import_path(path: &[SmolStr], buf: &mut String) {
67 let mut segments = path.iter();
68 if let Some(s) = segments.next() {
69 buf.push_str(&s);
70 }
71 for s in segments {
72 buf.push_str("::");
73 buf.push_str(&s);
74 }
75}
76
77#[derive(Debug, Clone, Default)]
78pub(crate) struct ImportResolver {
79 // todo: use fst crate or something like that
80 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
81}
82
83impl ImportResolver {
84 pub(crate) fn new() -> Self {
85 let dummy_names = vec![
86 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
87 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
88 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
89 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
90 (
91 SmolStr::new("Debug"),
92 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
93 ),
94 (
95 SmolStr::new("Display"),
96 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
97 ),
98 (
99 SmolStr::new("Hash"),
100 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
101 ),
102 (
103 SmolStr::new("Hasher"),
104 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
105 ),
106 (
107 SmolStr::new("Iterator"),
108 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
109 ),
110 ];
111
112 ImportResolver { dummy_names }
113 }
114
115 // Returns a map of importable items filtered by name.
116 // The map associates item name with its full path.
117 // todo: should return Resolutions
118 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
119 if name.len() > 1 {
120 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
121 } else {
122 FxHashMap::default()
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use crate::completion::{do_completion, CompletionItem, CompletionKind};
130 use insta::assert_debug_snapshot;
131
132 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
133 do_completion(code, CompletionKind::Reference)
134 }
135
136 #[test]
137 fn completes_bindings_from_let() {
138 assert_debug_snapshot!(
139 do_reference_completion(
140 r"
141 fn quux(x: i32) {
142 let y = 92;
143 1 + <|>;
144 let z = ();
145 }
146 "
147 ),
148 @r###"
149 [
150 CompletionItem {
151 label: "quux(…)",
152 source_range: [91; 91),
153 delete: [91; 91),
154 insert: "quux($0)",
155 kind: Function,
156 lookup: "quux",
157 detail: "fn quux(x: i32)",
158 },
159 CompletionItem {
160 label: "x",
161 source_range: [91; 91),
162 delete: [91; 91),
163 insert: "x",
164 kind: Binding,
165 detail: "i32",
166 },
167 CompletionItem {
168 label: "y",
169 source_range: [91; 91),
170 delete: [91; 91),
171 insert: "y",
172 kind: Binding,
173 detail: "i32",
174 },
175 ]
176 "###
177 );
178 }
179
180 #[test]
181 fn completes_bindings_from_if_let() {
182 assert_debug_snapshot!(
183 do_reference_completion(
184 r"
185 fn quux() {
186 if let Some(x) = foo() {
187 let y = 92;
188 };
189 if let Some(a) = bar() {
190 let b = 62;
191 1 + <|>
192 }
193 }
194 "
195 ),
196 @r###"
197 [
198 CompletionItem {
199 label: "a",
200 source_range: [242; 242),
201 delete: [242; 242),
202 insert: "a",
203 kind: Binding,
204 },
205 CompletionItem {
206 label: "b",
207 source_range: [242; 242),
208 delete: [242; 242),
209 insert: "b",
210 kind: Binding,
211 detail: "i32",
212 },
213 CompletionItem {
214 label: "quux()",
215 source_range: [242; 242),
216 delete: [242; 242),
217 insert: "quux()$0",
218 kind: Function,
219 lookup: "quux",
220 detail: "fn quux()",
221 },
222 ]
223 "###
224 );
225 }
226
227 #[test]
228 fn completes_bindings_from_for() {
229 assert_debug_snapshot!(
230 do_reference_completion(
231 r"
232 fn quux() {
233 for x in &[1, 2, 3] {
234 <|>
235 }
236 }
237 "
238 ),
239 @r###"
240 [
241 CompletionItem {
242 label: "quux()",
243 source_range: [95; 95),
244 delete: [95; 95),
245 insert: "quux()$0",
246 kind: Function,
247 lookup: "quux",
248 detail: "fn quux()",
249 },
250 CompletionItem {
251 label: "x",
252 source_range: [95; 95),
253 delete: [95; 95),
254 insert: "x",
255 kind: Binding,
256 },
257 ]
258 "###
259 );
260 }
261
262 #[test]
263 fn completes_generic_params() {
264 assert_debug_snapshot!(
265 do_reference_completion(
266 r"
267 fn quux<T>() {
268 <|>
269 }
270 "
271 ),
272 @r###"
273 [
274 CompletionItem {
275 label: "T",
276 source_range: [52; 52),
277 delete: [52; 52),
278 insert: "T",
279 kind: TypeParam,
280 },
281 CompletionItem {
282 label: "quux()",
283 source_range: [52; 52),
284 delete: [52; 52),
285 insert: "quux()$0",
286 kind: Function,
287 lookup: "quux",
288 detail: "fn quux<T>()",
289 },
290 ]
291 "###
292 );
293 }
294
295 #[test]
296 fn completes_generic_params_in_struct() {
297 assert_debug_snapshot!(
298 do_reference_completion(
299 r"
300 struct X<T> {
301 x: <|>
302 }
303 "
304 ),
305 @r###"
306 [
307 CompletionItem {
308 label: "Self",
309 source_range: [54; 54),
310 delete: [54; 54),
311 insert: "Self",
312 kind: TypeParam,
313 },
314 CompletionItem {
315 label: "T",
316 source_range: [54; 54),
317 delete: [54; 54),
318 insert: "T",
319 kind: TypeParam,
320 },
321 CompletionItem {
322 label: "X<…>",
323 source_range: [54; 54),
324 delete: [54; 54),
325 insert: "X<$0>",
326 kind: Struct,
327 lookup: "X",
328 },
329 ]
330 "###
331 );
332 }
333
334 #[test]
335 fn completes_self_in_enum() {
336 assert_debug_snapshot!(
337 do_reference_completion(
338 r"
339 enum X {
340 Y(<|>)
341 }
342 "
343 ),
344 @r###"
345 [
346 CompletionItem {
347 label: "Self",
348 source_range: [48; 48),
349 delete: [48; 48),
350 insert: "Self",
351 kind: TypeParam,
352 },
353 CompletionItem {
354 label: "X",
355 source_range: [48; 48),
356 delete: [48; 48),
357 insert: "X",
358 kind: Enum,
359 },
360 ]
361 "###
362 );
363 }
364
365 #[test]
366 fn completes_module_items() {
367 assert_debug_snapshot!(
368 do_reference_completion(
369 r"
370 struct Foo;
371 enum Baz {}
372 fn quux() {
373 <|>
374 }
375 "
376 ),
377 @r###"
378 [
379 CompletionItem {
380 label: "Baz",
381 source_range: [105; 105),
382 delete: [105; 105),
383 insert: "Baz",
384 kind: Enum,
385 },
386 CompletionItem {
387 label: "Foo",
388 source_range: [105; 105),
389 delete: [105; 105),
390 insert: "Foo",
391 kind: Struct,
392 },
393 CompletionItem {
394 label: "quux()",
395 source_range: [105; 105),
396 delete: [105; 105),
397 insert: "quux()$0",
398 kind: Function,
399 lookup: "quux",
400 detail: "fn quux()",
401 },
402 ]
403 "###
404 );
405 }
406
407 #[test]
408 fn completes_extern_prelude() {
409 assert_debug_snapshot!(
410 do_reference_completion(
411 r"
412 //- /lib.rs
413 use <|>;
414
415 //- /other_crate/lib.rs
416 // nothing here
417 "
418 ),
419 @r###"
420 [
421 CompletionItem {
422 label: "other_crate",
423 source_range: [4; 4),
424 delete: [4; 4),
425 insert: "other_crate",
426 kind: Module,
427 },
428 ]
429 "###
430 );
431 }
432
433 #[test]
434 fn completes_module_items_in_nested_modules() {
435 assert_debug_snapshot!(
436 do_reference_completion(
437 r"
438 struct Foo;
439 mod m {
440 struct Bar;
441 fn quux() { <|> }
442 }
443 "
444 ),
445 @r###"
446 [
447 CompletionItem {
448 label: "Bar",
449 source_range: [117; 117),
450 delete: [117; 117),
451 insert: "Bar",
452 kind: Struct,
453 },
454 CompletionItem {
455 label: "quux()",
456 source_range: [117; 117),
457 delete: [117; 117),
458 insert: "quux()$0",
459 kind: Function,
460 lookup: "quux",
461 detail: "fn quux()",
462 },
463 ]
464 "###
465 );
466 }
467
468 #[test]
469 fn completes_return_type() {
470 assert_debug_snapshot!(
471 do_reference_completion(
472 r"
473 struct Foo;
474 fn x() -> <|>
475 "
476 ),
477 @r###"
478 [
479 CompletionItem {
480 label: "Foo",
481 source_range: [55; 55),
482 delete: [55; 55),
483 insert: "Foo",
484 kind: Struct,
485 },
486 CompletionItem {
487 label: "x()",
488 source_range: [55; 55),
489 delete: [55; 55),
490 insert: "x()$0",
491 kind: Function,
492 lookup: "x",
493 detail: "fn x()",
494 },
495 ]
496 "###
497 );
498 }
499
500 #[test]
501 fn dont_show_both_completions_for_shadowing() {
502 assert_debug_snapshot!(
503 do_reference_completion(
504 r"
505 fn foo() {
506 let bar = 92;
507 {
508 let bar = 62;
509 <|>
510 }
511 }
512 "
513 ),
514 @r###"
515 [
516 CompletionItem {
517 label: "bar",
518 source_range: [146; 146),
519 delete: [146; 146),
520 insert: "bar",
521 kind: Binding,
522 detail: "i32",
523 },
524 CompletionItem {
525 label: "foo()",
526 source_range: [146; 146),
527 delete: [146; 146),
528 insert: "foo()$0",
529 kind: Function,
530 lookup: "foo",
531 detail: "fn foo()",
532 },
533 ]
534 "###
535 );
536 }
537
538 #[test]
539 fn completes_self_in_methods() {
540 assert_debug_snapshot!(
541 do_reference_completion(r"impl S { fn foo(&self) { <|> } }"),
542 @r###"
543 [
544 CompletionItem {
545 label: "Self",
546 source_range: [25; 25),
547 delete: [25; 25),
548 insert: "Self",
549 kind: TypeParam,
550 },
551 CompletionItem {
552 label: "self",
553 source_range: [25; 25),
554 delete: [25; 25),
555 insert: "self",
556 kind: Binding,
557 detail: "&{unknown}",
558 },
559 ]
560 "###
561 );
562 }
563
564 #[test]
565 fn completes_prelude() {
566 assert_debug_snapshot!(
567 do_reference_completion(
568 "
569 //- /main.rs
570 fn foo() { let x: <|> }
571
572 //- /std/lib.rs
573 #[prelude_import]
574 use prelude::*;
575
576 mod prelude {
577 struct Option;
578 }
579 "
580 ),
581 @r###"
582 [
583 CompletionItem {
584 label: "Option",
585 source_range: [18; 18),
586 delete: [18; 18),
587 insert: "Option",
588 kind: Struct,
589 },
590 CompletionItem {
591 label: "foo()",
592 source_range: [18; 18),
593 delete: [18; 18),
594 insert: "foo()$0",
595 kind: Function,
596 lookup: "foo",
597 detail: "fn foo()",
598 },
599 CompletionItem {
600 label: "std",
601 source_range: [18; 18),
602 delete: [18; 18),
603 insert: "std",
604 kind: Module,
605 },
606 ]
607 "###
608 );
609 }
610
611 #[test]
612 fn completes_std_prelude_if_core_is_defined() {
613 assert_debug_snapshot!(
614 do_reference_completion(
615 "
616 //- /main.rs
617 fn foo() { let x: <|> }
618
619 //- /core/lib.rs
620 #[prelude_import]
621 use prelude::*;
622
623 mod prelude {
624 struct Option;
625 }
626
627 //- /std/lib.rs
628 #[prelude_import]
629 use prelude::*;
630
631 mod prelude {
632 struct String;
633 }
634 "
635 ),
636 @r###"
637 [
638 CompletionItem {
639 label: "String",
640 source_range: [18; 18),
641 delete: [18; 18),
642 insert: "String",
643 kind: Struct,
644 },
645 CompletionItem {
646 label: "core",
647 source_range: [18; 18),
648 delete: [18; 18),
649 insert: "core",
650 kind: Module,
651 },
652 CompletionItem {
653 label: "foo()",
654 source_range: [18; 18),
655 delete: [18; 18),
656 insert: "foo()$0",
657 kind: Function,
658 lookup: "foo",
659 detail: "fn foo()",
660 },
661 CompletionItem {
662 label: "std",
663 source_range: [18; 18),
664 delete: [18; 18),
665 insert: "std",
666 kind: Module,
667 },
668 ]
669 "###
670 );
671 }
672
673 #[test]
674 fn completes_macros_as_value() {
675 assert_debug_snapshot!(
676 do_reference_completion(
677 "
678 //- /main.rs
679 macro_rules! foo {
680 () => {}
681 }
682
683 #[macro_use]
684 mod m1 {
685 macro_rules! bar {
686 () => {}
687 }
688 }
689
690 mod m2 {
691 macro_rules! nope {
692 () => {}
693 }
694
695 #[macro_export]
696 macro_rules! baz {
697 () => {}
698 }
699 }
700
701 fn main() {
702 let v = <|>
703 }
704 "
705 ),
706 @r###"
707 [
708 CompletionItem {
709 label: "bar!",
710 source_range: [252; 252),
711 delete: [252; 252),
712 insert: "bar!($0)",
713 kind: Macro,
714 detail: "macro_rules! bar",
715 },
716 CompletionItem {
717 label: "baz!",
718 source_range: [252; 252),
719 delete: [252; 252),
720 insert: "baz!($0)",
721 kind: Macro,
722 detail: "#[macro_export]\nmacro_rules! baz",
723 },
724 CompletionItem {
725 label: "foo!",
726 source_range: [252; 252),
727 delete: [252; 252),
728 insert: "foo!($0)",
729 kind: Macro,
730 detail: "macro_rules! foo",
731 },
732 CompletionItem {
733 label: "m1",
734 source_range: [252; 252),
735 delete: [252; 252),
736 insert: "m1",
737 kind: Module,
738 },
739 CompletionItem {
740 label: "m2",
741 source_range: [252; 252),
742 delete: [252; 252),
743 insert: "m2",
744 kind: Module,
745 },
746 CompletionItem {
747 label: "main()",
748 source_range: [252; 252),
749 delete: [252; 252),
750 insert: "main()$0",
751 kind: Function,
752 lookup: "main",
753 detail: "fn main()",
754 },
755 ]
756 "###
757 );
758 }
759
760 #[test]
761 fn completes_both_macro_and_value() {
762 assert_debug_snapshot!(
763 do_reference_completion(
764 "
765 //- /main.rs
766 macro_rules! foo {
767 () => {}
768 }
769
770 fn foo() {
771 <|>
772 }
773 "
774 ),
775 @r###"
776 [
777 CompletionItem {
778 label: "foo!",
779 source_range: [49; 49),
780 delete: [49; 49),
781 insert: "foo!($0)",
782 kind: Macro,
783 detail: "macro_rules! foo",
784 },
785 CompletionItem {
786 label: "foo()",
787 source_range: [49; 49),
788 delete: [49; 49),
789 insert: "foo()$0",
790 kind: Function,
791 lookup: "foo",
792 detail: "fn foo()",
793 },
794 ]
795 "###
796 );
797 }
798
799 #[test]
800 fn completes_macros_as_type() {
801 assert_debug_snapshot!(
802 do_reference_completion(
803 "
804 //- /main.rs
805 macro_rules! foo {
806 () => {}
807 }
808
809 fn main() {
810 let x: <|>
811 }
812 "
813 ),
814 @r###"
815 [
816 CompletionItem {
817 label: "foo!",
818 source_range: [57; 57),
819 delete: [57; 57),
820 insert: "foo!($0)",
821 kind: Macro,
822 detail: "macro_rules! foo",
823 },
824 CompletionItem {
825 label: "main()",
826 source_range: [57; 57),
827 delete: [57; 57),
828 insert: "main()$0",
829 kind: Function,
830 lookup: "main",
831 detail: "fn main()",
832 },
833 ]
834 "###
835 );
836 }
837
838 #[test]
839 fn completes_macros_as_stmt() {
840 assert_debug_snapshot!(
841 do_reference_completion(
842 "
843 //- /main.rs
844 macro_rules! foo {
845 () => {}
846 }
847
848 fn main() {
849 <|>
850 }
851 "
852 ),
853 @r###"
854 [
855 CompletionItem {
856 label: "foo!",
857 source_range: [50; 50),
858 delete: [50; 50),
859 insert: "foo!($0)",
860 kind: Macro,
861 detail: "macro_rules! foo",
862 },
863 CompletionItem {
864 label: "main()",
865 source_range: [50; 50),
866 delete: [50; 50),
867 insert: "main()$0",
868 kind: Function,
869 lookup: "main",
870 detail: "fn main()",
871 },
872 ]
873 "###
874 );
875 }
876}