aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs32
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs4
-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.rs6
-rw-r--r--crates/ra_hir/src/lib.rs3
-rw-r--r--crates/ra_hir_expand/src/name.rs7
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs136
-rw-r--r--crates/ra_parser/src/syntax_kind/generated.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs2
-rw-r--r--docs/dev/README.md12
-rw-r--r--docs/user/README.md18
-rw-r--r--docs/user/assists.md28
-rw-r--r--editors/emacs/rust-analyzer.el295
-rw-r--r--xtask/src/lib.rs3
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
3use super::check; 3use super::check;
4 4
@@ -161,21 +161,6 @@ impl Trait<u32> for () {
161} 161}
162 162
163#[test] 163#[test]
164fn doctest_add_import() {
165 check(
166 "add_import",
167 r#####"
168fn process(map: std::collections::<|>HashMap<String, String>) {}
169"#####,
170 r#####"
171use std::collections::HashMap;
172
173fn process(map: HashMap<String, String>) {}
174"#####,
175 )
176}
177
178#[test]
179fn doctest_add_new() { 164fn 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]
580fn doctest_replace_qualified_name_with_use() {
581 check(
582 "replace_qualified_name_with_use",
583 r#####"
584fn process(map: std::collections::<|>HashMap<String, String>) {}
585"#####,
586 r#####"
587use std::collections::HashMap;
588
589fn process(map: HashMap<String, String>) {}
590"#####,
591 )
592}
593
594#[test]
595fn doctest_split_import() { 595fn 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
8use crate::{ 8use 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};
12use std::collections::BTreeSet; 12use std::collections::BTreeSet;
13 13
@@ -78,7 +78,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
78fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { 78fn 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.
18pub fn auto_import_text_edit( 18pub 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// ```
53pub(crate) fn add_import(ctx: AssistCtx) -> Option<Assist> { 53pub(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
80fn collect_path_segments_raw( 84fn 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
110fn fmt_segments(segments: &[SmolStr]) -> String {
111 let mut buf = String::new();
112 fmt_segments_raw(segments, &mut buf);
113 buf
114}
115
116fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) { 114fn 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
561fn apply_auto_import( 559fn 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 "
610std::fmt::Debug<|> 608std::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 "
624std::fmt::Debug<|> 622std::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 "
645fn main() { 643fn 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 "
666std::fmt<|>::Debug 664std::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 "
681use stdx; 679use 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 "
701impl std::fmt::Debug<|> for Foo { 699impl 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 "
735use std::fmt; 733use 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 "
754use std::fmt; 752use 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 "
773use std::fmt::Debug; 771use 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 "
792use std::fmt::{Debug, nested::{Display}}; 790use 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 "
811use std::fmt::{Debug, nested::{self, Display}}; 809use 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 "
830use std::fmt::{Debug, nested::{Display}}; 828use 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 "
849use std::fmt::Debug; 847use 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 "
868use std::fmt::nested::Debug; 866use 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 "
887use crate::{ 885use 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 "
910use std::fmt as foo; 908use 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 "
929impl foo<|> for Foo { 927impl 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 "
940use std::fmt<|>; 938use 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 "
950mod foo { 948mod 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};
21use ra_text_edit::TextEdit; 21use ra_text_edit::TextEdit;
22 22
23pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; 23pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
24pub use crate::handlers::add_import::auto_import_text_edit; 24pub 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};
60pub use hir_expand::{ 60pub 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};
64pub use hir_ty::{display::HirDisplay, CallableDef}; 63pub 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
3use ra_assists::auto_import_text_edit; 3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::{ast, AstNode, SmolStr};
5use ra_text_edit::TextEditBuilder;
6use rustc_hash::FxHashMap;
7
8use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9use hir::{ModPath, PathKind};
10 4
11pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 5pub(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
58fn 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)]
68pub(crate) struct ImportResolver {
69 // todo: use fst crate or something like that
70 dummy_names: Vec<(SmolStr, ModPath)>,
71}
72
73impl 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)]
147mod tests { 16mod 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
3use crate::{ 3use 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
44We use GitHub Actions for CI. Most of the things, including formatting, are checked by 44We 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
46be green as well. The only exception is that long-running by default a skipped locally. 46be green as well. The only exception is that some long-running tests are skipped locally by default.
47Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite. 47Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.
48 48
49We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule. 49We 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
55All Rust code lives in the `crates` top-level directory, and is organized as a 55All Rust code lives in the `crates` top-level directory, and is organized as a
56single Cargo workspace. The `editors` top-level directory contains code for 56single Cargo workspace. The `editors` top-level directory contains code for
57integrating with editors. Currently, it contains plugins for VS Code (in 57integrating with editors. Currently, it contains the plugin for VS Code (in
58typescript) and Emacs (in elisp). The `docs` top-level directory contains both 58typescript). The `docs` top-level directory contains both developer and user
59developer and user documentation. 59documentation.
60 60
61We have some automation infra in Rust in the `xtask` package. It contains 61We have some automation infra in Rust in the `xtask` package. It contains
62stuff like formatting checking, code generation and powers `cargo xtask install`. 62stuff 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
107feel even more sad. I don't have a specific workflow for this case. 107feel even more sad. I don't have a specific workflow for this case.
108 108
109Additionally, I use `cargo run --release -p ra_cli -- analysis-stats 109Additionally, I use `cargo run --release -p ra_cli -- analysis-stats
110path/to/some/rust/crate` to run a batch analysis. This is primaraly useful for 110path/to/some/rust/crate` to run a batch analysis. This is primarily useful for
111performance optimiations, or for bug minimization. 111performance 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
6you should be able to use it with any LSP-compatible editor. We use custom 6you should be able to use it with any LSP-compatible editor. We use custom
7extensions to LSP, so special client-side support is required to take full 7extensions to LSP, so special client-side support is required to take full
8advantage of rust-analyzer. This repository contains support code for VS Code 8advantage of rust-analyzer. This repository contains support code for VS Code.
9and 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
133Prerequisites: 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
137Installation:
138 136
139* add 137[emacs-lsp]: https://github.com/emacs-lsp/lsp-mode
140[rust-analyzer.el](../../editors/emacs/rust-analyzer.el)
141to 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
159Adds a use statement for a given fully-qualified path.
160
161```rust
162// BEFORE
163fn process(map: std::collections::┃HashMap<String, String>) {}
164
165// AFTER
166use std::collections::HashMap;
167
168fn process(map: HashMap<String, String>) {}
169```
170
171## `add_new` 157## `add_new`
172 158
173Adds a new inherent impl for a type. 159Adds 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
559Adds a use statement for a given fully-qualified name.
560
561```rust
562// BEFORE
563fn process(map: std::collections::┃HashMap<String, String>) {}
564
565// AFTER
566use std::collections::HashMap;
567
568fn process(map: HashMap<String, String>) {}
569```
570
571## `split_import` 571## `split_import`
572 572
573Wraps the tail of import into braces. 573Wraps 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