diff options
-rw-r--r-- | crates/ra_assists/src/doc_tests/generated.rs | 32 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 4 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs (renamed from crates/ra_assists/src/handlers/add_import.rs) | 114 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 136 | ||||
-rw-r--r-- | crates/ra_parser/src/syntax_kind/generated.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 2 | ||||
-rw-r--r-- | docs/dev/README.md | 12 | ||||
-rw-r--r-- | docs/user/README.md | 18 | ||||
-rw-r--r-- | docs/user/assists.md | 28 | ||||
-rw-r--r-- | editors/emacs/rust-analyzer.el | 295 | ||||
-rw-r--r-- | xtask/src/lib.rs | 3 |
14 files changed, 110 insertions, 552 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 0d95b957b..4ab09b167 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen` | 1 | //! Generated file, do not edit by hand, see `xtask/src/codegen` |
2 | 2 | ||
3 | use super::check; | 3 | use super::check; |
4 | 4 | ||
@@ -161,21 +161,6 @@ impl Trait<u32> for () { | |||
161 | } | 161 | } |
162 | 162 | ||
163 | #[test] | 163 | #[test] |
164 | fn doctest_add_import() { | ||
165 | check( | ||
166 | "add_import", | ||
167 | r#####" | ||
168 | fn process(map: std::collections::<|>HashMap<String, String>) {} | ||
169 | "#####, | ||
170 | r#####" | ||
171 | use std::collections::HashMap; | ||
172 | |||
173 | fn process(map: HashMap<String, String>) {} | ||
174 | "#####, | ||
175 | ) | ||
176 | } | ||
177 | |||
178 | #[test] | ||
179 | fn doctest_add_new() { | 164 | fn doctest_add_new() { |
180 | check( | 165 | check( |
181 | "add_new", | 166 | "add_new", |
@@ -592,6 +577,21 @@ fn handle(action: Action) { | |||
592 | } | 577 | } |
593 | 578 | ||
594 | #[test] | 579 | #[test] |
580 | fn doctest_replace_qualified_name_with_use() { | ||
581 | check( | ||
582 | "replace_qualified_name_with_use", | ||
583 | r#####" | ||
584 | fn process(map: std::collections::<|>HashMap<String, String>) {} | ||
585 | "#####, | ||
586 | r#####" | ||
587 | use std::collections::HashMap; | ||
588 | |||
589 | fn process(map: HashMap<String, String>) {} | ||
590 | "#####, | ||
591 | ) | ||
592 | } | ||
593 | |||
594 | #[test] | ||
595 | fn doctest_split_import() { | 595 | fn doctest_split_import() { |
596 | check( | 596 | check( |
597 | "split_import", | 597 | "split_import", |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 84b5474f9..4514b8691 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -7,7 +7,7 @@ use ra_syntax::{ | |||
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | assist_ctx::{ActionBuilder, Assist, AssistCtx}, | 9 | assist_ctx::{ActionBuilder, Assist, AssistCtx}, |
10 | auto_import_text_edit, AssistId, | 10 | insert_use_statement, AssistId, |
11 | }; | 11 | }; |
12 | use std::collections::BTreeSet; | 12 | use std::collections::BTreeSet; |
13 | 13 | ||
@@ -78,7 +78,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
78 | fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | 78 | fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { |
79 | let mut action_builder = ActionBuilder::default(); | 79 | let mut action_builder = ActionBuilder::default(); |
80 | action_builder.label(format!("Import `{}`", &import)); | 80 | action_builder.label(format!("Import `{}`", &import)); |
81 | auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder()); | 81 | insert_use_statement(position, anchor, &import, action_builder.text_edit_builder()); |
82 | action_builder | 82 | action_builder |
83 | } | 83 | } |
84 | 84 | ||
diff --git a/crates/ra_assists/src/handlers/add_import.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index f03dddac8..b70c88ec2 100644 --- a/crates/ra_assists/src/handlers/add_import.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -12,10 +12,10 @@ use crate::{ | |||
12 | AssistId, | 12 | AssistId, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | /// This function produces sequence of text edits into edit | 15 | /// Creates and inserts a use statement for the given path to import. |
16 | /// to import the target path in the most appropriate scope given | 16 | /// The use statement is inserted in the scope most appropriate to the |
17 | /// the cursor position | 17 | /// the cursor position given, additionally merged with the existing use imports. |
18 | pub fn auto_import_text_edit( | 18 | pub fn insert_use_statement( |
19 | // Ideally the position of the cursor, used to | 19 | // Ideally the position of the cursor, used to |
20 | position: &SyntaxNode, | 20 | position: &SyntaxNode, |
21 | // The statement to use as anchor (last resort) | 21 | // The statement to use as anchor (last resort) |
@@ -37,9 +37,9 @@ pub fn auto_import_text_edit( | |||
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
40 | // Assist: add_import | 40 | // Assist: replace_qualified_name_with_use |
41 | // | 41 | // |
42 | // Adds a use statement for a given fully-qualified path. | 42 | // Adds a use statement for a given fully-qualified name. |
43 | // | 43 | // |
44 | // ``` | 44 | // ``` |
45 | // fn process(map: std::collections::<|>HashMap<String, String>) {} | 45 | // fn process(map: std::collections::<|>HashMap<String, String>) {} |
@@ -50,7 +50,7 @@ pub fn auto_import_text_edit( | |||
50 | // | 50 | // |
51 | // fn process(map: HashMap<String, String>) {} | 51 | // fn process(map: HashMap<String, String>) {} |
52 | // ``` | 52 | // ``` |
53 | pub(crate) fn add_import(ctx: AssistCtx) -> Option<Assist> { | 53 | pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> { |
54 | let path: ast::Path = ctx.find_node_at_offset()?; | 54 | let path: ast::Path = ctx.find_node_at_offset()?; |
55 | // We don't want to mess with use statements | 55 | // We don't want to mess with use statements |
56 | if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { | 56 | if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { |
@@ -72,9 +72,13 @@ pub(crate) fn add_import(ctx: AssistCtx) -> Option<Assist> { | |||
72 | } | 72 | } |
73 | }; | 73 | }; |
74 | 74 | ||
75 | ctx.add_assist(AssistId("add_import"), format!("Import {}", fmt_segments(&segments)), |edit| { | 75 | ctx.add_assist( |
76 | apply_auto_import(&position, &path, &segments, edit.text_edit_builder()); | 76 | AssistId("replace_qualified_name_with_use"), |
77 | }) | 77 | "Replace qualified path with use", |
78 | |edit| { | ||
79 | replace_with_use(&position, &path, &segments, edit.text_edit_builder()); | ||
80 | }, | ||
81 | ) | ||
78 | } | 82 | } |
79 | 83 | ||
80 | fn collect_path_segments_raw( | 84 | fn collect_path_segments_raw( |
@@ -107,12 +111,6 @@ fn collect_path_segments_raw( | |||
107 | Some(segments.len() - oldlen) | 111 | Some(segments.len() - oldlen) |
108 | } | 112 | } |
109 | 113 | ||
110 | fn fmt_segments(segments: &[SmolStr]) -> String { | ||
111 | let mut buf = String::new(); | ||
112 | fmt_segments_raw(segments, &mut buf); | ||
113 | buf | ||
114 | } | ||
115 | |||
116 | fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) { | 114 | fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) { |
117 | let mut iter = segments.iter(); | 115 | let mut iter = segments.iter(); |
118 | if let Some(s) = iter.next() { | 116 | if let Some(s) = iter.next() { |
@@ -558,7 +556,7 @@ fn make_assist_add_nested_import( | |||
558 | } | 556 | } |
559 | } | 557 | } |
560 | 558 | ||
561 | fn apply_auto_import( | 559 | fn replace_with_use( |
562 | container: &SyntaxNode, | 560 | container: &SyntaxNode, |
563 | path: &ast::Path, | 561 | path: &ast::Path, |
564 | target: &[SmolStr], | 562 | target: &[SmolStr], |
@@ -567,7 +565,7 @@ fn apply_auto_import( | |||
567 | let action = best_action_for_target(container.clone(), path.syntax().clone(), target); | 565 | let action = best_action_for_target(container.clone(), path.syntax().clone(), target); |
568 | make_assist(&action, target, edit); | 566 | make_assist(&action, target, edit); |
569 | if let Some(last) = path.segment() { | 567 | if let Some(last) = path.segment() { |
570 | // Here we are assuming the assist will provide a correct use statement | 568 | // Here we are assuming the assist will provide a correct use statement |
571 | // so we can delete the path qualifier | 569 | // so we can delete the path qualifier |
572 | edit.delete(TextRange::from_to( | 570 | edit.delete(TextRange::from_to( |
573 | path.syntax().text_range().start(), | 571 | path.syntax().text_range().start(), |
@@ -603,9 +601,9 @@ mod tests { | |||
603 | use super::*; | 601 | use super::*; |
604 | 602 | ||
605 | #[test] | 603 | #[test] |
606 | fn test_auto_import_add_use_no_anchor() { | 604 | fn test_replace_add_use_no_anchor() { |
607 | check_assist( | 605 | check_assist( |
608 | add_import, | 606 | replace_qualified_name_with_use, |
609 | " | 607 | " |
610 | std::fmt::Debug<|> | 608 | std::fmt::Debug<|> |
611 | ", | 609 | ", |
@@ -617,9 +615,9 @@ Debug<|> | |||
617 | ); | 615 | ); |
618 | } | 616 | } |
619 | #[test] | 617 | #[test] |
620 | fn test_auto_import_add_use_no_anchor_with_item_below() { | 618 | fn test_replace_add_use_no_anchor_with_item_below() { |
621 | check_assist( | 619 | check_assist( |
622 | add_import, | 620 | replace_qualified_name_with_use, |
623 | " | 621 | " |
624 | std::fmt::Debug<|> | 622 | std::fmt::Debug<|> |
625 | 623 | ||
@@ -638,9 +636,9 @@ fn main() { | |||
638 | } | 636 | } |
639 | 637 | ||
640 | #[test] | 638 | #[test] |
641 | fn test_auto_import_add_use_no_anchor_with_item_above() { | 639 | fn test_replace_add_use_no_anchor_with_item_above() { |
642 | check_assist( | 640 | check_assist( |
643 | add_import, | 641 | replace_qualified_name_with_use, |
644 | " | 642 | " |
645 | fn main() { | 643 | fn main() { |
646 | } | 644 | } |
@@ -659,9 +657,9 @@ Debug<|> | |||
659 | } | 657 | } |
660 | 658 | ||
661 | #[test] | 659 | #[test] |
662 | fn test_auto_import_add_use_no_anchor_2seg() { | 660 | fn test_replace_add_use_no_anchor_2seg() { |
663 | check_assist( | 661 | check_assist( |
664 | add_import, | 662 | replace_qualified_name_with_use, |
665 | " | 663 | " |
666 | std::fmt<|>::Debug | 664 | std::fmt<|>::Debug |
667 | ", | 665 | ", |
@@ -674,9 +672,9 @@ fmt<|>::Debug | |||
674 | } | 672 | } |
675 | 673 | ||
676 | #[test] | 674 | #[test] |
677 | fn test_auto_import_add_use() { | 675 | fn test_replace_add_use() { |
678 | check_assist( | 676 | check_assist( |
679 | add_import, | 677 | replace_qualified_name_with_use, |
680 | " | 678 | " |
681 | use stdx; | 679 | use stdx; |
682 | 680 | ||
@@ -694,9 +692,9 @@ impl Debug<|> for Foo { | |||
694 | } | 692 | } |
695 | 693 | ||
696 | #[test] | 694 | #[test] |
697 | fn test_auto_import_file_use_other_anchor() { | 695 | fn test_replace_file_use_other_anchor() { |
698 | check_assist( | 696 | check_assist( |
699 | add_import, | 697 | replace_qualified_name_with_use, |
700 | " | 698 | " |
701 | impl std::fmt::Debug<|> for Foo { | 699 | impl std::fmt::Debug<|> for Foo { |
702 | } | 700 | } |
@@ -711,9 +709,9 @@ impl Debug<|> for Foo { | |||
711 | } | 709 | } |
712 | 710 | ||
713 | #[test] | 711 | #[test] |
714 | fn test_auto_import_add_use_other_anchor_indent() { | 712 | fn test_replace_add_use_other_anchor_indent() { |
715 | check_assist( | 713 | check_assist( |
716 | add_import, | 714 | replace_qualified_name_with_use, |
717 | " | 715 | " |
718 | impl std::fmt::Debug<|> for Foo { | 716 | impl std::fmt::Debug<|> for Foo { |
719 | } | 717 | } |
@@ -728,9 +726,9 @@ impl Debug<|> for Foo { | |||
728 | } | 726 | } |
729 | 727 | ||
730 | #[test] | 728 | #[test] |
731 | fn test_auto_import_split_different() { | 729 | fn test_replace_split_different() { |
732 | check_assist( | 730 | check_assist( |
733 | add_import, | 731 | replace_qualified_name_with_use, |
734 | " | 732 | " |
735 | use std::fmt; | 733 | use std::fmt; |
736 | 734 | ||
@@ -747,9 +745,9 @@ impl io<|> for Foo { | |||
747 | } | 745 | } |
748 | 746 | ||
749 | #[test] | 747 | #[test] |
750 | fn test_auto_import_split_self_for_use() { | 748 | fn test_replace_split_self_for_use() { |
751 | check_assist( | 749 | check_assist( |
752 | add_import, | 750 | replace_qualified_name_with_use, |
753 | " | 751 | " |
754 | use std::fmt; | 752 | use std::fmt; |
755 | 753 | ||
@@ -766,9 +764,9 @@ impl Debug<|> for Foo { | |||
766 | } | 764 | } |
767 | 765 | ||
768 | #[test] | 766 | #[test] |
769 | fn test_auto_import_split_self_for_target() { | 767 | fn test_replace_split_self_for_target() { |
770 | check_assist( | 768 | check_assist( |
771 | add_import, | 769 | replace_qualified_name_with_use, |
772 | " | 770 | " |
773 | use std::fmt::Debug; | 771 | use std::fmt::Debug; |
774 | 772 | ||
@@ -785,9 +783,9 @@ impl fmt<|> for Foo { | |||
785 | } | 783 | } |
786 | 784 | ||
787 | #[test] | 785 | #[test] |
788 | fn test_auto_import_add_to_nested_self_nested() { | 786 | fn test_replace_add_to_nested_self_nested() { |
789 | check_assist( | 787 | check_assist( |
790 | add_import, | 788 | replace_qualified_name_with_use, |
791 | " | 789 | " |
792 | use std::fmt::{Debug, nested::{Display}}; | 790 | use std::fmt::{Debug, nested::{Display}}; |
793 | 791 | ||
@@ -804,9 +802,9 @@ impl nested<|> for Foo { | |||
804 | } | 802 | } |
805 | 803 | ||
806 | #[test] | 804 | #[test] |
807 | fn test_auto_import_add_to_nested_self_already_included() { | 805 | fn test_replace_add_to_nested_self_already_included() { |
808 | check_assist( | 806 | check_assist( |
809 | add_import, | 807 | replace_qualified_name_with_use, |
810 | " | 808 | " |
811 | use std::fmt::{Debug, nested::{self, Display}}; | 809 | use std::fmt::{Debug, nested::{self, Display}}; |
812 | 810 | ||
@@ -823,9 +821,9 @@ impl nested<|> for Foo { | |||
823 | } | 821 | } |
824 | 822 | ||
825 | #[test] | 823 | #[test] |
826 | fn test_auto_import_add_to_nested_nested() { | 824 | fn test_replace_add_to_nested_nested() { |
827 | check_assist( | 825 | check_assist( |
828 | add_import, | 826 | replace_qualified_name_with_use, |
829 | " | 827 | " |
830 | use std::fmt::{Debug, nested::{Display}}; | 828 | use std::fmt::{Debug, nested::{Display}}; |
831 | 829 | ||
@@ -842,9 +840,9 @@ impl Debug<|> for Foo { | |||
842 | } | 840 | } |
843 | 841 | ||
844 | #[test] | 842 | #[test] |
845 | fn test_auto_import_split_common_target_longer() { | 843 | fn test_replace_split_common_target_longer() { |
846 | check_assist( | 844 | check_assist( |
847 | add_import, | 845 | replace_qualified_name_with_use, |
848 | " | 846 | " |
849 | use std::fmt::Debug; | 847 | use std::fmt::Debug; |
850 | 848 | ||
@@ -861,9 +859,9 @@ impl Display<|> for Foo { | |||
861 | } | 859 | } |
862 | 860 | ||
863 | #[test] | 861 | #[test] |
864 | fn test_auto_import_split_common_use_longer() { | 862 | fn test_replace_split_common_use_longer() { |
865 | check_assist( | 863 | check_assist( |
866 | add_import, | 864 | replace_qualified_name_with_use, |
867 | " | 865 | " |
868 | use std::fmt::nested::Debug; | 866 | use std::fmt::nested::Debug; |
869 | 867 | ||
@@ -880,9 +878,9 @@ impl Display<|> for Foo { | |||
880 | } | 878 | } |
881 | 879 | ||
882 | #[test] | 880 | #[test] |
883 | fn test_auto_import_use_nested_import() { | 881 | fn test_replace_use_nested_import() { |
884 | check_assist( | 882 | check_assist( |
885 | add_import, | 883 | replace_qualified_name_with_use, |
886 | " | 884 | " |
887 | use crate::{ | 885 | use crate::{ |
888 | ty::{Substs, Ty}, | 886 | ty::{Substs, Ty}, |
@@ -903,9 +901,9 @@ fn foo() { lower<|>::trait_env() } | |||
903 | } | 901 | } |
904 | 902 | ||
905 | #[test] | 903 | #[test] |
906 | fn test_auto_import_alias() { | 904 | fn test_replace_alias() { |
907 | check_assist( | 905 | check_assist( |
908 | add_import, | 906 | replace_qualified_name_with_use, |
909 | " | 907 | " |
910 | use std::fmt as foo; | 908 | use std::fmt as foo; |
911 | 909 | ||
@@ -922,9 +920,9 @@ impl Debug<|> for Foo { | |||
922 | } | 920 | } |
923 | 921 | ||
924 | #[test] | 922 | #[test] |
925 | fn test_auto_import_not_applicable_one_segment() { | 923 | fn test_replace_not_applicable_one_segment() { |
926 | check_assist_not_applicable( | 924 | check_assist_not_applicable( |
927 | add_import, | 925 | replace_qualified_name_with_use, |
928 | " | 926 | " |
929 | impl foo<|> for Foo { | 927 | impl foo<|> for Foo { |
930 | } | 928 | } |
@@ -933,9 +931,9 @@ impl foo<|> for Foo { | |||
933 | } | 931 | } |
934 | 932 | ||
935 | #[test] | 933 | #[test] |
936 | fn test_auto_import_not_applicable_in_use() { | 934 | fn test_replace_not_applicable_in_use() { |
937 | check_assist_not_applicable( | 935 | check_assist_not_applicable( |
938 | add_import, | 936 | replace_qualified_name_with_use, |
939 | " | 937 | " |
940 | use std::fmt<|>; | 938 | use std::fmt<|>; |
941 | ", | 939 | ", |
@@ -943,9 +941,9 @@ use std::fmt<|>; | |||
943 | } | 941 | } |
944 | 942 | ||
945 | #[test] | 943 | #[test] |
946 | fn test_auto_import_add_use_no_anchor_in_mod_mod() { | 944 | fn test_replace_add_use_no_anchor_in_mod_mod() { |
947 | check_assist( | 945 | check_assist( |
948 | add_import, | 946 | replace_qualified_name_with_use, |
949 | " | 947 | " |
950 | mod foo { | 948 | mod foo { |
951 | mod bar { | 949 | mod bar { |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index eca6dec4b..f79189ae8 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -21,7 +21,7 @@ use ra_syntax::{TextRange, TextUnit}; | |||
21 | use ra_text_edit::TextEdit; | 21 | use ra_text_edit::TextEdit; |
22 | 22 | ||
23 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 23 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
24 | pub use crate::handlers::add_import::auto_import_text_edit; | 24 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; |
25 | 25 | ||
26 | /// Unique identifier of the assist, should not be shown to the user | 26 | /// Unique identifier of the assist, should not be shown to the user |
27 | /// directly. | 27 | /// directly. |
@@ -133,7 +133,7 @@ mod handlers { | |||
133 | mod replace_if_let_with_match; | 133 | mod replace_if_let_with_match; |
134 | mod split_import; | 134 | mod split_import; |
135 | mod remove_dbg; | 135 | mod remove_dbg; |
136 | pub(crate) mod add_import; | 136 | pub(crate) mod replace_qualified_name_with_use; |
137 | mod add_missing_impl_members; | 137 | mod add_missing_impl_members; |
138 | mod move_guard; | 138 | mod move_guard; |
139 | mod move_bounds; | 139 | mod move_bounds; |
@@ -158,7 +158,7 @@ mod handlers { | |||
158 | replace_if_let_with_match::replace_if_let_with_match, | 158 | replace_if_let_with_match::replace_if_let_with_match, |
159 | split_import::split_import, | 159 | split_import::split_import, |
160 | remove_dbg::remove_dbg, | 160 | remove_dbg::remove_dbg, |
161 | add_import::add_import, | 161 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
162 | add_missing_impl_members::add_missing_impl_members, | 162 | add_missing_impl_members::add_missing_impl_members, |
163 | add_missing_impl_members::add_missing_default_members, | 163 | add_missing_impl_members::add_missing_default_members, |
164 | inline_local_variable::inline_local_variable, | 164 | inline_local_variable::inline_local_variable, |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 86e422779..e1c7b7a20 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -58,7 +58,6 @@ pub use hir_def::{ | |||
58 | type_ref::Mutability, | 58 | type_ref::Mutability, |
59 | }; | 59 | }; |
60 | pub use hir_expand::{ | 60 | pub use hir_expand::{ |
61 | name::{name, Name}, | 61 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, |
62 | HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, | ||
63 | }; | 62 | }; |
64 | pub use hir_ty::{display::HirDisplay, CallableDef}; | 63 | pub use hir_ty::{display::HirDisplay, CallableDef}; |
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 133805bdb..b2e10f445 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -187,13 +187,6 @@ pub mod known { | |||
187 | PartialOrd, | 187 | PartialOrd, |
188 | Eq, | 188 | Eq, |
189 | PartialEq, | 189 | PartialEq, |
190 | // FIXME delete those after `ImportResolver` is removed. | ||
191 | hash, | ||
192 | fmt, | ||
193 | io, | ||
194 | Display, | ||
195 | Iterator, | ||
196 | Hasher, | ||
197 | ); | 190 | ); |
198 | 191 | ||
199 | // self/Self cannot be used as an identifier | 192 | // self/Self cannot be used as an identifier |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 64b04ec2b..e2ee86dd1 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,12 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_assists::auto_import_text_edit; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | use ra_syntax::{ast, AstNode, SmolStr}; | ||
5 | use ra_text_edit::TextEditBuilder; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | ||
9 | use hir::{ModPath, PathKind}; | ||
10 | 4 | ||
11 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { |
12 | if !ctx.is_trivial_path { | 6 | if !ctx.is_trivial_path { |
@@ -16,138 +10,14 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | |||
16 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | 10 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { |
17 | acc.add_resolution(ctx, name.to_string(), &res) | 11 | acc.add_resolution(ctx, name.to_string(), &res) |
18 | }); | 12 | }); |
19 | |||
20 | // auto-import | ||
21 | // We fetch ident from the original file, because we need to pre-filter auto-imports | ||
22 | if ast::NameRef::cast(ctx.token.parent()).is_some() { | ||
23 | let import_resolver = ImportResolver::new(); | ||
24 | let import_names = import_resolver.all_names(ctx.token.text()); | ||
25 | import_names.into_iter().for_each(|(name, path)| { | ||
26 | let edit = { | ||
27 | let mut builder = TextEditBuilder::default(); | ||
28 | builder.replace(ctx.source_range(), name.to_string()); | ||
29 | auto_import_text_edit( | ||
30 | &ctx.token.parent(), | ||
31 | &ctx.token.parent(), | ||
32 | &path, | ||
33 | &mut builder, | ||
34 | ); | ||
35 | builder.finish() | ||
36 | }; | ||
37 | |||
38 | // Hack: copied this check form conv.rs beacause auto import can produce edits | ||
39 | // that invalidate assert in conv_with. | ||
40 | if edit | ||
41 | .as_atoms() | ||
42 | .iter() | ||
43 | .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) | ||
44 | .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) | ||
45 | { | ||
46 | CompletionItem::new( | ||
47 | CompletionKind::Reference, | ||
48 | ctx.source_range(), | ||
49 | build_import_label(&name, &path), | ||
50 | ) | ||
51 | .text_edit(edit) | ||
52 | .add_to(acc); | ||
53 | } | ||
54 | }); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | fn build_import_label(name: &str, path: &ModPath) -> String { | ||
59 | let mut buf = String::with_capacity(64); | ||
60 | buf.push_str(name); | ||
61 | buf.push_str(" ("); | ||
62 | buf.push_str(&path.to_string()); | ||
63 | buf.push_str(")"); | ||
64 | buf | ||
65 | } | ||
66 | |||
67 | #[derive(Debug, Clone, Default)] | ||
68 | pub(crate) struct ImportResolver { | ||
69 | // todo: use fst crate or something like that | ||
70 | dummy_names: Vec<(SmolStr, ModPath)>, | ||
71 | } | ||
72 | |||
73 | impl ImportResolver { | ||
74 | pub(crate) fn new() -> Self { | ||
75 | use hir::name; | ||
76 | |||
77 | let dummy_names = vec![ | ||
78 | ( | ||
79 | SmolStr::new("fmt"), | ||
80 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] }, | ||
81 | ), | ||
82 | ( | ||
83 | SmolStr::new("io"), | ||
84 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] }, | ||
85 | ), | ||
86 | ( | ||
87 | SmolStr::new("iter"), | ||
88 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] }, | ||
89 | ), | ||
90 | ( | ||
91 | SmolStr::new("hash"), | ||
92 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] }, | ||
93 | ), | ||
94 | ( | ||
95 | SmolStr::new("Debug"), | ||
96 | ModPath { | ||
97 | kind: PathKind::Plain, | ||
98 | segments: vec![name![std], name![fmt], name![Debug]], | ||
99 | }, | ||
100 | ), | ||
101 | ( | ||
102 | SmolStr::new("Display"), | ||
103 | ModPath { | ||
104 | kind: PathKind::Plain, | ||
105 | segments: vec![name![std], name![fmt], name![Display]], | ||
106 | }, | ||
107 | ), | ||
108 | ( | ||
109 | SmolStr::new("Hash"), | ||
110 | ModPath { | ||
111 | kind: PathKind::Plain, | ||
112 | segments: vec![name![std], name![hash], name![Hash]], | ||
113 | }, | ||
114 | ), | ||
115 | ( | ||
116 | SmolStr::new("Hasher"), | ||
117 | ModPath { | ||
118 | kind: PathKind::Plain, | ||
119 | segments: vec![name![std], name![hash], name![Hasher]], | ||
120 | }, | ||
121 | ), | ||
122 | ( | ||
123 | SmolStr::new("Iterator"), | ||
124 | ModPath { | ||
125 | kind: PathKind::Plain, | ||
126 | segments: vec![name![std], name![iter], name![Iterator]], | ||
127 | }, | ||
128 | ), | ||
129 | ]; | ||
130 | |||
131 | ImportResolver { dummy_names } | ||
132 | } | ||
133 | |||
134 | // Returns a map of importable items filtered by name. | ||
135 | // The map associates item name with its full path. | ||
136 | // todo: should return Resolutions | ||
137 | pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, ModPath> { | ||
138 | if name.len() > 1 { | ||
139 | self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() | ||
140 | } else { | ||
141 | FxHashMap::default() | ||
142 | } | ||
143 | } | ||
144 | } | 13 | } |
145 | 14 | ||
146 | #[cfg(test)] | 15 | #[cfg(test)] |
147 | mod tests { | 16 | mod tests { |
148 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
149 | use insta::assert_debug_snapshot; | 17 | use insta::assert_debug_snapshot; |
150 | 18 | ||
19 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
20 | |||
151 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | 21 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { |
152 | do_completion(code, CompletionKind::Reference) | 22 | do_completion(code, CompletionKind::Reference) |
153 | } | 23 | } |
diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index 4b301d67a..e27b27ffa 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen` | 1 | //! Generated file, do not edit by hand, see `xtask/src/codegen` |
2 | 2 | ||
3 | #![allow(bad_style, missing_docs, unreachable_pub)] | 3 | #![allow(bad_style, missing_docs, unreachable_pub)] |
4 | #[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT_DEF`."] | 4 | #[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT_DEF`."] |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 33d5578e7..435135f92 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Generated file, do not edit by hand, see `crate/ra_tools/src/codegen` | 1 | //! Generated file, do not edit by hand, see `xtask/src/codegen` |
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | ast::{self, AstChildren, AstNode}, | 4 | ast::{self, AstChildren, AstNode}, |
diff --git a/docs/dev/README.md b/docs/dev/README.md index d30727786..732e4bdd3 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -43,7 +43,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 | |||
43 | 43 | ||
44 | We use GitHub Actions for CI. Most of the things, including formatting, are checked by | 44 | We use GitHub Actions for CI. Most of the things, including formatting, are checked by |
45 | `cargo test` so, if `cargo test` passes locally, that's a good sign that CI will | 45 | `cargo test` so, if `cargo test` passes locally, that's a good sign that CI will |
46 | be green as well. The only exception is that long-running by default a skipped locally. | 46 | be green as well. The only exception is that some long-running tests are skipped locally by default. |
47 | Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite. | 47 | Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite. |
48 | 48 | ||
49 | We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule. | 49 | We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule. |
@@ -54,9 +54,9 @@ You can run `cargo xtask install-pre-commit-hook` to install git-hook to run rus | |||
54 | 54 | ||
55 | All Rust code lives in the `crates` top-level directory, and is organized as a | 55 | All Rust code lives in the `crates` top-level directory, and is organized as a |
56 | single Cargo workspace. The `editors` top-level directory contains code for | 56 | single Cargo workspace. The `editors` top-level directory contains code for |
57 | integrating with editors. Currently, it contains plugins for VS Code (in | 57 | integrating with editors. Currently, it contains the plugin for VS Code (in |
58 | typescript) and Emacs (in elisp). The `docs` top-level directory contains both | 58 | typescript). The `docs` top-level directory contains both developer and user |
59 | developer and user documentation. | 59 | documentation. |
60 | 60 | ||
61 | We have some automation infra in Rust in the `xtask` package. It contains | 61 | We have some automation infra in Rust in the `xtask` package. It contains |
62 | stuff like formatting checking, code generation and powers `cargo xtask install`. | 62 | stuff like formatting checking, code generation and powers `cargo xtask install`. |
@@ -107,8 +107,8 @@ If I need to fix something simultaneously in the server and in the client, I | |||
107 | feel even more sad. I don't have a specific workflow for this case. | 107 | feel even more sad. I don't have a specific workflow for this case. |
108 | 108 | ||
109 | Additionally, I use `cargo run --release -p ra_cli -- analysis-stats | 109 | Additionally, I use `cargo run --release -p ra_cli -- analysis-stats |
110 | path/to/some/rust/crate` to run a batch analysis. This is primaraly useful for | 110 | path/to/some/rust/crate` to run a batch analysis. This is primarily useful for |
111 | performance optimiations, or for bug minimization. | 111 | performance optimizations, or for bug minimization. |
112 | 112 | ||
113 | # Logging | 113 | # Logging |
114 | 114 | ||
diff --git a/docs/user/README.md b/docs/user/README.md index 18867cd11..da99a063c 100644 --- a/docs/user/README.md +++ b/docs/user/README.md | |||
@@ -5,8 +5,7 @@ install lsp server, clone the repository and then run `cargo xtask install | |||
5 | ./crates/ra_lsp_server`). This will produce a binary named `ra_lsp_server` which | 5 | ./crates/ra_lsp_server`). This will produce a binary named `ra_lsp_server` which |
6 | you should be able to use it with any LSP-compatible editor. We use custom | 6 | you should be able to use it with any LSP-compatible editor. We use custom |
7 | extensions to LSP, so special client-side support is required to take full | 7 | extensions to LSP, so special client-side support is required to take full |
8 | advantage of rust-analyzer. This repository contains support code for VS Code | 8 | advantage of rust-analyzer. This repository contains support code for VS Code. |
9 | and Emacs. | ||
10 | 9 | ||
11 | ``` | 10 | ``` |
12 | $ git clone [email protected]:rust-analyzer/rust-analyzer && cd rust-analyzer | 11 | $ git clone [email protected]:rust-analyzer/rust-analyzer && cd rust-analyzer |
@@ -130,17 +129,12 @@ host. | |||
130 | 129 | ||
131 | ## Emacs | 130 | ## Emacs |
132 | 131 | ||
133 | Prerequisites: | 132 | * install recent version of `emacs-lsp` package by following the instructions [here][emacs-lsp] |
134 | 133 | * set `lsp-rust-server` to `'rust-analyzer` | |
135 | `emacs-lsp`, `dash` and `ht` packages. | 134 | * run `lsp` in a Rust buffer |
136 | 135 | * (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys | |
137 | Installation: | ||
138 | 136 | ||
139 | * add | 137 | [emacs-lsp]: https://github.com/emacs-lsp/lsp-mode |
140 | [rust-analyzer.el](../../editors/emacs/rust-analyzer.el) | ||
141 | to load path and require it in `init.el` | ||
142 | * run `lsp` in a rust buffer | ||
143 | * (Optionally) bind commands like `rust-analyzer-join-lines`, `rust-analyzer-extend-selection` and `rust-analyzer-expand-macro` to keys, and enable `rust-analyzer-inlay-hints-mode` to get inline type hints | ||
144 | 138 | ||
145 | 139 | ||
146 | ## Vim and NeoVim (coc-rust-analyzer) | 140 | ## Vim and NeoVim (coc-rust-analyzer) |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 1e2dd7485..f737a2fa4 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -154,20 +154,6 @@ impl Trait<u32> for () { | |||
154 | } | 154 | } |
155 | ``` | 155 | ``` |
156 | 156 | ||
157 | ## `add_import` | ||
158 | |||
159 | Adds a use statement for a given fully-qualified path. | ||
160 | |||
161 | ```rust | ||
162 | // BEFORE | ||
163 | fn process(map: std::collections::┃HashMap<String, String>) {} | ||
164 | |||
165 | // AFTER | ||
166 | use std::collections::HashMap; | ||
167 | |||
168 | fn process(map: HashMap<String, String>) {} | ||
169 | ``` | ||
170 | |||
171 | ## `add_new` | 157 | ## `add_new` |
172 | 158 | ||
173 | Adds a new inherent impl for a type. | 159 | Adds a new inherent impl for a type. |
@@ -568,6 +554,20 @@ fn handle(action: Action) { | |||
568 | } | 554 | } |
569 | ``` | 555 | ``` |
570 | 556 | ||
557 | ## `replace_qualified_name_with_use` | ||
558 | |||
559 | Adds a use statement for a given fully-qualified name. | ||
560 | |||
561 | ```rust | ||
562 | // BEFORE | ||
563 | fn process(map: std::collections::┃HashMap<String, String>) {} | ||
564 | |||
565 | // AFTER | ||
566 | use std::collections::HashMap; | ||
567 | |||
568 | fn process(map: HashMap<String, String>) {} | ||
569 | ``` | ||
570 | |||
571 | ## `split_import` | 571 | ## `split_import` |
572 | 572 | ||
573 | Wraps the tail of import into braces. | 573 | Wraps the tail of import into braces. |
diff --git a/editors/emacs/rust-analyzer.el b/editors/emacs/rust-analyzer.el deleted file mode 100644 index 9b426fcae..000000000 --- a/editors/emacs/rust-analyzer.el +++ /dev/null | |||
@@ -1,295 +0,0 @@ | |||
1 | ;;; rust-analyzer.el --- Rust analyzer emacs bindings for emacs-lsp -*- lexical-binding: t; -*- | ||
2 | ;;; Code: | ||
3 | |||
4 | (require 'lsp) | ||
5 | (require 'dash) | ||
6 | (require 'ht) | ||
7 | |||
8 | ;; This currently | ||
9 | ;; - sets up rust-analyzer with emacs-lsp, giving | ||
10 | ;; - code actions | ||
11 | ;; - completion (use company-lsp for proper snippet support) | ||
12 | ;; - imenu support | ||
13 | ;; - on-type formatting | ||
14 | ;; - 'hover' type information & documentation (with lsp-ui) | ||
15 | ;; - implements source changes (for code actions etc.), except for file system changes | ||
16 | ;; - implements joinLines (you need to bind rust-analyzer-join-lines to a key) | ||
17 | ;; - implements selectionRanges (either bind lsp-extend-selection to a key, or use expand-region) | ||
18 | ;; - provides rust-analyzer-inlay-hints-mode for inline type hints | ||
19 | ;; - provides rust-analyzer-expand-macro to expand macros | ||
20 | |||
21 | ;; What's missing: | ||
22 | ;; - file system changes in apply-source-change | ||
23 | ;; - semantic highlighting | ||
24 | ;; - onEnter, parentModule, findMatchingBrace | ||
25 | ;; - runnables | ||
26 | ;; - the debugging commands (syntaxTree and analyzerStatus) | ||
27 | ;; - more | ||
28 | |||
29 | ;; Also, there's a problem with company-lsp's caching being too eager, sometimes | ||
30 | ;; resulting in outdated completions. | ||
31 | |||
32 | (defcustom rust-analyzer-command '("ra_lsp_server") | ||
33 | "" | ||
34 | :type '(repeat (string))) | ||
35 | |||
36 | (defconst rust-analyzer--notification-handlers | ||
37 | '(("rust-analyzer/publishDecorations" . (lambda (_w _p))))) | ||
38 | |||
39 | (defconst rust-analyzer--action-handlers | ||
40 | '(("rust-analyzer.applySourceChange" . | ||
41 | (lambda (p) (rust-analyzer--apply-source-change-command p))) | ||
42 | ("rust-analyzer.selectAndApplySourceChange" . | ||
43 | (lambda (p) (rust-analyzer--select-and-apply-source-change-command p))))) | ||
44 | |||
45 | (defun rust-analyzer--uri-filename (text-document) | ||
46 | (lsp--uri-to-path (gethash "uri" text-document))) | ||
47 | |||
48 | (defun rust-analyzer--goto-lsp-loc (loc) | ||
49 | (-let (((&hash "line" "character") loc)) | ||
50 | (goto-line (1+ line)) | ||
51 | (move-to-column character))) | ||
52 | |||
53 | (defun rust-analyzer--apply-text-document-edit (edit) | ||
54 | "Like lsp--apply-text-document-edit, but it allows nil version." | ||
55 | (let* ((ident (gethash "textDocument" edit)) | ||
56 | (filename (rust-analyzer--uri-filename ident)) | ||
57 | (version (gethash "version" ident))) | ||
58 | (with-current-buffer (find-file-noselect filename) | ||
59 | (when (or (not version) (= version (lsp--cur-file-version))) | ||
60 | (lsp--apply-text-edits (gethash "edits" edit)))))) | ||
61 | |||
62 | (defun rust-analyzer--apply-source-change (data) | ||
63 | ;; TODO fileSystemEdits | ||
64 | (seq-doseq (it (-> data (ht-get "workspaceEdit") (ht-get "documentChanges"))) | ||
65 | (rust-analyzer--apply-text-document-edit it)) | ||
66 | (-when-let (cursor-position (ht-get data "cursorPosition")) | ||
67 | (let ((filename (rust-analyzer--uri-filename (ht-get cursor-position "textDocument"))) | ||
68 | (position (ht-get cursor-position "position"))) | ||
69 | (find-file filename) | ||
70 | (rust-analyzer--goto-lsp-loc position)))) | ||
71 | |||
72 | (defun rust-analyzer--apply-source-change-command (p) | ||
73 | (let ((data (-> p (ht-get "arguments") (lsp-seq-first)))) | ||
74 | (rust-analyzer--apply-source-change data))) | ||
75 | |||
76 | (defun rust-analyzer--select-and-apply-source-change-command (p) | ||
77 | (let* ((options (-> p (ht-get "arguments") (lsp-seq-first))) | ||
78 | (chosen-option (lsp--completing-read "Select option:" options | ||
79 | (-lambda ((&hash "label")) label)))) | ||
80 | (rust-analyzer--apply-source-change chosen-option))) | ||
81 | |||
82 | (lsp-register-client | ||
83 | (make-lsp-client | ||
84 | :new-connection (lsp-stdio-connection (lambda () rust-analyzer-command)) | ||
85 | :notification-handlers (ht<-alist rust-analyzer--notification-handlers) | ||
86 | :action-handlers (ht<-alist rust-analyzer--action-handlers) | ||
87 | :major-modes '(rust-mode) | ||
88 | :ignore-messages nil | ||
89 | :server-id 'rust-analyzer)) | ||
90 | |||
91 | (defun rust-analyzer--initialized? () | ||
92 | (when-let ((workspace (lsp-find-workspace 'rust-analyzer (buffer-file-name)))) | ||
93 | (eq 'initialized (lsp--workspace-status workspace)))) | ||
94 | |||
95 | (with-eval-after-load 'company-lsp | ||
96 | ;; company-lsp provides a snippet handler for rust by default that adds () after function calls, which RA does better | ||
97 | (setq company-lsp--snippet-functions (cl-delete "rust" company-lsp--snippet-functions :key #'car :test #'equal))) | ||
98 | |||
99 | ;; join lines | ||
100 | |||
101 | (defun rust-analyzer--join-lines-params () | ||
102 | "Join lines params." | ||
103 | (list :textDocument (lsp--text-document-identifier) | ||
104 | :range (if (use-region-p) | ||
105 | (lsp--region-to-range (region-beginning) (region-end)) | ||
106 | (lsp--region-to-range (point) (point))))) | ||
107 | |||
108 | (defun rust-analyzer-join-lines () | ||
109 | (interactive) | ||
110 | (-> | ||
111 | (lsp-send-request (lsp-make-request "rust-analyzer/joinLines" | ||
112 | (rust-analyzer--join-lines-params))) | ||
113 | (rust-analyzer--apply-source-change))) | ||
114 | |||
115 | ;; selection ranges | ||
116 | |||
117 | (defun rust-analyzer--add-er-expansion () | ||
118 | (make-variable-buffer-local 'er/try-expand-list) | ||
119 | (setq er/try-expand-list (append | ||
120 | er/try-expand-list | ||
121 | '(lsp-extend-selection)))) | ||
122 | |||
123 | (with-eval-after-load 'expand-region | ||
124 | ;; add the expansion for all existing rust-mode buffers. If expand-region is | ||
125 | ;; loaded lazily, it might be loaded when the first rust buffer is opened, and | ||
126 | ;; then it's too late for the hook for that buffer | ||
127 | (dolist (buf (buffer-list)) | ||
128 | (with-current-buffer buf | ||
129 | (when (eq 'rust-mode major-mode) | ||
130 | (rust-analyzer--add-er-expansion)))) | ||
131 | (add-hook 'rust-mode-hook 'rust-analyzer--add-er-expansion)) | ||
132 | |||
133 | ;; runnables | ||
134 | (defvar rust-analyzer--last-runnable nil) | ||
135 | |||
136 | (defun rust-analyzer--runnables-params () | ||
137 | (list :textDocument (lsp--text-document-identifier) | ||
138 | :position (lsp--cur-position))) | ||
139 | |||
140 | (defun rust-analyzer--runnables () | ||
141 | (lsp-send-request (lsp-make-request "rust-analyzer/runnables" | ||
142 | (rust-analyzer--runnables-params)))) | ||
143 | |||
144 | (defun rust-analyzer--select-runnable () | ||
145 | (lsp--completing-read | ||
146 | "Select runnable:" | ||
147 | (if rust-analyzer--last-runnable | ||
148 | (cons rust-analyzer--last-runnable (rust-analyzer--runnables)) | ||
149 | (rust-analyzer--runnables)) | ||
150 | (-lambda ((&hash "label")) label))) | ||
151 | |||
152 | (defun rust-analyzer-run (runnable) | ||
153 | (interactive (list (rust-analyzer--select-runnable))) | ||
154 | (-let* (((&hash "env" "bin" "args" "label") runnable) | ||
155 | (compilation-environment (-map (-lambda ((k v)) (concat k "=" v)) (ht-items env)))) | ||
156 | (compilation-start | ||
157 | (string-join (append (list bin) args '()) " ") | ||
158 | ;; cargo-process-mode is nice, but try to work without it... | ||
159 | (if (functionp 'cargo-process-mode) 'cargo-process-mode nil) | ||
160 | (lambda (_) (concat "*" label "*"))) | ||
161 | (setq rust-analyzer--last-runnable runnable))) | ||
162 | |||
163 | (defun rust-analyzer-rerun (&optional runnable) | ||
164 | (interactive (list (or rust-analyzer--last-runnable | ||
165 | (rust-analyzer--select-runnable)))) | ||
166 | (rust-analyzer-run (or runnable rust-analyzer--last-runnable))) | ||
167 | |||
168 | ;; analyzer status buffer | ||
169 | (define-derived-mode rust-analyzer-status-mode special-mode "Rust-Analyzer-Status" | ||
170 | "Mode for the rust-analyzer status buffer.") | ||
171 | |||
172 | (defvar-local rust-analyzer--status-buffer-workspace nil) | ||
173 | |||
174 | (defun rust-analyzer-status () | ||
175 | "Displays status information for rust-analyzer." | ||
176 | (interactive) | ||
177 | (let* ((workspace (lsp-find-workspace 'rust-analyzer (buffer-file-name))) | ||
178 | (buf (get-buffer-create (concat "*rust-analyzer status " (with-lsp-workspace workspace (lsp-workspace-root)) "*")))) | ||
179 | (with-current-buffer buf | ||
180 | (rust-analyzer-status-mode) | ||
181 | (setq rust-analyzer--status-buffer-workspace workspace) | ||
182 | (rust-analyzer-status-buffer-refresh)) | ||
183 | (pop-to-buffer buf))) | ||
184 | |||
185 | (defun rust-analyzer-status-buffer-refresh () | ||
186 | (interactive) | ||
187 | (when rust-analyzer--status-buffer-workspace | ||
188 | (let ((inhibit-read-only t)) | ||
189 | (erase-buffer) | ||
190 | (insert (with-lsp-workspace rust-analyzer--status-buffer-workspace | ||
191 | (lsp-send-request (lsp-make-request | ||
192 | "rust-analyzer/analyzerStatus"))))))) | ||
193 | |||
194 | |||
195 | (defun rust-analyzer--syntax-tree-params () | ||
196 | "Syntax tree params." | ||
197 | (list :textDocument (lsp--text-document-identifier) | ||
198 | :range (if (use-region-p) | ||
199 | (lsp--region-to-range (region-beginning) (region-end)) | ||
200 | (lsp--region-to-range (point-min) (point-max))))) | ||
201 | |||
202 | (defun rust-analyzer-syntax-tree () | ||
203 | "Displays syntax tree for current buffer." | ||
204 | (interactive) | ||
205 | (when (eq 'rust-mode major-mode) | ||
206 | (let* ((workspace (lsp-find-workspace 'rust-analyzer (buffer-file-name))) | ||
207 | (buf (get-buffer-create (concat "*rust-analyzer syntax tree " (with-lsp-workspace workspace (lsp-workspace-root)) "*")))) | ||
208 | (when workspace | ||
209 | (let ((parse-result (with-lsp-workspace workspace | ||
210 | (lsp-send-request (lsp-make-request | ||
211 | "rust-analyzer/syntaxTree" | ||
212 | (rust-analyzer--syntax-tree-params)))))) | ||
213 | (with-current-buffer buf | ||
214 | (let ((inhibit-read-only t)) | ||
215 | (erase-buffer) | ||
216 | (insert parse-result))) | ||
217 | (pop-to-buffer buf)))))) | ||
218 | |||
219 | ;; inlay hints | ||
220 | (defun rust-analyzer--update-inlay-hints (buffer) | ||
221 | (if (and (rust-analyzer--initialized?) (eq buffer (current-buffer))) | ||
222 | (lsp-request-async | ||
223 | "rust-analyzer/inlayHints" | ||
224 | (list :textDocument (lsp--text-document-identifier)) | ||
225 | (lambda (res) | ||
226 | (remove-overlays (point-min) (point-max) 'rust-analyzer--inlay-hint t) | ||
227 | (dolist (hint res) | ||
228 | (-let* (((&hash "range" "label" "kind") hint) | ||
229 | ((beg . end) (lsp--range-to-region range)) | ||
230 | (overlay (make-overlay beg end))) | ||
231 | (overlay-put overlay 'rust-analyzer--inlay-hint t) | ||
232 | (overlay-put overlay 'evaporate t) | ||
233 | (cond | ||
234 | ((string= kind "TypeHint") | ||
235 | (overlay-put overlay 'after-string (propertize (concat ": " label) | ||
236 | 'font-lock-face 'font-lock-comment-face))) | ||
237 | ((string= kind "ParameterHint") | ||
238 | (overlay-put overlay 'before-string (propertize (concat label ": ") | ||
239 | 'font-lock-face 'font-lock-comment-face))) | ||
240 | ) | ||
241 | ))) | ||
242 | :mode 'tick)) | ||
243 | nil) | ||
244 | |||
245 | (defvar-local rust-analyzer--inlay-hints-timer nil) | ||
246 | |||
247 | (defun rust-analyzer--inlay-hints-change-handler (&rest rest) | ||
248 | (when rust-analyzer--inlay-hints-timer | ||
249 | (cancel-timer rust-analyzer--inlay-hints-timer)) | ||
250 | (setq rust-analyzer--inlay-hints-timer | ||
251 | (run-with-idle-timer 0.1 nil #'rust-analyzer--update-inlay-hints (current-buffer)))) | ||
252 | |||
253 | (define-minor-mode rust-analyzer-inlay-hints-mode | ||
254 | "Mode for showing inlay hints." | ||
255 | nil nil nil | ||
256 | (cond | ||
257 | (rust-analyzer-inlay-hints-mode | ||
258 | (rust-analyzer--update-inlay-hints (current-buffer)) | ||
259 | (add-hook 'lsp-after-initialize-hook #'rust-analyzer--inlay-hints-change-handler nil t) | ||
260 | (add-hook 'after-change-functions #'rust-analyzer--inlay-hints-change-handler nil t)) | ||
261 | (t | ||
262 | (remove-overlays (point-min) (point-max) 'rust-analyzer--inlay-hint t) | ||
263 | (remove-hook 'lsp-after-initialize-hook #'rust-analyzer--inlay-hints-change-handler t) | ||
264 | (remove-hook 'after-change-functions #'rust-analyzer--inlay-hints-change-handler t)))) | ||
265 | |||
266 | |||
267 | |||
268 | ;; expand macros | ||
269 | (defun rust-analyzer-expand-macro () | ||
270 | "Expands the macro call at point recursively." | ||
271 | (interactive) | ||
272 | (when (eq 'rust-mode major-mode) | ||
273 | (let* ((workspace (lsp-find-workspace 'rust-analyzer (buffer-file-name))) | ||
274 | (params (list :textDocument (lsp--text-document-identifier) | ||
275 | :position (lsp--cur-position)))) | ||
276 | (when workspace | ||
277 | (let* ((response (with-lsp-workspace workspace | ||
278 | (lsp-send-request (lsp-make-request | ||
279 | "rust-analyzer/expandMacro" | ||
280 | params)))) | ||
281 | (result (when response (ht-get response "expansion")))) | ||
282 | (if result | ||
283 | (let ((buf (get-buffer-create (concat "*rust-analyzer macro expansion " (with-lsp-workspace workspace (lsp-workspace-root)) "*")))) | ||
284 | (with-current-buffer buf | ||
285 | (let ((inhibit-read-only t)) | ||
286 | (erase-buffer) | ||
287 | (insert result) | ||
288 | (setq buffer-read-only t) | ||
289 | (special-mode))) | ||
290 | (pop-to-buffer buf)) | ||
291 | (message "No macro found at point, or it could not be expanded"))))))) | ||
292 | |||
293 | |||
294 | (provide 'rust-analyzer) | ||
295 | ;;; rust-analyzer.el ends here | ||
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 9b0afe8e0..8fdf43e4a 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -53,8 +53,7 @@ fn reformat(text: impl std::fmt::Display) -> Result<String> { | |||
53 | write!(rustfmt.stdin.take().unwrap(), "{}", text)?; | 53 | write!(rustfmt.stdin.take().unwrap(), "{}", text)?; |
54 | let output = rustfmt.wait_with_output()?; | 54 | let output = rustfmt.wait_with_output()?; |
55 | let stdout = String::from_utf8(output.stdout)?; | 55 | let stdout = String::from_utf8(output.stdout)?; |
56 | // TODO: update the preable: replace ra_tools with the relevant path | 56 | let preamble = "Generated file, do not edit by hand, see `xtask/src/codegen`"; |
57 | let preamble = "Generated file, do not edit by hand, see `crate/ra_tools/src/codegen`"; | ||
58 | Ok(format!("//! {}\n\n{}", preamble, stdout)) | 57 | Ok(format!("//! {}\n\n{}", preamble, stdout)) |
59 | } | 58 | } |
60 | 59 | ||