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/presentation.rs733
1 files changed, 340 insertions, 393 deletions
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 8026170da..9ec33a050 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -330,14 +330,14 @@ pub(crate) fn compute_score(
330 // FIXME: this should not fall back to string equality. 330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string(); 331 let ty = &ty.display(ctx.db).to_string();
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 mark::hit!(test_struct_field_completion_in_record_lit); 333 mark::hit!(record_field_type_match);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 ( 335 (
336 struct_field.name(ctx.db).to_string(), 336 struct_field.name(ctx.db).to_string(),
337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
338 ) 338 )
339 } else if let Some(active_parameter) = &ctx.active_parameter { 339 } else if let Some(active_parameter) = &ctx.active_parameter {
340 mark::hit!(test_struct_field_completion_in_func_call); 340 mark::hit!(active_param_type_match);
341 (active_parameter.name.clone(), active_parameter.ty.clone()) 341 (active_parameter.name.clone(), active_parameter.ty.clone())
342 } else { 342 } else {
343 return None; 343 return None;
@@ -461,174 +461,155 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
461 461
462#[cfg(test)] 462#[cfg(test)]
463mod tests { 463mod tests {
464 use insta::assert_debug_snapshot; 464 use expect::{expect, Expect};
465 use test_utils::mark; 465 use test_utils::mark;
466 466
467 use crate::completion::{ 467 use crate::completion::{
468 test_utils::{check_edit, check_edit_with_config, do_completion}, 468 test_utils::{check_edit, check_edit_with_config, do_completion},
469 CompletionConfig, CompletionItem, CompletionKind, 469 CompletionConfig, CompletionKind,
470 }; 470 };
471 471
472 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 472 fn check(ra_fixture: &str, expect: Expect) {
473 do_completion(ra_fixture, CompletionKind::Reference) 473 let actual = do_completion(ra_fixture, CompletionKind::Reference);
474 expect.assert_debug_eq(&actual);
474 } 475 }
475 476
476 #[test] 477 #[test]
477 fn enum_detail_includes_names_for_record() { 478 fn enum_detail_includes_record_fields() {
478 assert_debug_snapshot!( 479 check(
479 do_reference_completion(
480 r#" 480 r#"
481 enum Foo { 481enum Foo { Foo { x: i32, y: i32 } }
482 Foo {x: i32, y: i32}
483 }
484 482
485 fn main() { Foo::Fo<|> } 483fn main() { Foo::Fo<|> }
486 "#, 484"#,
487 ), 485 expect![[r#"
488 @r###" 486 [
489 [ 487 CompletionItem {
490 CompletionItem { 488 label: "Foo",
491 label: "Foo", 489 source_range: 54..56,
492 source_range: 56..58, 490 delete: 54..56,
493 delete: 56..58, 491 insert: "Foo",
494 insert: "Foo", 492 kind: EnumVariant,
495 kind: EnumVariant, 493 detail: "{ x: i32, y: i32 }",
496 detail: "{ x: i32, y: i32 }", 494 },
497 }, 495 ]
498 ] 496 "#]],
499 "###
500 ); 497 );
501 } 498 }
502 499
503 #[test] 500 #[test]
504 fn enum_detail_doesnt_include_names_for_tuple() { 501 fn enum_detail_doesnt_include_tuple_fields() {
505 assert_debug_snapshot!( 502 check(
506 do_reference_completion(
507 r#" 503 r#"
508 enum Foo { 504enum Foo { Foo (i32, i32) }
509 Foo (i32, i32)
510 }
511 505
512 fn main() { Foo::Fo<|> } 506fn main() { Foo::Fo<|> }
513 "#, 507"#,
514 ), 508 expect![[r#"
515 @r###" 509 [
516 [ 510 CompletionItem {
517 CompletionItem { 511 label: "Foo(…)",
518 label: "Foo(…)", 512 source_range: 46..48,
519 source_range: 50..52, 513 delete: 46..48,
520 delete: 50..52, 514 insert: "Foo($0)",
521 insert: "Foo($0)", 515 kind: EnumVariant,
522 kind: EnumVariant, 516 lookup: "Foo",
523 lookup: "Foo", 517 detail: "(i32, i32)",
524 detail: "(i32, i32)", 518 trigger_call_info: true,
525 trigger_call_info: true, 519 },
526 }, 520 ]
527 ] 521 "#]],
528 "###
529 ); 522 );
530 } 523 }
531 524
532 #[test] 525 #[test]
533 fn enum_detail_just_parentheses_for_unit() { 526 fn enum_detail_just_parentheses_for_unit() {
534 assert_debug_snapshot!( 527 check(
535 do_reference_completion(
536 r#" 528 r#"
537 enum Foo { 529enum Foo { Foo }
538 Foo
539 }
540 530
541 fn main() { Foo::Fo<|> } 531fn main() { Foo::Fo<|> }
542 "#, 532"#,
543 ), 533 expect![[r#"
544 @r###" 534 [
545 [ 535 CompletionItem {
546 CompletionItem { 536 label: "Foo",
547 label: "Foo", 537 source_range: 35..37,
548 source_range: 39..41, 538 delete: 35..37,
549 delete: 39..41, 539 insert: "Foo",
550 insert: "Foo", 540 kind: EnumVariant,
551 kind: EnumVariant, 541 detail: "()",
552 detail: "()", 542 },
553 }, 543 ]
554 ] 544 "#]],
555 "###
556 ); 545 );
557 } 546 }
558 547
559 #[test] 548 #[test]
560 fn sets_deprecated_flag_in_completion_items() { 549 fn sets_deprecated_flag_in_completion_items() {
561 assert_debug_snapshot!( 550 check(
562 do_reference_completion( 551 r#"
563 r#" 552#[deprecated]
564 #[deprecated] 553fn something_deprecated() {}
565 fn something_deprecated() {} 554#[deprecated(since = "1.0.0")]
566 555fn something_else_deprecated() {}
567 #[deprecated(since = "1.0.0")] 556
568 fn something_else_deprecated() {} 557fn main() { som<|> }
569 558"#,
570 fn main() { som<|> } 559 expect![[r#"
571 "#, 560 [
572 ), 561 CompletionItem {
573 @r###" 562 label: "main()",
574 [ 563 source_range: 121..124,
575 CompletionItem { 564 delete: 121..124,
576 label: "main()", 565 insert: "main()$0",
577 source_range: 122..125, 566 kind: Function,
578 delete: 122..125, 567 lookup: "main",
579 insert: "main()$0", 568 detail: "fn main()",
580 kind: Function, 569 },
581 lookup: "main", 570 CompletionItem {
582 detail: "fn main()", 571 label: "something_deprecated()",
583 }, 572 source_range: 121..124,
584 CompletionItem { 573 delete: 121..124,
585 label: "something_deprecated()", 574 insert: "something_deprecated()$0",
586 source_range: 122..125, 575 kind: Function,
587 delete: 122..125, 576 lookup: "something_deprecated",
588 insert: "something_deprecated()$0", 577 detail: "fn something_deprecated()",
589 kind: Function, 578 deprecated: true,
590 lookup: "something_deprecated", 579 },
591 detail: "fn something_deprecated()", 580 CompletionItem {
592 deprecated: true, 581 label: "something_else_deprecated()",
593 }, 582 source_range: 121..124,
594 CompletionItem { 583 delete: 121..124,
595 label: "something_else_deprecated()", 584 insert: "something_else_deprecated()$0",
596 source_range: 122..125, 585 kind: Function,
597 delete: 122..125, 586 lookup: "something_else_deprecated",
598 insert: "something_else_deprecated()$0", 587 detail: "fn something_else_deprecated()",
599 kind: Function, 588 deprecated: true,
600 lookup: "something_else_deprecated", 589 },
601 detail: "fn something_else_deprecated()", 590 ]
602 deprecated: true, 591 "#]],
603 },
604 ]
605 "###
606 ); 592 );
607 593
608 assert_debug_snapshot!(do_reference_completion( 594 check(
609 r#" 595 r#"
610struct A { 596struct A { #[deprecated] the_field: u32 }
611 #[deprecated] 597fn foo() { A { the<|> } }
612 the_field: u32,
613}
614fn foo() {
615 A { the<|> }
616}
617"#, 598"#,
618 ), 599 expect![[r#"
619 @r###" 600 [
620 [ 601 CompletionItem {
621 CompletionItem { 602 label: "the_field",
622 label: "the_field", 603 source_range: 57..60,
623 source_range: 69..72, 604 delete: 57..60,
624 delete: 69..72, 605 insert: "the_field",
625 insert: "the_field", 606 kind: Field,
626 kind: Field, 607 detail: "u32",
627 detail: "u32", 608 deprecated: true,
628 deprecated: true, 609 },
629 }, 610 ]
630 ] 611 "#]],
631 "###); 612 );
632 } 613 }
633 614
634 #[test] 615 #[test]
@@ -921,279 +902,245 @@ fn main() { frobnicate!(); }
921 } 902 }
922 903
923 #[test] 904 #[test]
924 fn test_struct_field_completion_in_func_call() { 905 fn active_param_type_match() {
925 mark::check!(test_struct_field_completion_in_func_call); 906 mark::check!(active_param_type_match);
926 assert_debug_snapshot!( 907 check(
927 do_reference_completion( 908 r#"
928 r" 909struct S { foo: i64, bar: u32, baz: () }
929 struct A { another_field: i64, the_field: u32, my_string: String } 910fn test(x: u32) { }
930 fn test(my_param: u32) -> u32 { my_param } 911fn foo(s: S) { test(s.<|>) }
931 fn foo(a: A) { 912"#,
932 test(a.<|>) 913 expect![[r#"
933 } 914 [
934 ", 915 CompletionItem {
935 ), 916 label: "bar",
936 @r###" 917 source_range: 83..83,
937 [ 918 delete: 83..83,
938 CompletionItem { 919 insert: "bar",
939 label: "another_field", 920 kind: Field,
940 source_range: 136..136, 921 detail: "u32",
941 delete: 136..136, 922 score: TypeMatch,
942 insert: "another_field", 923 },
943 kind: Field, 924 CompletionItem {
944 detail: "i64", 925 label: "baz",
945 }, 926 source_range: 83..83,
946 CompletionItem { 927 delete: 83..83,
947 label: "my_string", 928 insert: "baz",
948 source_range: 136..136, 929 kind: Field,
949 delete: 136..136, 930 detail: "()",
950 insert: "my_string", 931 },
951 kind: Field, 932 CompletionItem {
952 detail: "{unknown}", 933 label: "foo",
953 }, 934 source_range: 83..83,
954 CompletionItem { 935 delete: 83..83,
955 label: "the_field", 936 insert: "foo",
956 source_range: 136..136, 937 kind: Field,
957 delete: 136..136, 938 detail: "i64",
958 insert: "the_field", 939 },
959 kind: Field, 940 ]
960 detail: "u32", 941 "#]],
961 score: TypeMatch,
962 },
963 ]
964 "###
965 ); 942 );
966 } 943 }
967 944
968 #[test] 945 #[test]
969 fn test_struct_field_completion_in_func_call_with_type_and_name() { 946 fn active_param_type_and_name_match() {
970 assert_debug_snapshot!( 947 check(
971 do_reference_completion( 948 r#"
972 r" 949struct S { foo: i64, bar: u32, baz: u32 }
973 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 950fn test(bar: u32) { }
974 fn test(the_field: u32) -> u32 { the_field } 951fn foo(s: S) { test(s.<|>) }
975 fn foo(a: A) { 952"#,
976 test(a.<|>) 953 expect![[r#"
977 } 954 [
978 ", 955 CompletionItem {
979 ), 956 label: "bar",
980 @r###" 957 source_range: 86..86,
981 [ 958 delete: 86..86,
982 CompletionItem { 959 insert: "bar",
983 label: "another_field", 960 kind: Field,
984 source_range: 143..143, 961 detail: "u32",
985 delete: 143..143, 962 score: TypeAndNameMatch,
986 insert: "another_field", 963 },
987 kind: Field, 964 CompletionItem {
988 detail: "i64", 965 label: "baz",
989 }, 966 source_range: 86..86,
990 CompletionItem { 967 delete: 86..86,
991 label: "another_good_type", 968 insert: "baz",
992 source_range: 143..143, 969 kind: Field,
993 delete: 143..143, 970 detail: "u32",
994 insert: "another_good_type", 971 score: TypeMatch,
995 kind: Field, 972 },
996 detail: "u32", 973 CompletionItem {
997 score: TypeMatch, 974 label: "foo",
998 }, 975 source_range: 86..86,
999 CompletionItem { 976 delete: 86..86,
1000 label: "the_field", 977 insert: "foo",
1001 source_range: 143..143, 978 kind: Field,
1002 delete: 143..143, 979 detail: "i64",
1003 insert: "the_field", 980 },
1004 kind: Field, 981 ]
1005 detail: "u32", 982 "#]],
1006 score: TypeAndNameMatch,
1007 },
1008 ]
1009 "###
1010 ); 983 );
1011 } 984 }
1012 985
1013 #[test] 986 #[test]
1014 fn test_struct_field_completion_in_record_lit() { 987 fn record_field_type_match() {
1015 mark::check!(test_struct_field_completion_in_record_lit); 988 mark::check!(record_field_type_match);
1016 assert_debug_snapshot!( 989 check(
1017 do_reference_completion( 990 r#"
1018 r" 991struct A { foo: i64, bar: u32, baz: u32 }
1019 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 992struct B { x: (), y: f32, bar: u32 }
1020 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 993fn foo(a: A) { B { bar: a.<|> }; }
1021 fn foo(a: A) { 994"#,
1022 let b = B { 995 expect![[r#"
1023 the_field: a.<|> 996 [
1024 }; 997 CompletionItem {
1025 } 998 label: "bar",
1026 ", 999 source_range: 105..105,
1027 ), 1000 delete: 105..105,
1028 @r###" 1001 insert: "bar",
1029 [ 1002 kind: Field,
1030 CompletionItem { 1003 detail: "u32",
1031 label: "another_field", 1004 score: TypeAndNameMatch,
1032 source_range: 189..189, 1005 },
1033 delete: 189..189, 1006 CompletionItem {
1034 insert: "another_field", 1007 label: "baz",
1035 kind: Field, 1008 source_range: 105..105,
1036 detail: "i64", 1009 delete: 105..105,
1037 }, 1010 insert: "baz",
1038 CompletionItem { 1011 kind: Field,
1039 label: "another_good_type", 1012 detail: "u32",
1040 source_range: 189..189, 1013 score: TypeMatch,
1041 delete: 189..189, 1014 },
1042 insert: "another_good_type", 1015 CompletionItem {
1043 kind: Field, 1016 label: "foo",
1044 detail: "u32", 1017 source_range: 105..105,
1045 score: TypeMatch, 1018 delete: 105..105,
1046 }, 1019 insert: "foo",
1047 CompletionItem { 1020 kind: Field,
1048 label: "the_field", 1021 detail: "i64",
1049 source_range: 189..189, 1022 },
1050 delete: 189..189, 1023 ]
1051 insert: "the_field", 1024 "#]],
1052 kind: Field, 1025 )
1053 detail: "u32",
1054 score: TypeAndNameMatch,
1055 },
1056 ]
1057 "###
1058 );
1059 } 1026 }
1060 1027
1061 #[test] 1028 #[test]
1062 fn test_struct_field_completion_in_record_lit_and_fn_call() { 1029 fn record_field_type_match_and_fn_call() {
1063 assert_debug_snapshot!( 1030 check(
1064 do_reference_completion( 1031 r#"
1065 r" 1032struct A { foo: i64, bar: u32, baz: u32 }
1066 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1033struct B { x: (), y: f32, bar: u32 }
1067 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1034fn f(foo: i64) { }
1068 fn test(the_field: i64) -> i64 { the_field } 1035fn foo(a: A) { B { bar: f(a.<|>) }; }
1069 fn foo(a: A) { 1036"#,
1070 let b = B { 1037 expect![[r#"
1071 the_field: test(a.<|>) 1038 [
1072 }; 1039 CompletionItem {
1073 } 1040 label: "bar",
1074 ", 1041 source_range: 127..127,
1075 ), 1042 delete: 127..127,
1076 @r###" 1043 insert: "bar",
1077 [ 1044 kind: Field,
1078 CompletionItem { 1045 detail: "u32",
1079 label: "another_field", 1046 },
1080 source_range: 239..239, 1047 CompletionItem {
1081 delete: 239..239, 1048 label: "baz",
1082 insert: "another_field", 1049 source_range: 127..127,
1083 kind: Field, 1050 delete: 127..127,
1084 detail: "i64", 1051 insert: "baz",
1085 score: TypeMatch, 1052 kind: Field,
1086 }, 1053 detail: "u32",
1087 CompletionItem { 1054 },
1088 label: "another_good_type", 1055 CompletionItem {
1089 source_range: 239..239, 1056 label: "foo",
1090 delete: 239..239, 1057 source_range: 127..127,
1091 insert: "another_good_type", 1058 delete: 127..127,
1092 kind: Field, 1059 insert: "foo",
1093 detail: "u32", 1060 kind: Field,
1094 }, 1061 detail: "i64",
1095 CompletionItem { 1062 score: TypeAndNameMatch,
1096 label: "the_field", 1063 },
1097 source_range: 239..239, 1064 ]
1098 delete: 239..239, 1065 "#]],
1099 insert: "the_field",
1100 kind: Field,
1101 detail: "u32",
1102 },
1103 ]
1104 "###
1105 ); 1066 );
1106 } 1067 check(
1107 1068 r#"
1108 #[test] 1069struct A { foo: i64, bar: u32, baz: u32 }
1109 fn test_struct_field_completion_in_fn_call_and_record_lit() { 1070struct B { x: (), y: f32, bar: u32 }
1110 assert_debug_snapshot!( 1071fn f(foo: i64) { }
1111 do_reference_completion( 1072fn foo(a: A) { f(B { bar: a.<|> }); }
1112 r" 1073"#,
1113 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1074 expect![[r#"
1114 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1075 [
1115 fn test(the_field: i64) -> i64 { the_field } 1076 CompletionItem {
1116 fn foo(a: A) { 1077 label: "bar",
1117 test(B { 1078 source_range: 127..127,
1118 the_field: a.<|> 1079 delete: 127..127,
1119 }); 1080 insert: "bar",
1120 } 1081 kind: Field,
1121 ", 1082 detail: "u32",
1122 ), 1083 score: TypeAndNameMatch,
1123 @r###" 1084 },
1124 [ 1085 CompletionItem {
1125 CompletionItem { 1086 label: "baz",
1126 label: "another_field", 1087 source_range: 127..127,
1127 source_range: 231..231, 1088 delete: 127..127,
1128 delete: 231..231, 1089 insert: "baz",
1129 insert: "another_field", 1090 kind: Field,
1130 kind: Field, 1091 detail: "u32",
1131 detail: "i64", 1092 score: TypeMatch,
1132 }, 1093 },
1133 CompletionItem { 1094 CompletionItem {
1134 label: "another_good_type", 1095 label: "foo",
1135 source_range: 231..231, 1096 source_range: 127..127,
1136 delete: 231..231, 1097 delete: 127..127,
1137 insert: "another_good_type", 1098 insert: "foo",
1138 kind: Field, 1099 kind: Field,
1139 detail: "u32", 1100 detail: "i64",
1140 score: TypeMatch, 1101 },
1141 }, 1102 ]
1142 CompletionItem { 1103 "#]],
1143 label: "the_field",
1144 source_range: 231..231,
1145 delete: 231..231,
1146 insert: "the_field",
1147 kind: Field,
1148 detail: "u32",
1149 score: TypeAndNameMatch,
1150 },
1151 ]
1152 "###
1153 ); 1104 );
1154 } 1105 }
1155 1106
1156 #[test] 1107 #[test]
1157 fn prioritize_exact_ref_match() { 1108 fn prioritize_exact_ref_match() {
1158 assert_debug_snapshot!( 1109 check(
1159 do_reference_completion( 1110 r#"
1160 r" 1111struct WorldSnapshot { _f: () };
1161 struct WorldSnapshot { _f: () }; 1112fn go(world: &WorldSnapshot) { go(w<|>) }
1162 fn go(world: &WorldSnapshot) { 1113"#,
1163 go(w<|>) 1114 expect![[r#"
1164 } 1115 [
1165 ", 1116 CompletionItem {
1166 ), 1117 label: "WorldSnapshot",
1167 @r###" 1118 source_range: 67..68,
1168 [ 1119 delete: 67..68,
1169 CompletionItem { 1120 insert: "WorldSnapshot",
1170 label: "WorldSnapshot", 1121 kind: Struct,
1171 source_range: 71..72, 1122 },
1172 delete: 71..72, 1123 CompletionItem {
1173 insert: "WorldSnapshot", 1124 label: "go(…)",
1174 kind: Struct, 1125 source_range: 67..68,
1175 }, 1126 delete: 67..68,
1176 CompletionItem { 1127 insert: "go(${1:world})$0",
1177 label: "go(…)", 1128 kind: Function,
1178 source_range: 71..72, 1129 lookup: "go",
1179 delete: 71..72, 1130 detail: "fn go(world: &WorldSnapshot)",
1180 insert: "go(${1:world})$0", 1131 trigger_call_info: true,
1181 kind: Function, 1132 },
1182 lookup: "go", 1133 CompletionItem {
1183 detail: "fn go(world: &WorldSnapshot)", 1134 label: "world",
1184 trigger_call_info: true, 1135 source_range: 67..68,
1185 }, 1136 delete: 67..68,
1186 CompletionItem { 1137 insert: "world",
1187 label: "world", 1138 kind: Binding,
1188 source_range: 71..72, 1139 detail: "&WorldSnapshot",
1189 delete: 71..72, 1140 score: TypeAndNameMatch,
1190 insert: "world", 1141 },
1191 kind: Binding, 1142 ]
1192 detail: "&WorldSnapshot", 1143 "#]],
1193 score: TypeAndNameMatch,
1194 },
1195 ]
1196 "###
1197 ); 1144 );
1198 } 1145 }
1199} 1146}