aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/find_path.rs587
-rw-r--r--crates/ide/src/move_item.rs100
-rw-r--r--crates/rust-analyzer/src/handlers.rs9
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs12
-rw-r--r--docs/dev/lsp-extensions.md6
-rw-r--r--editors/code/src/commands.ts26
-rw-r--r--editors/code/src/lsp_ext.ts2
8 files changed, 410 insertions, 334 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 109d3552f..317ca86ec 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -425,106 +425,145 @@ mod tests {
425 425
426 #[test] 426 #[test]
427 fn same_module() { 427 fn same_module() {
428 let code = r#" 428 check_found_path(
429 //- /main.rs 429 r#"
430 struct S; 430//- /main.rs
431 $0 431struct S;
432 "#; 432$0
433 check_found_path(code, "S", "S", "crate::S", "self::S"); 433 "#,
434 "S",
435 "S",
436 "crate::S",
437 "self::S",
438 );
434 } 439 }
435 440
436 #[test] 441 #[test]
437 fn enum_variant() { 442 fn enum_variant() {
438 let code = r#" 443 check_found_path(
439 //- /main.rs 444 r#"
440 enum E { A } 445//- /main.rs
441 $0 446enum E { A }
442 "#; 447$0
443 check_found_path(code, "E::A", "E::A", "E::A", "E::A"); 448 "#,
449 "E::A",
450 "E::A",
451 "E::A",
452 "E::A",
453 );
444 } 454 }
445 455
446 #[test] 456 #[test]
447 fn sub_module() { 457 fn sub_module() {
448 let code = r#" 458 check_found_path(
449 //- /main.rs 459 r#"
450 mod foo { 460//- /main.rs
451 pub struct S; 461mod foo {
452 } 462 pub struct S;
453 $0 463}
454 "#; 464$0
455 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S"); 465 "#,
466 "foo::S",
467 "foo::S",
468 "crate::foo::S",
469 "self::foo::S",
470 );
456 } 471 }
457 472
458 #[test] 473 #[test]
459 fn super_module() { 474 fn super_module() {
460 let code = r#" 475 check_found_path(
461 //- /main.rs 476 r#"
462 mod foo; 477//- /main.rs
463 //- /foo.rs 478mod foo;
464 mod bar; 479//- /foo.rs
465 struct S; 480mod bar;
466 //- /foo/bar.rs 481struct S;
467 $0 482//- /foo/bar.rs
468 "#; 483$0
469 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S"); 484 "#,
485 "super::S",
486 "super::S",
487 "crate::foo::S",
488 "super::S",
489 );
470 } 490 }
471 491
472 #[test] 492 #[test]
473 fn self_module() { 493 fn self_module() {
474 let code = r#" 494 check_found_path(
475 //- /main.rs 495 r#"
476 mod foo; 496//- /main.rs
477 //- /foo.rs 497mod foo;
478 $0 498//- /foo.rs
479 "#; 499$0
480 check_found_path(code, "self", "self", "crate::foo", "self"); 500 "#,
501 "self",
502 "self",
503 "crate::foo",
504 "self",
505 );
481 } 506 }
482 507
483 #[test] 508 #[test]
484 fn crate_root() { 509 fn crate_root() {
485 let code = r#" 510 check_found_path(
486 //- /main.rs 511 r#"
487 mod foo; 512//- /main.rs
488 //- /foo.rs 513mod foo;
489 $0 514//- /foo.rs
490 "#; 515$0
491 check_found_path(code, "crate", "crate", "crate", "crate"); 516 "#,
517 "crate",
518 "crate",
519 "crate",
520 "crate",
521 );
492 } 522 }
493 523
494 #[test] 524 #[test]
495 fn same_crate() { 525 fn same_crate() {
496 let code = r#" 526 check_found_path(
497 //- /main.rs 527 r#"
498 mod foo; 528//- /main.rs
499 struct S; 529mod foo;
500 //- /foo.rs 530struct S;
501 $0 531//- /foo.rs
502 "#; 532$0
503 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S"); 533 "#,
534 "crate::S",
535 "crate::S",
536 "crate::S",
537 "crate::S",
538 );
504 } 539 }
505 540
506 #[test] 541 #[test]
507 fn different_crate() { 542 fn different_crate() {
508 let code = r#" 543 check_found_path(
509 //- /main.rs crate:main deps:std 544 r#"
510 $0 545//- /main.rs crate:main deps:std
511 //- /std.rs crate:std 546$0
512 pub struct S; 547//- /std.rs crate:std
513 "#; 548pub struct S;
514 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 549 "#,
550 "std::S",
551 "std::S",
552 "std::S",
553 "std::S",
554 );
515 } 555 }
516 556
517 #[test] 557 #[test]
518 fn different_crate_renamed() { 558 fn different_crate_renamed() {
519 let code = r#"
520 //- /main.rs crate:main deps:std
521 extern crate std as std_renamed;
522 $0
523 //- /std.rs crate:std
524 pub struct S;
525 "#;
526 check_found_path( 559 check_found_path(
527 code, 560 r#"
561//- /main.rs crate:main deps:std
562extern crate std as std_renamed;
563$0
564//- /std.rs crate:std
565pub struct S;
566 "#,
528 "std_renamed::S", 567 "std_renamed::S",
529 "std_renamed::S", 568 "std_renamed::S",
530 "std_renamed::S", 569 "std_renamed::S",
@@ -537,41 +576,38 @@ mod tests {
537 cov_mark::check!(partially_imported); 576 cov_mark::check!(partially_imported);
538 // Tests that short paths are used even for external items, when parts of the path are 577 // Tests that short paths are used even for external items, when parts of the path are
539 // already in scope. 578 // already in scope.
540 let code = r#" 579 check_found_path(
541 //- /main.rs crate:main deps:syntax 580 r#"
581//- /main.rs crate:main deps:syntax
542 582
543 use syntax::ast; 583use syntax::ast;
544 $0 584$0
545 585
546 //- /lib.rs crate:syntax 586//- /lib.rs crate:syntax
547 pub mod ast { 587pub mod ast {
548 pub enum ModuleItem { 588 pub enum ModuleItem {
549 A, B, C, 589 A, B, C,
550 } 590 }
551 } 591}
552 "#; 592 "#,
553 check_found_path(
554 code,
555 "ast::ModuleItem", 593 "ast::ModuleItem",
556 "syntax::ast::ModuleItem", 594 "syntax::ast::ModuleItem",
557 "syntax::ast::ModuleItem", 595 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem", 596 "syntax::ast::ModuleItem",
559 ); 597 );
560 598
561 let code = r#"
562 //- /main.rs crate:main deps:syntax
563
564 $0
565
566 //- /lib.rs crate:syntax
567 pub mod ast {
568 pub enum ModuleItem {
569 A, B, C,
570 }
571 }
572 "#;
573 check_found_path( 599 check_found_path(
574 code, 600 r#"
601//- /main.rs crate:main deps:syntax
602$0
603
604//- /lib.rs crate:syntax
605pub mod ast {
606 pub enum ModuleItem {
607 A, B, C,
608 }
609}
610 "#,
575 "syntax::ast::ModuleItem", 611 "syntax::ast::ModuleItem",
576 "syntax::ast::ModuleItem", 612 "syntax::ast::ModuleItem",
577 "syntax::ast::ModuleItem", 613 "syntax::ast::ModuleItem",
@@ -581,68 +617,88 @@ mod tests {
581 617
582 #[test] 618 #[test]
583 fn same_crate_reexport() { 619 fn same_crate_reexport() {
584 let code = r#" 620 check_found_path(
585 //- /main.rs 621 r#"
586 mod bar { 622//- /main.rs
587 mod foo { pub(super) struct S; } 623mod bar {
588 pub(crate) use foo::*; 624 mod foo { pub(super) struct S; }
589 } 625 pub(crate) use foo::*;
590 $0 626}
591 "#; 627$0
592 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S"); 628 "#,
629 "bar::S",
630 "bar::S",
631 "crate::bar::S",
632 "self::bar::S",
633 );
593 } 634 }
594 635
595 #[test] 636 #[test]
596 fn same_crate_reexport_rename() { 637 fn same_crate_reexport_rename() {
597 let code = r#" 638 check_found_path(
598 //- /main.rs 639 r#"
599 mod bar { 640//- /main.rs
600 mod foo { pub(super) struct S; } 641mod bar {
601 pub(crate) use foo::S as U; 642 mod foo { pub(super) struct S; }
602 } 643 pub(crate) use foo::S as U;
603 $0 644}
604 "#; 645$0
605 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U"); 646 "#,
647 "bar::U",
648 "bar::U",
649 "crate::bar::U",
650 "self::bar::U",
651 );
606 } 652 }
607 653
608 #[test] 654 #[test]
609 fn different_crate_reexport() { 655 fn different_crate_reexport() {
610 let code = r#" 656 check_found_path(
611 //- /main.rs crate:main deps:std 657 r#"
612 $0 658//- /main.rs crate:main deps:std
613 //- /std.rs crate:std deps:core 659$0
614 pub use core::S; 660//- /std.rs crate:std deps:core
615 //- /core.rs crate:core 661pub use core::S;
616 pub struct S; 662//- /core.rs crate:core
617 "#; 663pub struct S;
618 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 664 "#,
665 "std::S",
666 "std::S",
667 "std::S",
668 "std::S",
669 );
619 } 670 }
620 671
621 #[test] 672 #[test]
622 fn prelude() { 673 fn prelude() {
623 let code = r#" 674 check_found_path(
624 //- /main.rs crate:main deps:std 675 r#"
625 $0 676//- /main.rs crate:main deps:std
626 //- /std.rs crate:std 677$0
627 pub mod prelude { pub struct S; } 678//- /std.rs crate:std
628 #[prelude_import] 679pub mod prelude { pub struct S; }
629 pub use prelude::*; 680#[prelude_import]
630 "#; 681pub use prelude::*;
631 check_found_path(code, "S", "S", "S", "S"); 682 "#,
683 "S",
684 "S",
685 "S",
686 "S",
687 );
632 } 688 }
633 689
634 #[test] 690 #[test]
635 fn enum_variant_from_prelude() { 691 fn enum_variant_from_prelude() {
636 let code = r#" 692 let code = r#"
637 //- /main.rs crate:main deps:std 693//- /main.rs crate:main deps:std
638 $0 694$0
639 //- /std.rs crate:std 695//- /std.rs crate:std
640 pub mod prelude { 696pub mod prelude {
641 pub enum Option<T> { Some(T), None } 697 pub enum Option<T> { Some(T), None }
642 pub use Option::*; 698 pub use Option::*;
643 } 699}
644 #[prelude_import] 700#[prelude_import]
645 pub use prelude::*; 701pub use prelude::*;
646 "#; 702 "#;
647 check_found_path(code, "None", "None", "None", "None"); 703 check_found_path(code, "None", "None", "None", "None");
648 check_found_path(code, "Some", "Some", "Some", "Some"); 704 check_found_path(code, "Some", "Some", "Some", "Some");
@@ -650,71 +706,85 @@ mod tests {
650 706
651 #[test] 707 #[test]
652 fn shortest_path() { 708 fn shortest_path() {
653 let code = r#" 709 check_found_path(
654 //- /main.rs 710 r#"
655 pub mod foo; 711//- /main.rs
656 pub mod baz; 712pub mod foo;
657 struct S; 713pub mod baz;
658 $0 714struct S;
659 //- /foo.rs 715$0
660 pub mod bar { pub struct S; } 716//- /foo.rs
661 //- /baz.rs 717pub mod bar { pub struct S; }
662 pub use crate::foo::bar::S; 718//- /baz.rs
663 "#; 719pub use crate::foo::bar::S;
664 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S"); 720 "#,
721 "baz::S",
722 "baz::S",
723 "crate::baz::S",
724 "self::baz::S",
725 );
665 } 726 }
666 727
667 #[test] 728 #[test]
668 fn discount_private_imports() { 729 fn discount_private_imports() {
669 let code = r#" 730 check_found_path(
670 //- /main.rs 731 r#"
671 mod foo; 732//- /main.rs
672 pub mod bar { pub struct S; } 733mod foo;
673 use bar::S; 734pub mod bar { pub struct S; }
674 //- /foo.rs 735use bar::S;
675 $0 736//- /foo.rs
676 "#; 737$0
677 // crate::S would be shorter, but using private imports seems wrong 738 "#,
678 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S"); 739 // crate::S would be shorter, but using private imports seems wrong
740 "crate::bar::S",
741 "crate::bar::S",
742 "crate::bar::S",
743 "crate::bar::S",
744 );
679 } 745 }
680 746
681 #[test] 747 #[test]
682 fn import_cycle() { 748 fn import_cycle() {
683 let code = r#" 749 check_found_path(
684 //- /main.rs 750 r#"
685 pub mod foo; 751//- /main.rs
686 pub mod bar; 752pub mod foo;
687 pub mod baz; 753pub mod bar;
688 //- /bar.rs 754pub mod baz;
689 $0 755//- /bar.rs
690 //- /foo.rs 756$0
691 pub use super::baz; 757//- /foo.rs
692 pub struct S; 758pub use super::baz;
693 //- /baz.rs 759pub struct S;
694 pub use super::foo; 760//- /baz.rs
695 "#; 761pub use super::foo;
696 check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S"); 762 "#,
763 "crate::foo::S",
764 "crate::foo::S",
765 "crate::foo::S",
766 "crate::foo::S",
767 );
697 } 768 }
698 769
699 #[test] 770 #[test]
700 fn prefer_std_paths_over_alloc() { 771 fn prefer_std_paths_over_alloc() {
701 cov_mark::check!(prefer_std_paths); 772 cov_mark::check!(prefer_std_paths);
702 let code = r#" 773 check_found_path(
703 //- /main.rs crate:main deps:alloc,std 774 r#"
704 $0 775//- /main.rs crate:main deps:alloc,std
776$0
705 777
706 //- /std.rs crate:std deps:alloc 778//- /std.rs crate:std deps:alloc
707 pub mod sync { 779pub mod sync {
708 pub use alloc::sync::Arc; 780 pub use alloc::sync::Arc;
709 } 781}
710 782
711 //- /zzz.rs crate:alloc 783//- /zzz.rs crate:alloc
712 pub mod sync { 784pub mod sync {
713 pub struct Arc; 785 pub struct Arc;
714 } 786}
715 "#; 787 "#,
716 check_found_path(
717 code,
718 "std::sync::Arc", 788 "std::sync::Arc",
719 "std::sync::Arc", 789 "std::sync::Arc",
720 "std::sync::Arc", 790 "std::sync::Arc",
@@ -725,26 +795,25 @@ mod tests {
725 #[test] 795 #[test]
726 fn prefer_core_paths_over_std() { 796 fn prefer_core_paths_over_std() {
727 cov_mark::check!(prefer_no_std_paths); 797 cov_mark::check!(prefer_no_std_paths);
728 let code = r#" 798 check_found_path(
729 //- /main.rs crate:main deps:core,std 799 r#"
730 #![no_std] 800//- /main.rs crate:main deps:core,std
801#![no_std]
731 802
732 $0 803$0
733 804
734 //- /std.rs crate:std deps:core 805//- /std.rs crate:std deps:core
735 806
736 pub mod fmt { 807pub mod fmt {
737 pub use core::fmt::Error; 808 pub use core::fmt::Error;
738 } 809}
739 810
740 //- /zzz.rs crate:core 811//- /zzz.rs crate:core
741 812
742 pub mod fmt { 813pub mod fmt {
743 pub struct Error; 814 pub struct Error;
744 } 815}
745 "#; 816 "#,
746 check_found_path(
747 code,
748 "core::fmt::Error", 817 "core::fmt::Error",
749 "core::fmt::Error", 818 "core::fmt::Error",
750 "core::fmt::Error", 819 "core::fmt::Error",
@@ -754,26 +823,25 @@ mod tests {
754 823
755 #[test] 824 #[test]
756 fn prefer_alloc_paths_over_std() { 825 fn prefer_alloc_paths_over_std() {
757 let code = r#" 826 check_found_path(
758 //- /main.rs crate:main deps:alloc,std 827 r#"
759 #![no_std] 828//- /main.rs crate:main deps:alloc,std
829#![no_std]
760 830
761 $0 831$0
762 832
763 //- /std.rs crate:std deps:alloc 833//- /std.rs crate:std deps:alloc
764 834
765 pub mod sync { 835pub mod sync {
766 pub use alloc::sync::Arc; 836 pub use alloc::sync::Arc;
767 } 837}
768 838
769 //- /zzz.rs crate:alloc 839//- /zzz.rs crate:alloc
770 840
771 pub mod sync { 841pub mod sync {
772 pub struct Arc; 842 pub struct Arc;
773 } 843}
774 "#; 844 "#,
775 check_found_path(
776 code,
777 "alloc::sync::Arc", 845 "alloc::sync::Arc",
778 "alloc::sync::Arc", 846 "alloc::sync::Arc",
779 "alloc::sync::Arc", 847 "alloc::sync::Arc",
@@ -783,20 +851,19 @@ mod tests {
783 851
784 #[test] 852 #[test]
785 fn prefer_shorter_paths_if_not_alloc() { 853 fn prefer_shorter_paths_if_not_alloc() {
786 let code = r#" 854 check_found_path(
787 //- /main.rs crate:main deps:megaalloc,std 855 r#"
788 $0 856//- /main.rs crate:main deps:megaalloc,std
857$0
789 858
790 //- /std.rs crate:std deps:megaalloc 859//- /std.rs crate:std deps:megaalloc
791 pub mod sync { 860pub mod sync {
792 pub use megaalloc::sync::Arc; 861 pub use megaalloc::sync::Arc;
793 } 862}
794 863
795 //- /zzz.rs crate:megaalloc 864//- /zzz.rs crate:megaalloc
796 pub struct Arc; 865pub struct Arc;
797 "#; 866 "#,
798 check_found_path(
799 code,
800 "megaalloc::Arc", 867 "megaalloc::Arc",
801 "megaalloc::Arc", 868 "megaalloc::Arc",
802 "megaalloc::Arc", 869 "megaalloc::Arc",
@@ -807,12 +874,12 @@ mod tests {
807 #[test] 874 #[test]
808 fn builtins_are_in_scope() { 875 fn builtins_are_in_scope() {
809 let code = r#" 876 let code = r#"
810 //- /main.rs 877//- /main.rs
811 $0 878$0
812 879
813 pub mod primitive { 880pub mod primitive {
814 pub use u8; 881 pub use u8;
815 } 882}
816 "#; 883 "#;
817 check_found_path(code, "u8", "u8", "u8", "u8"); 884 check_found_path(code, "u8", "u8", "u8", "u8");
818 check_found_path(code, "u16", "u16", "u16", "u16"); 885 check_found_path(code, "u16", "u16", "u16", "u16");
@@ -822,10 +889,10 @@ mod tests {
822 fn inner_items() { 889 fn inner_items() {
823 check_found_path( 890 check_found_path(
824 r#" 891 r#"
825 fn main() { 892fn main() {
826 struct Inner {} 893 struct Inner {}
827 $0 894 $0
828 } 895}
829 "#, 896 "#,
830 "Inner", 897 "Inner",
831 "Inner", 898 "Inner",
@@ -838,12 +905,12 @@ mod tests {
838 fn inner_items_from_outer_scope() { 905 fn inner_items_from_outer_scope() {
839 check_found_path( 906 check_found_path(
840 r#" 907 r#"
841 fn main() { 908fn main() {
842 struct Struct {} 909 struct Struct {}
843 { 910 {
844 $0 911 $0
845 } 912 }
846 } 913}
847 "#, 914 "#,
848 "Struct", 915 "Struct",
849 "Struct", 916 "Struct",
@@ -857,14 +924,14 @@ mod tests {
857 cov_mark::check!(prefixed_in_block_expression); 924 cov_mark::check!(prefixed_in_block_expression);
858 check_found_path( 925 check_found_path(
859 r#" 926 r#"
860 fn main() { 927fn main() {
861 mod module { 928 mod module {
862 struct Struct {} 929 struct Struct {}
863 } 930 }
864 { 931 {
865 $0 932 $0
866 } 933 }
867 } 934}
868 "#, 935 "#,
869 "module::Struct", 936 "module::Struct",
870 "module::Struct", 937 "module::Struct",
@@ -877,14 +944,14 @@ mod tests {
877 fn outer_items_with_inner_items_present() { 944 fn outer_items_with_inner_items_present() {
878 check_found_path( 945 check_found_path(
879 r#" 946 r#"
880 mod module { 947mod module {
881 pub struct CompleteMe; 948 pub struct CompleteMe;
882 } 949}
883 950
884 fn main() { 951fn main() {
885 fn inner() {} 952 fn inner() {}
886 $0 953 $0
887 } 954}
888 "#, 955 "#,
889 "module::CompleteMe", 956 "module::CompleteMe",
890 "module::CompleteMe", 957 "module::CompleteMe",
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 8d37f4f92..246f10a0a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,4 +1,4 @@
1use std::iter::once; 1use std::{iter::once, mem};
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
@@ -102,7 +102,7 @@ fn move_in_direction(
102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
105 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(range, node, &match direction {
106 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
107 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
108 }?)) 108 }?))
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
125 .next(); 125 .next();
126 126
127 if let Some((l, r)) = list_lookup { 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(l.syntax(), r.syntax())) 128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else { 129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum). 130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example), 131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
@@ -134,11 +134,38 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
134 } 134 }
135} 135}
136 136
137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes<'a>(
138 range: TextRange,
139 mut first: &'a SyntaxNode,
140 mut second: &'a SyntaxNode,
141) -> TextEdit {
142 let cursor_offset = if range.is_empty() {
143 // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges
144 if first.text_range().contains_range(range) {
145 Some(range.start() - first.text_range().start())
146 } else if second.text_range().contains_range(range) {
147 mem::swap(&mut first, &mut second);
148 Some(range.start() - first.text_range().start())
149 } else {
150 None
151 }
152 } else {
153 None
154 };
155
156 let first_with_cursor = match cursor_offset {
157 Some(offset) => {
158 let mut item_text = first.text().to_string();
159 item_text.insert_str(offset.into(), "$0");
160 item_text
161 }
162 None => first.text().to_string(),
163 };
164
138 let mut edit = TextEditBuilder::default(); 165 let mut edit = TextEditBuilder::default();
139 166
140 algo::diff(first, second).into_text_edit(&mut edit); 167 algo::diff(first, second).into_text_edit(&mut edit);
141 algo::diff(second, first).into_text_edit(&mut edit); 168 edit.replace(second.text_range(), first_with_cursor);
142 169
143 edit.finish() 170 edit.finish()
144} 171}
@@ -188,7 +215,7 @@ fn main() {
188 expect![[r#" 215 expect![[r#"
189fn main() { 216fn main() {
190 match true { 217 match true {
191 false => { 218 false =>$0 {
192 println!("Test"); 219 println!("Test");
193 }, 220 },
194 true => { 221 true => {
@@ -222,7 +249,7 @@ fn main() {
222 false => { 249 false => {
223 println!("Test"); 250 println!("Test");
224 }, 251 },
225 true => { 252 true =>$0 {
226 println!("Hello, world"); 253 println!("Hello, world");
227 } 254 }
228 }; 255 };
@@ -274,7 +301,7 @@ fn main() {
274 "#, 301 "#,
275 expect![[r#" 302 expect![[r#"
276fn main() { 303fn main() {
277 let test2 = 456; 304 let test2$0 = 456;
278 let test = 123; 305 let test = 123;
279} 306}
280 "#]], 307 "#]],
@@ -293,7 +320,7 @@ fn main() {
293 "#, 320 "#,
294 expect![[r#" 321 expect![[r#"
295fn main() { 322fn main() {
296 println!("All I want to say is..."); 323 println!("All I want to say is...");$0
297 println!("Hello, world"); 324 println!("Hello, world");
298} 325}
299 "#]], 326 "#]],
@@ -313,7 +340,7 @@ fn main() {
313fn main() { 340fn main() {
314 if true { 341 if true {
315 println!("Test"); 342 println!("Test");
316 } 343 }$0
317 344
318 println!("Hello, world"); 345 println!("Hello, world");
319} 346}
@@ -334,7 +361,7 @@ fn main() {
334fn main() { 361fn main() {
335 for i in 0..10 { 362 for i in 0..10 {
336 println!("Test"); 363 println!("Test");
337 } 364 }$0
338 365
339 println!("Hello, world"); 366 println!("Hello, world");
340} 367}
@@ -355,7 +382,7 @@ fn main() {
355fn main() { 382fn main() {
356 loop { 383 loop {
357 println!("Test"); 384 println!("Test");
358 } 385 }$0
359 386
360 println!("Hello, world"); 387 println!("Hello, world");
361} 388}
@@ -376,7 +403,7 @@ fn main() {
376fn main() { 403fn main() {
377 while true { 404 while true {
378 println!("Test"); 405 println!("Test");
379 } 406 }$0
380 407
381 println!("Hello, world"); 408 println!("Hello, world");
382} 409}
@@ -393,7 +420,7 @@ fn main() {
393 "#, 420 "#,
394 expect![[r#" 421 expect![[r#"
395fn main() { 422fn main() {
396 return 123; 423 return 123;$0
397 424
398 println!("Hello, world"); 425 println!("Hello, world");
399} 426}
@@ -430,7 +457,7 @@ fn main() {}
430fn foo() {}$0$0 457fn foo() {}$0$0
431 "#, 458 "#,
432 expect![[r#" 459 expect![[r#"
433fn foo() {} 460fn foo() {}$0
434 461
435fn main() {} 462fn main() {}
436 "#]], 463 "#]],
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{}
451 expect![[r#" 478 expect![[r#"
452struct Yay; 479struct Yay;
453 480
454impl Wow for Yay {} 481impl Wow for Yay $0{}
455 482
456trait Wow {} 483trait Wow {}
457 "#]], 484 "#]],
@@ -467,7 +494,7 @@ use std::vec::Vec;
467use std::collections::HashMap$0$0; 494use std::collections::HashMap$0$0;
468 "#, 495 "#,
469 expect![[r#" 496 expect![[r#"
470use std::collections::HashMap; 497use std::collections::HashMap$0;
471use std::vec::Vec; 498use std::vec::Vec;
472 "#]], 499 "#]],
473 Direction::Up, 500 Direction::Up,
@@ -502,7 +529,7 @@ fn main() {
502 } 529 }
503 530
504 #[test] 531 #[test]
505 fn test_moves_param_up() { 532 fn test_moves_param() {
506 check( 533 check(
507 r#" 534 r#"
508fn test(one: i32, two$0$0: u32) {} 535fn test(one: i32, two$0$0: u32) {}
@@ -512,7 +539,7 @@ fn main() {
512} 539}
513 "#, 540 "#,
514 expect![[r#" 541 expect![[r#"
515fn test(two: u32, one: i32) {} 542fn test(two$0: u32, one: i32) {}
516 543
517fn main() { 544fn main() {
518 test(123, 456); 545 test(123, 456);
@@ -520,6 +547,15 @@ fn main() {
520 "#]], 547 "#]],
521 Direction::Up, 548 Direction::Up,
522 ); 549 );
550 check(
551 r#"
552fn f($0$0arg: u8, arg2: u16) {}
553 "#,
554 expect![[r#"
555fn f(arg2: u16, $0arg: u8) {}
556 "#]],
557 Direction::Down,
558 );
523 } 559 }
524 560
525 #[test] 561 #[test]
@@ -536,7 +572,7 @@ fn main() {
536fn test(one: i32, two: u32) {} 572fn test(one: i32, two: u32) {}
537 573
538fn main() { 574fn main() {
539 test(456, 123); 575 test(456$0, 123);
540} 576}
541 "#]], 577 "#]],
542 Direction::Up, 578 Direction::Up,
@@ -557,7 +593,7 @@ fn main() {
557fn test(one: i32, two: u32) {} 593fn test(one: i32, two: u32) {}
558 594
559fn main() { 595fn main() {
560 test(456, 123); 596 test(456, 123$0);
561} 597}
562 "#]], 598 "#]],
563 Direction::Down, 599 Direction::Down,
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
594fn main() {} 630fn main() {}
595 "#, 631 "#,
596 expect![[r#" 632 expect![[r#"
597struct Test<B, A>(A, B); 633struct Test<B$0, A>(A, B);
598 634
599fn main() {} 635fn main() {}
600 "#]], 636 "#]],
@@ -616,7 +652,7 @@ fn main() {
616struct Test<A, B>(A, B); 652struct Test<A, B>(A, B);
617 653
618fn main() { 654fn main() {
619 let t = Test::<&str, i32>(123, "yay"); 655 let t = Test::<&str$0, i32>(123, "yay");
620} 656}
621 "#]], 657 "#]],
622 Direction::Up, 658 Direction::Up,
@@ -636,7 +672,7 @@ fn main() {}
636 "#, 672 "#,
637 expect![[r#" 673 expect![[r#"
638enum Hello { 674enum Hello {
639 Two, 675 Two$0,
640 One 676 One
641} 677}
642 678
@@ -663,7 +699,7 @@ trait One {}
663 699
664trait Two {} 700trait Two {}
665 701
666fn test<T: Two + One>(t: T) {} 702fn test<T: Two$0 + One>(t: T) {}
667 703
668fn main() {} 704fn main() {}
669 "#]], 705 "#]],
@@ -709,7 +745,7 @@ trait Yay {
709impl Yay for Test { 745impl Yay for Test {
710 type One = i32; 746 type One = i32;
711 747
712 fn inner() { 748 fn inner() {$0
713 println!("Mmmm"); 749 println!("Mmmm");
714 } 750 }
715 751
@@ -736,7 +772,7 @@ fn test() {
736 "#, 772 "#,
737 expect![[r#" 773 expect![[r#"
738fn test() { 774fn test() {
739 mod hi { 775 mod hi {$0
740 fn inner() {} 776 fn inner() {}
741 } 777 }
742 778
@@ -764,7 +800,7 @@ fn main() {}
764 expect![[r#" 800 expect![[r#"
765fn main() {} 801fn main() {}
766 802
767#[derive(Debug)] 803$0#[derive(Debug)]
768enum FooBar { 804enum FooBar {
769 Foo, 805 Foo,
770 Bar, 806 Bar,
@@ -784,7 +820,7 @@ fn main() {}
784 expect![[r#" 820 expect![[r#"
785fn main() {} 821fn main() {}
786 822
787enum FooBar { 823$0enum FooBar {
788 Foo, 824 Foo,
789 Bar, 825 Bar,
790} 826}
@@ -804,7 +840,7 @@ fn main() {}
804 expect![[r#" 840 expect![[r#"
805struct Test; 841struct Test;
806 842
807impl SomeTrait for Test {} 843$0impl SomeTrait for Test {}
808 844
809trait SomeTrait {} 845trait SomeTrait {}
810 846
@@ -831,7 +867,7 @@ fn main() {}
831enum FooBar { 867enum FooBar {
832 Foo, 868 Foo,
833 Bar, 869 Bar,
834} 870}$0
835 "#]], 871 "#]],
836 Direction::Down, 872 Direction::Down,
837 ); 873 );
@@ -848,7 +884,7 @@ fn main() {}
848 expect![[r#" 884 expect![[r#"
849struct Test; 885struct Test;
850 886
851impl SomeTrait for Test {} 887impl SomeTrait for Test {}$0
852 888
853trait SomeTrait {} 889trait SomeTrait {}
854 890
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f0c9d23c..1f59402e5 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1410,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml(
1410pub(crate) fn handle_move_item( 1410pub(crate) fn handle_move_item(
1411 snap: GlobalStateSnapshot, 1411 snap: GlobalStateSnapshot,
1412 params: lsp_ext::MoveItemParams, 1412 params: lsp_ext::MoveItemParams,
1413) -> Result<Option<lsp_types::TextDocumentEdit>> { 1413) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
1414 let _p = profile::span("handle_move_item"); 1414 let _p = profile::span("handle_move_item");
1415 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 1415 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1416 let range = from_proto::file_range(&snap, params.text_document, params.range)?; 1416 let range = from_proto::file_range(&snap, params.text_document, params.range)?;
@@ -1421,8 +1421,11 @@ pub(crate) fn handle_move_item(
1421 }; 1421 };
1422 1422
1423 match snap.analysis.move_item(range, direction)? { 1423 match snap.analysis.move_item(range, direction)? {
1424 Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)), 1424 Some(text_edit) => {
1425 None => Ok(None), 1425 let line_index = snap.file_line_index(file_id)?;
1426 Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
1427 }
1428 None => Ok(vec![]),
1426 } 1429 }
1427} 1430}
1428 1431
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 81a6f22f1..d648cda32 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -407,7 +407,7 @@ pub enum MoveItem {}
407 407
408impl Request for MoveItem { 408impl Request for MoveItem {
409 type Params = MoveItemParams; 409 type Params = MoveItemParams;
410 type Result = Option<lsp_types::TextDocumentEdit>; 410 type Result = Vec<SnippetTextEdit>;
411 const METHOD: &'static str = "experimental/moveItem"; 411 const METHOD: &'static str = "experimental/moveItem";
412} 412}
413 413
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 8d7cb9b74..1a1f65f3b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -688,18 +688,6 @@ pub(crate) fn goto_definition_response(
688 } 688 }
689} 689}
690 690
691pub(crate) fn text_document_edit(
692 snap: &GlobalStateSnapshot,
693 file_id: FileId,
694 edit: TextEdit,
695) -> Result<lsp_types::TextDocumentEdit> {
696 let text_document = optional_versioned_text_document_identifier(snap, file_id);
697 let line_index = snap.file_line_index(file_id)?;
698 let edits =
699 edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect();
700 Ok(lsp_types::TextDocumentEdit { text_document, edits })
701}
702
703pub(crate) fn snippet_text_document_edit( 691pub(crate) fn snippet_text_document_edit(
704 snap: &GlobalStateSnapshot, 692 snap: &GlobalStateSnapshot,
705 is_snippet: bool, 693 is_snippet: bool,
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index a46121bb2..a4d92242b 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,8 +1,8 @@
1<!--- 1<!---
2lsp_ext.rs hash: faae991334a151d0 2lsp_ext.rs hash: b19ddc3ab8767af9
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
6 6
7 https://github.com/rust-analyzer/rust-analyzer/issues/4604 7 https://github.com/rust-analyzer/rust-analyzer/issues/4604
8 8
@@ -620,7 +620,7 @@ This request is sent from client to server to move item under cursor or selectio
620 620
621**Request:** `MoveItemParams` 621**Request:** `MoveItemParams`
622 622
623**Response:** `TextDocumentEdit | null` 623**Response:** `SnippetTextEdit[]`
624 624
625```typescript 625```typescript
626export interface MoveItemParams { 626export interface MoveItemParams {
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 1a0805bd3..4092435db 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -148,34 +148,16 @@ export function moveItem(ctx: Ctx, direction: ra.Direction): Cmd {
148 const client = ctx.client; 148 const client = ctx.client;
149 if (!editor || !client) return; 149 if (!editor || !client) return;
150 150
151 const edit = await client.sendRequest(ra.moveItem, { 151 const lcEdits = await client.sendRequest(ra.moveItem, {
152 range: client.code2ProtocolConverter.asRange(editor.selection), 152 range: client.code2ProtocolConverter.asRange(editor.selection),
153 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), 153 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
154 direction 154 direction
155 }); 155 });
156 156
157 if (!edit) return; 157 if (!lcEdits) return;
158 158
159 let cursor: vscode.Position | null = null; 159 const edits = client.protocol2CodeConverter.asTextEdits(lcEdits);
160 160 await applySnippetTextEdits(editor, edits);
161 await editor.edit((builder) => {
162 client.protocol2CodeConverter.asTextEdits(edit.edits).forEach((edit: any) => {
163 builder.replace(edit.range, edit.newText);
164
165 if (direction === ra.Direction.Up) {
166 if (!cursor || edit.range.end.isBeforeOrEqual(cursor)) {
167 cursor = edit.range.end;
168 }
169 } else {
170 if (!cursor || edit.range.end.isAfterOrEqual(cursor)) {
171 cursor = edit.range.end;
172 }
173 }
174 });
175 }).then(() => {
176 const newPosition = cursor ?? editor.selection.start;
177 editor.selection = new vscode.Selection(newPosition, newPosition);
178 });
179 }; 161 };
180} 162}
181 163
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index e453bb9e0..f78de894b 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -129,7 +129,7 @@ export interface OpenCargoTomlParams {
129 textDocument: lc.TextDocumentIdentifier; 129 textDocument: lc.TextDocumentIdentifier;
130} 130}
131 131
132export const moveItem = new lc.RequestType<MoveItemParams, lc.TextDocumentEdit | void, void>("experimental/moveItem"); 132export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem");
133 133
134export interface MoveItemParams { 134export interface MoveItemParams {
135 textDocument: lc.TextDocumentIdentifier; 135 textDocument: lc.TextDocumentIdentifier;