aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/inlay_hints.rs800
-rw-r--r--crates/test_utils/src/lib.rs30
2 files changed, 199 insertions, 631 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index c87652555..64980a832 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{FileId, FunctionSignature}; 9use crate::{FileId, FunctionSignature};
@@ -112,7 +112,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 112 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 113 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 114 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 115 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 116 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 117 if ty.is_unknown() {
118 return None; 118 return None;
@@ -345,583 +345,245 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
345 345
346#[cfg(test)] 346#[cfg(test)]
347mod tests { 347mod tests {
348 use crate::inlay_hints::InlayHintsConfig;
349 use insta::assert_debug_snapshot; 348 use insta::assert_debug_snapshot;
349 use test_utils::extract_annotations;
350 350
351 use crate::mock_analysis::single_file; 351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352
353 fn check(ra_fixture: &str) {
354 check_with_config(ra_fixture, InlayHintsConfig::default());
355 }
356
357 fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual);
364 }
352 365
353 #[test] 366 #[test]
354 fn param_hints_only() { 367 fn param_hints_only() {
355 let (analysis, file_id) = single_file( 368 check_with_config(
356 r#" 369 r#"
357 fn foo(a: i32, b: i32) -> i32 { a + b } 370fn foo(a: i32, b: i32) -> i32 { a + b }
358 fn main() { 371fn main() {
359 let _x = foo(4, 4); 372 let _x = foo(
360 }"#, 373 4,
361 ); 374 //^ a
362 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 375 4,
363 [ 376 //^ b
364 InlayHint { 377 );
365 range: 69..70, 378}"#,
366 kind: ParameterHint, 379 InlayHintsConfig {
367 label: "a", 380 parameter_hints: true,
368 }, 381 type_hints: false,
369 InlayHint { 382 chaining_hints: false,
370 range: 72..73, 383 max_length: None,
371 kind: ParameterHint,
372 label: "b",
373 }, 384 },
374 ] 385 );
375 "###);
376 } 386 }
377 387
378 #[test] 388 #[test]
379 fn hints_disabled() { 389 fn hints_disabled() {
380 let (analysis, file_id) = single_file( 390 check_with_config(
381 r#" 391 r#"
382 fn foo(a: i32, b: i32) -> i32 { a + b } 392fn foo(a: i32, b: i32) -> i32 { a + b }
383 fn main() { 393fn main() {
384 let _x = foo(4, 4); 394 let _x = foo(4, 4);
385 }"#, 395}"#,
396 InlayHintsConfig {
397 type_hints: false,
398 parameter_hints: false,
399 chaining_hints: false,
400 max_length: None,
401 },
386 ); 402 );
387 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
388 } 403 }
389 404
390 #[test] 405 #[test]
391 fn type_hints_only() { 406 fn type_hints_only() {
392 let (analysis, file_id) = single_file( 407 check_with_config(
393 r#" 408 r#"
394 fn foo(a: i32, b: i32) -> i32 { a + b } 409fn foo(a: i32, b: i32) -> i32 { a + b }
395 fn main() { 410fn main() {
396 let _x = foo(4, 4); 411 let _x = foo(4, 4);
397 }"#, 412 //^^ i32
398 ); 413}"#,
399 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 414 InlayHintsConfig {
400 [ 415 type_hints: true,
401 InlayHint { 416 parameter_hints: false,
402 range: 60..62, 417 chaining_hints: false,
403 kind: TypeHint, 418 max_length: None,
404 label: "i32",
405 }, 419 },
406 ] 420 );
407 "###);
408 } 421 }
422
409 #[test] 423 #[test]
410 fn default_generic_types_should_not_be_displayed() { 424 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 425 check(
412 r#" 426 r#"
413struct Test<K, T = u8> { 427struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 428
418fn main() { 429fn main() {
419 let zz = Test { t: 23u8, k: 33 }; 430 let zz = Test { t: 23u8, k: 33 };
431 //^^ Test<i32>
420 let zz_ref = &zz; 432 let zz_ref = &zz;
433 //^^^^^^ &Test<i32>
421}"#, 434}"#,
422 ); 435 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 68..70,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 106..112,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 436 }
440 437
441 #[test] 438 #[test]
442 fn let_statement() { 439 fn let_statement() {
443 let (analysis, file_id) = single_file( 440 check(
444 r#" 441 r#"
445#[derive(PartialEq)] 442#[derive(PartialEq)]
446enum CustomOption<T> { 443enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 444
451#[derive(PartialEq)] 445#[derive(PartialEq)]
452struct Test { 446struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 447
457fn main() { 448fn main() {
458 struct InnerStruct {} 449 struct InnerStruct {}
459 450
460 let test = 54; 451 let test = 54;
452 //^^^^ i32
461 let test: i32 = 33; 453 let test: i32 = 33;
462 let mut test = 33; 454 let mut test = 33;
455 //^^^^^^^^ i32
463 let _ = 22; 456 let _ = 22;
464 let test = "test"; 457 let test = "test";
458 //^^^^ &str
465 let test = InnerStruct {}; 459 let test = InnerStruct {};
466 460
467 let test = vec![222]; 461 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 462
475 let test = (42, 'a'); 463 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 464 //^^^^ (i32, char)
465 let (a, (b, (c,)) = (2, (3, (9.2,));
466 //^ i32 ^ i32 ^ f64
477 let &x = &92; 467 let &x = &92;
468 //^ i32
478}"#, 469}"#,
479 ); 470 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 192..196,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 235..243,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 274..278,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 538..542,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 565..566,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 569..570,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 572..573,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 576..577,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 579..580,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 583..584,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 626..627,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 471 }
542 472
543 #[test] 473 #[test]
544 fn closure_parameters() { 474 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 475 check(
546 r#" 476 r#"
547fn main() { 477fn main() {
548 let mut start = 0; 478 let mut start = 0;
549 (0..2).for_each(|increment| { 479 //^^^^^^^^^ i32
550 start += increment; 480 (0..2).for_each(|increment| { start += increment; });
551 }); 481 //^^^^^^^^^ i32
552 482
553 let multiply = |a, b, c, d| a * b * c * d; 483 let multiply =
554 let _: i32 = multiply(1, 2, 3, 4); 484 //^^^^^^^^ |…| -> i32
485 | a, b| a * b
486 //^ i32 ^ i32
487 ;
488
489 let _: i32 = multiply(1, 2);
555 let multiply_ref = &multiply; 490 let multiply_ref = &multiply;
491 //^^^^^^^^^^^^ &|…| -> i32
556 492
557 let return_42 = || 42; 493 let return_42 = || 42;
494 //^^^^^^^^^ || -> i32
558}"#, 495}"#,
559 ); 496 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 20..29,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 56..65,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 114..122,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 126..127,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 129..130,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 132..133,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 135..136,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 200..212,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 235..244,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 497 }
612 498
613 #[test] 499 #[test]
614 fn for_expression() { 500 fn for_expression() {
615 let (analysis, file_id) = single_file( 501 check(
616 r#" 502 r#"
617fn main() { 503fn main() {
618 let mut start = 0; 504 let mut start = 0;
619 for increment in 0..2 { 505 //^^^^^^^^^ i32
620 start += increment; 506 for increment in 0..2 { start += increment; }
621 } 507 //^^^^^^^^^ i32
622}"#, 508}"#,
623 ); 509 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 20..29,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 43..52,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 510 }
641 511
642 #[test] 512 #[test]
643 fn if_expr() { 513 fn if_expr() {
644 let (analysis, file_id) = single_file( 514 check(
645 r#" 515 r#"
646#[derive(PartialEq)] 516enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 517use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 518
658use CustomOption::*; 519struct Test { a: Option<u32>, b: u8 }
659 520
660fn main() { 521fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 522 let test = Some(Test { a: Some(3), b: 1 });
523 //^^^^ Option<Test>
662 if let None = &test {}; 524 if let None = &test {};
663 if let test = &test {}; 525 if let test = &test {};
526 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 527 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 528 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 529 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 530 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 531 if let Some(Test { a: x, b: y }) = &test {};
532 //^ &Option<u32> ^ &u8
533 if let Some(Test { a: Some(x), b: y }) = &test {};
534 //^ &u32 ^ &u8
535 if let Some(Test { a: None, b: y }) = &test {};
536 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 537 if let Some(Test { b: y, .. }) = &test {};
670 538 //^ &u8
671 if test == None {} 539 if test == None {}
672}"#, 540}"#,
673 ); 541 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 187..191,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 266..270,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 299..303,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 340..341,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 343..344,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 386..387,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 392..393,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 440..441,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 447..448,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 499..500,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 542..543,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 542 }
736 543
737 #[test] 544 #[test]
738 fn while_expr() { 545 fn while_expr() {
739 let (analysis, file_id) = single_file( 546 check(
740 r#" 547 r#"
741#[derive(PartialEq)] 548enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 549use Option::*;
743 None,
744 Some(T),
745}
746 550
747#[derive(PartialEq)] 551struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 552
755fn main() { 553fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 554 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 555 //^^^^ Option<Test>
758 while let test = &test {}; 556 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 557 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 558}"#,
768 ); 559 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 187..191,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 272..276,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 308..312,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 352..353,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 355..356,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 401..402,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 407..408,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 458..459,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 465..466,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 520..521,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 566..567,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 560 }
831 561
832 #[test] 562 #[test]
833 fn match_arm_list() { 563 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 564 check(
835 r#" 565 r#"
836#[derive(PartialEq)] 566enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 567use Option::*;
838 None,
839 Some(T),
840}
841 568
842#[derive(PartialEq)] 569struct Test { a: Option<u32>, b: u8 }
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847
848use CustomOption::*;
849 570
850fn main() { 571fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 572 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 573 None => (),
853 test => (), 574 test => (),
854 Some(test) => (), 575 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 576 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 577 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 578 _ => {}
861 } 579 }
862}"#, 580}"#,
863 ); 581 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 251..255,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 276..280,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 309..310,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 312..313,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 347..348,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 353..354,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 393..394,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 400..401,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 444..445,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 479..480,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 582 }
921 583
922 #[test] 584 #[test]
923 fn hint_truncation() { 585 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 586 check_with_config(
925 r#" 587 r#"
926struct Smol<T>(T); 588struct Smol<T>(T);
927 589
@@ -929,52 +591,26 @@ struct VeryLongOuterName<T>(T);
929 591
930fn main() { 592fn main() {
931 let a = Smol(0u32); 593 let a = Smol(0u32);
594 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 595 let b = VeryLongOuterName(0usize);
596 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 597 let c = Smol(Smol(0u32))
598 //^ Smol<Smol<…>>
934}"#, 599}"#,
935 ); 600 InlayHintsConfig { max_length: Some(8), ..Default::default() },
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 73..74,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 97..98,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 136..137,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 ); 601 );
957 } 602 }
958 603
959 #[test] 604 #[test]
960 fn function_call_parameter_hint() { 605 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 606 check(
962 r#" 607 r#"
963enum CustomOption<T> { 608enum Option<T> { None, Some(T) }
964 None, 609use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 610
969struct FileId {} 611struct FileId {}
970struct SmolStr {} 612struct SmolStr {}
971 613
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 614struct TextRange {}
979struct SyntaxKind {} 615struct SyntaxKind {}
980struct NavigationTarget {} 616struct NavigationTarget {}
@@ -982,18 +618,15 @@ struct NavigationTarget {}
982struct Test {} 618struct Test {}
983 619
984impl Test { 620impl Test {
985 fn method(&self, mut param: i32) -> i32 { 621 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 622
989 fn from_syntax( 623 fn from_syntax(
990 file_id: FileId, 624 file_id: FileId,
991 name: SmolStr, 625 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 626 focus_range: Option<TextRange>,
993 full_range: TextRange, 627 full_range: TextRange,
994 kind: SyntaxKind, 628 kind: SyntaxKind,
995 docs: CustomOption<String>, 629 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 630 ) -> NavigationTarget {
998 NavigationTarget {} 631 NavigationTarget {}
999 } 632 }
@@ -1005,108 +638,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 638
1006fn main() { 639fn main() {
1007 let not_literal = 1; 640 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 641 //^^^^^^^^^^^ i32
642 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
643 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 644 let t: Test = Test {};
1010 t.method(123); 645 t.method(123);
1011 Test::method(&t, 3456); 646 //^^^ param
1012 647 Test::method(&t, 3456);
648 //^^ &self ^^^^ param
1013 Test::from_syntax( 649 Test::from_syntax(
1014 FileId {}, 650 FileId {},
651 //^^^^^^^^^ file_id
1015 "impl".into(), 652 "impl".into(),
653 //^^^^^^^^^^^^^ name
1016 None, 654 None,
655 //^^^^ focus_range
1017 TextRange {}, 656 TextRange {},
657 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 658 SyntaxKind {},
659 //^^^^^^^^^^^^^ kind
1019 None, 660 None,
1020 None, 661 //^^^^ docs
1021 ); 662 );
1022}"#, 663}"#,
1023 ); 664 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 797..808,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 841..842,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 844..845,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 847..854,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 859..870,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 913..916,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 936..938,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 940..944,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 979..988,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 998..1011,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1021..1025,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1035..1047,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1057..1070,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1080..1084,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1094..1098,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 665 }
1106 666
1107 #[test] 667 #[test]
1108 fn omitted_parameters_hints_heuristics() { 668 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 669 check_with_config(
1110 r#" 670 r#"
1111fn map(f: i32) {} 671fn map(f: i32) {}
1112fn filter(predicate: i32) {} 672fn filter(predicate: i32) {}
@@ -1187,23 +747,16 @@ fn main() {
1187 let _: f64 = a.div_euclid(b); 747 let _: f64 = a.div_euclid(b);
1188 let _: f64 = a.abs_sub(b); 748 let _: f64 = a.abs_sub(b);
1189}"#, 749}"#,
1190 ); 750 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 ); 751 );
1196 } 752 }
1197 753
1198 #[test] 754 #[test]
1199 fn unit_structs_have_no_type_hints() { 755 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 756 check_with_config(
1201 r#" 757 r#"
1202enum CustomResult<T, E> { 758enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 759use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 760
1208struct SyntheticSyntax; 761struct SyntheticSyntax;
1209 762
@@ -1213,11 +766,7 @@ fn main() {
1213 Err(SyntheticSyntax) => (), 766 Err(SyntheticSyntax) => (),
1214 } 767 }
1215}"#, 768}"#,
1216 ); 769 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 ); 770 );
1222 } 771 }
1223 772
@@ -1255,42 +804,47 @@ fn main() {
1255 804
1256 #[test] 805 #[test]
1257 fn chaining_hints_without_newlines() { 806 fn chaining_hints_without_newlines() {
1258 let (analysis, file_id) = single_file( 807 check_with_config(
1259 r#" 808 r#"
1260 struct A(B); 809struct A(B);
1261 impl A { fn into_b(self) -> B { self.0 } } 810impl A { fn into_b(self) -> B { self.0 } }
1262 struct B(C); 811struct B(C);
1263 impl B { fn into_c(self) -> C { self.0 } } 812impl B { fn into_c(self) -> C { self.0 } }
1264 struct C; 813struct C;
1265 814
1266 fn main() { 815fn main() {
1267 let c = A(B(C)).into_b().into_c(); 816 let c = A(B(C)).into_b().into_c();
1268 }"#, 817}"#,
818 InlayHintsConfig {
819 parameter_hints: false,
820 type_hints: false,
821 chaining_hints: true,
822 max_length: None,
823 },
1269 ); 824 );
1270 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1271 } 825 }
1272 826
1273 #[test] 827 #[test]
1274 fn struct_access_chaining_hints() { 828 fn struct_access_chaining_hints() {
1275 let (analysis, file_id) = single_file( 829 let (analysis, file_id) = single_file(
1276 r#" 830 r#"
1277 struct A { pub b: B } 831struct A { pub b: B }
1278 struct B { pub c: C } 832struct B { pub c: C }
1279 struct C(pub bool); 833struct C(pub bool);
1280 struct D; 834struct D;
1281 835
1282 impl D { 836impl D {
1283 fn foo(&self) -> i32 { 42 } 837 fn foo(&self) -> i32 { 42 }
1284 } 838}
1285 839
1286 fn main() { 840fn main() {
1287 let x = A { b: B { c: C(true) } } 841 let x = A { b: B { c: C(true) } }
1288 .b 842 .b
1289 .c 843 .c
1290 .0; 844 .0;
1291 let x = D 845 let x = D
1292 .foo(); 846 .foo();
1293 }"#, 847}"#,
1294 ); 848 );
1295 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 849 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1296 [ 850 [
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index f9d6c6c96..e32a0a0c3 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -11,7 +11,7 @@ pub mod mark;
11mod fixture; 11mod fixture;
12 12
13use std::{ 13use std::{
14 convert::TryInto, 14 convert::{TryFrom, TryInto},
15 env, fs, 15 env, fs,
16 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
17}; 17};
@@ -169,10 +169,9 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
169 for line in lines_with_ends(text) { 169 for line in lines_with_ends(text) {
170 if let Some(idx) = line.find("//^") { 170 if let Some(idx) = line.find("//^") {
171 let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); 171 let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]);
172 let marker_and_data = &line[idx + "//".len()..]; 172 for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) {
173 let len = marker_and_data.chars().take_while(|&it| it == '^').count(); 173 res.push((line_range + offset, text))
174 let data = marker_and_data[len..].trim().to_string(); 174 }
175 res.push((TextRange::at(offset, len.try_into().unwrap()), data))
176 } 175 }
177 prev_line_start = Some(line_start); 176 prev_line_start = Some(line_start);
178 line_start += TextSize::of(line); 177 line_start += TextSize::of(line);
@@ -180,13 +179,28 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
180 res 179 res
181} 180}
182 181
182fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> {
183 let mut res = Vec::new();
184 let mut offset: TextSize = 0.into();
185 while !line.is_empty() {
186 let len = line.chars().take_while(|&it| it == '^').count();
187 assert!(len > 0);
188 let range = TextRange::at(offset, len.try_into().unwrap());
189 let next = line[len..].find('^').map_or(line.len(), |it| it + len);
190 res.push((range, line[len..][..next - len].trim().to_string()));
191 line = &line[next..];
192 offset += TextSize::try_from(next).unwrap();
193 }
194 res
195}
196
183#[test] 197#[test]
184fn test_extract_annotations() { 198fn test_extract_annotations() {
185 let text = stdx::trim_indent( 199 let text = stdx::trim_indent(
186 r#" 200 r#"
187fn main() { 201fn main() {
188 let x = 92; 202 let (x, y) = (9, 2);
189 //^ def 203 //^ def ^ def
190 zoo + 1 204 zoo + 1
191} //^^^ i32 205} //^^^ i32
192 "#, 206 "#,
@@ -195,7 +209,7 @@ fn main() {
195 .into_iter() 209 .into_iter()
196 .map(|(range, ann)| (&text[range], ann)) 210 .map(|(range, ann)| (&text[range], ann))
197 .collect::<Vec<_>>(); 211 .collect::<Vec<_>>();
198 assert_eq!(res, vec![("x", "def".into()), ("zoo", "i32".into()),]); 212 assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]);
199} 213}
200 214
201// Comparison functionality borrowed from cargo: 215// Comparison functionality borrowed from cargo: