aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/call_info.rs14
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs32
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs70
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs36
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs3
-rw-r--r--crates/ra_ide/src/completion/presentation.rs22
-rw-r--r--crates/ra_ide/src/display.rs37
-rw-r--r--crates/ra_ide/src/display/function_signature.rs4
-rw-r--r--crates/ra_ide/src/goto_definition.rs15
-rw-r--r--crates/ra_ide/src/hover.rs62
-rw-r--r--crates/ra_ide/src/lib.rs3
-rw-r--r--crates/ra_ide/src/ssr.rs36
-rw-r--r--crates/ra_ide/src/syntax_tree.rs16
-rw-r--r--crates/ra_ide/src/typing.rs40
14 files changed, 282 insertions, 108 deletions
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 2b35a3803..39d09a07f 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -544,6 +544,20 @@ fn main() {
544 } 544 }
545 545
546 #[test] 546 #[test]
547 fn generic_struct() {
548 let info = call_info(
549 r#"
550struct TS<T>(T);
551fn main() {
552 let s = TS(<|>);
553}"#,
554 );
555
556 assert_eq!(info.label(), "struct TS<T>(T) -> TS");
557 assert_eq!(info.active_parameter, Some(0));
558 }
559
560 #[test]
547 #[should_panic] 561 #[should_panic]
548 fn cant_call_named_structs() { 562 fn cant_call_named_structs() {
549 let _ = call_info( 563 let _ = call_info(
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 81e5037aa..f07611d88 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -402,6 +402,38 @@ mod tests {
402 } 402 }
403 403
404 #[test] 404 #[test]
405 fn completes_trait_method_from_other_module() {
406 assert_debug_snapshot!(
407 do_ref_completion(
408 r"
409 struct A {}
410 mod m {
411 pub trait Trait { fn the_method(&self); }
412 }
413 use m::Trait;
414 impl Trait for A {}
415 fn foo(a: A) {
416 a.<|>
417 }
418 ",
419 ),
420 @r###"
421 [
422 CompletionItem {
423 label: "the_method()",
424 source_range: [219; 219),
425 delete: [219; 219),
426 insert: "the_method()$0",
427 kind: Method,
428 lookup: "the_method",
429 detail: "fn the_method(&self)",
430 },
431 ]
432 "###
433 );
434 }
435
436 #[test]
405 fn test_no_non_self_method() { 437 fn test_no_non_self_method() {
406 assert_debug_snapshot!( 438 assert_debug_snapshot!(
407 do_ref_completion( 439 do_ref_completion(
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index d588ee364..3db17f15f 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -967,4 +967,74 @@ mod tests {
967 ] 967 ]
968 "###); 968 "###);
969 } 969 }
970
971 #[test]
972 fn function_mod_share_name() {
973 assert_debug_snapshot!(
974 do_reference_completion(
975 r"
976 fn foo() {
977 self::m::<|>
978 }
979
980 mod m {
981 pub mod z {}
982 pub fn z() {}
983 }
984 ",
985 ),
986 @r###"
987 [
988 CompletionItem {
989 label: "z",
990 source_range: [57; 57),
991 delete: [57; 57),
992 insert: "z",
993 kind: Module,
994 },
995 CompletionItem {
996 label: "z()",
997 source_range: [57; 57),
998 delete: [57; 57),
999 insert: "z()$0",
1000 kind: Function,
1001 lookup: "z",
1002 detail: "pub fn z()",
1003 },
1004 ]
1005 "###
1006 );
1007 }
1008
1009 #[test]
1010 fn completes_hashmap_new() {
1011 assert_debug_snapshot!(
1012 do_reference_completion(
1013 r"
1014 struct RandomState;
1015 struct HashMap<K, V, S = RandomState> {}
1016
1017 impl<K, V> HashMap<K, V, RandomState> {
1018 pub fn new() -> HashMap<K, V, RandomState> { }
1019 }
1020 fn foo() {
1021 HashMap::<|>
1022 }
1023 "
1024 ),
1025 @r###"
1026 [
1027 CompletionItem {
1028 label: "new()",
1029 source_range: [292; 292),
1030 delete: [292; 292),
1031 insert: "new()$0",
1032 kind: Function,
1033 lookup: "new",
1034 detail: "pub fn new() -> HashMap<K, V, RandomState>",
1035 },
1036 ]
1037 "###
1038 );
1039 }
970} 1040}
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index bd4adf23a..5ffff5a1c 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -42,6 +42,7 @@ mod tests {
42 kind: Function, 42 kind: Function,
43 lookup: "quux", 43 lookup: "quux",
44 detail: "fn quux(x: i32)", 44 detail: "fn quux(x: i32)",
45 trigger_call_info: true,
45 }, 46 },
46 CompletionItem { 47 CompletionItem {
47 label: "x", 48 label: "x",
@@ -844,6 +845,7 @@ mod tests {
844 kind: Function, 845 kind: Function,
845 lookup: "quux", 846 lookup: "quux",
846 detail: "fn quux(x: i32)", 847 detail: "fn quux(x: i32)",
848 trigger_call_info: true,
847 }, 849 },
848 CompletionItem { 850 CompletionItem {
849 label: "x", 851 label: "x",
@@ -865,4 +867,38 @@ mod tests {
865 "### 867 "###
866 ); 868 );
867 } 869 }
870
871 #[test]
872 fn completes_unresolved_uses() {
873 assert_debug_snapshot!(
874 do_reference_completion(
875 r"
876 use spam::Quux;
877
878 fn main() {
879 <|>
880 }
881 "
882 ),
883 @r###"
884 [
885 CompletionItem {
886 label: "Quux",
887 source_range: [82; 82),
888 delete: [82; 82),
889 insert: "Quux",
890 },
891 CompletionItem {
892 label: "main()",
893 source_range: [82; 82),
894 delete: [82; 82),
895 insert: "main()$0",
896 kind: Function,
897 lookup: "main",
898 detail: "fn main()",
899 },
900 ]
901 "###
902 );
903 }
868} 904}
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index ef0eb43b2..bc0f1aff5 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -80,6 +80,9 @@ impl fmt::Debug for CompletionItem {
80 if self.deprecated { 80 if self.deprecated {
81 s.field("deprecated", &true); 81 s.field("deprecated", &true);
82 } 82 }
83 if self.trigger_call_info {
84 s.field("trigger_call_info", &true);
85 }
83 s.finish() 86 s.finish()
84 } 87 }
85} 88}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 5213def20..253848602 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,6 +1,6 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
4use join_to_string::join; 4use join_to_string::join;
5use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by; 6use test_utils::tested_by;
@@ -9,7 +9,10 @@ use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12use crate::display::{const_label, macro_label, type_label, FunctionSignature}; 12use crate::{
13 display::{const_label, macro_label, type_label, FunctionSignature},
14 RootDatabase,
15};
13 16
14impl Completions { 17impl Completions {
15 pub(crate) fn add_field( 18 pub(crate) fn add_field(
@@ -273,8 +276,10 @@ impl Completions {
273 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { 276 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
274 let is_deprecated = is_deprecated(variant, ctx.db); 277 let is_deprecated = is_deprecated(variant, ctx.db);
275 let name = variant.name(ctx.db); 278 let name = variant.name(ctx.db);
276 let detail_types = 279 let detail_types = variant
277 variant.fields(ctx.db).into_iter().map(|field| (field.name(ctx.db), field.ty(ctx.db))); 280 .fields(ctx.db)
281 .into_iter()
282 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
278 let detail = match variant.kind(ctx.db) { 283 let detail = match variant.kind(ctx.db) {
279 StructKind::Tuple | StructKind::Unit => { 284 StructKind::Tuple | StructKind::Unit => {
280 join(detail_types.map(|(_, t)| t.display(ctx.db).to_string())) 285 join(detail_types.map(|(_, t)| t.display(ctx.db).to_string()))
@@ -298,7 +303,7 @@ impl Completions {
298 } 303 }
299} 304}
300 305
301fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool { 306fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
302 node.attrs(db).by_key("deprecated").exists() 307 node.attrs(db).by_key("deprecated").exists()
303} 308}
304 309
@@ -510,6 +515,7 @@ mod tests {
510 kind: Function, 515 kind: Function,
511 lookup: "with_args", 516 lookup: "with_args",
512 detail: "fn with_args(x: i32, y: String)", 517 detail: "fn with_args(x: i32, y: String)",
518 trigger_call_info: true,
513 }, 519 },
514 ] 520 ]
515 "### 521 "###
@@ -566,6 +572,7 @@ mod tests {
566 kind: Method, 572 kind: Method,
567 lookup: "foo", 573 lookup: "foo",
568 detail: "fn foo(&self, x: i32)", 574 detail: "fn foo(&self, x: i32)",
575 trigger_call_info: true,
569 }, 576 },
570 ] 577 ]
571 "### 578 "###
@@ -600,6 +607,7 @@ mod tests {
600 kind: Method, 607 kind: Method,
601 lookup: "foo", 608 lookup: "foo",
602 detail: "fn foo(&self, x: i32)", 609 detail: "fn foo(&self, x: i32)",
610 trigger_call_info: true,
603 }, 611 },
604 ] 612 ]
605 "### 613 "###
@@ -718,6 +726,7 @@ mod tests {
718 kind: Function, 726 kind: Function,
719 lookup: "foo", 727 lookup: "foo",
720 detail: "fn foo(xs: Ve)", 728 detail: "fn foo(xs: Ve)",
729 trigger_call_info: true,
721 }, 730 },
722 ] 731 ]
723 "### 732 "###
@@ -747,6 +756,7 @@ mod tests {
747 kind: Function, 756 kind: Function,
748 lookup: "foo", 757 lookup: "foo",
749 detail: "fn foo(xs: Ve)", 758 detail: "fn foo(xs: Ve)",
759 trigger_call_info: true,
750 }, 760 },
751 ] 761 ]
752 "### 762 "###
@@ -775,6 +785,7 @@ mod tests {
775 kind: Function, 785 kind: Function,
776 lookup: "foo", 786 lookup: "foo",
777 detail: "fn foo(xs: Ve)", 787 detail: "fn foo(xs: Ve)",
788 trigger_call_info: true,
778 }, 789 },
779 ] 790 ]
780 "### 791 "###
@@ -803,6 +814,7 @@ mod tests {
803 kind: Function, 814 kind: Function,
804 lookup: "foo", 815 lookup: "foo",
805 detail: "fn foo(xs: Ve<i128>)", 816 detail: "fn foo(xs: Ve<i128>)",
817 trigger_call_info: true,
806 }, 818 },
807 ] 819 ]
808 "### 820 "###
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index eaeaaa2b4..c395057a7 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,6 +6,8 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::{Display, Write};
10
9use ra_syntax::{ 11use ra_syntax::{
10 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
11 SyntaxKind::{ATTR, COMMENT}, 13 SyntaxKind::{ATTR, COMMENT},
@@ -67,24 +69,27 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
67 format!("{}macro_rules! {}", vis, name) 69 format!("{}macro_rules! {}", vis, name)
68} 70}
69 71
70pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { 72pub(crate) fn rust_code_markup(code: &impl Display) -> String {
71 rust_code_markup_with_doc::<_, &str>(val, None, None) 73 rust_code_markup_with_doc(code, None, None)
72} 74}
73 75
74pub(crate) fn rust_code_markup_with_doc<CODE, DOC>( 76pub(crate) fn rust_code_markup_with_doc(
75 val: CODE, 77 code: &impl Display,
76 doc: Option<DOC>, 78 doc: Option<&str>,
77 mod_path: Option<String>, 79 mod_path: Option<&str>,
78) -> String 80) -> String {
79where 81 let mut markup = "```rust\n".to_owned();
80 CODE: AsRef<str>, 82
81 DOC: AsRef<str>, 83 if let Some(mod_path) = mod_path {
82{ 84 if !mod_path.is_empty() {
83 let mod_path = 85 write!(markup, "{}\n", mod_path).unwrap();
84 mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default(); 86 }
87 }
88 write!(markup, "{}\n```", code).unwrap();
89
85 if let Some(doc) = doc { 90 if let Some(doc) = doc {
86 format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref()) 91 write!(markup, "\n\n{}", doc).unwrap();
87 } else {
88 format!("```rust\n{}{}\n```", mod_path, val.as_ref())
89 } 92 }
93
94 markup
90} 95}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 2c4c932de..ec1bbd5a0 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -64,7 +64,7 @@ impl FunctionSignature {
64 .fields(db) 64 .fields(db)
65 .into_iter() 65 .into_iter()
66 .map(|field: hir::StructField| { 66 .map(|field: hir::StructField| {
67 let ty = field.ty(db); 67 let ty = field.signature_ty(db);
68 format!("{}", ty.display(db)) 68 format!("{}", ty.display(db))
69 }) 69 })
70 .collect(); 70 .collect();
@@ -102,7 +102,7 @@ impl FunctionSignature {
102 .into_iter() 102 .into_iter()
103 .map(|field: hir::StructField| { 103 .map(|field: hir::StructField| {
104 let name = field.name(db); 104 let name = field.name(db);
105 let ty = field.ty(db); 105 let ty = field.signature_ty(db);
106 format!("{}: {}", name, ty.display(db)) 106 format!("{}: {}", name, ty.display(db))
107 }) 107 })
108 .collect(); 108 .collect();
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a55a13ffc..a7be92ce3 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -788,6 +788,21 @@ mod tests {
788 } 788 }
789 789
790 #[test] 790 #[test]
791 fn goto_def_in_local_macro() {
792 check_goto(
793 "
794 //- /lib.rs
795 fn bar() {
796 macro_rules! foo { () => { () } }
797 <|>foo!();
798 }
799 ",
800 "foo MACRO_CALL FileId(1) [15; 48) [28; 31)",
801 "macro_rules! foo { () => { () } }|foo",
802 );
803 }
804
805 #[test]
791 fn goto_def_for_field_init_shorthand() { 806 fn goto_def_for_field_init_shorthand() {
792 covers!(ra_ide_db::goto_def_for_field_init_shorthand); 807 covers!(ra_ide_db::goto_def_for_field_init_shorthand);
793 check_goto( 808 check_goto(
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 0bbba4855..3bdd61a2e 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,4 +1,5 @@
1//! FIXME: write short doc here 1//! Logic for computing info that is displayed when the user hovers over any
2//! source code items (e.g. function call, struct field, variable symbol...)
2 3
3use hir::{ 4use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 5 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
@@ -24,35 +25,20 @@ use itertools::Itertools;
24use std::iter::once; 25use std::iter::once;
25 26
26/// Contains the results when hovering over an item 27/// Contains the results when hovering over an item
27#[derive(Debug, Clone)] 28#[derive(Debug, Default)]
28pub struct HoverResult { 29pub struct HoverResult {
29 results: Vec<String>, 30 results: Vec<String>,
30 exact: bool,
31}
32
33impl Default for HoverResult {
34 fn default() -> Self {
35 HoverResult::new()
36 }
37} 31}
38 32
39impl HoverResult { 33impl HoverResult {
40 pub fn new() -> HoverResult { 34 pub fn new() -> HoverResult {
41 HoverResult { 35 Self::default()
42 results: Vec::new(),
43 // We assume exact by default
44 exact: true,
45 }
46 } 36 }
47 37
48 pub fn extend(&mut self, item: Option<String>) { 38 pub fn extend(&mut self, item: Option<String>) {
49 self.results.extend(item); 39 self.results.extend(item);
50 } 40 }
51 41
52 pub fn is_exact(&self) -> bool {
53 self.exact
54 }
55
56 pub fn is_empty(&self) -> bool { 42 pub fn is_empty(&self) -> bool {
57 self.results.is_empty() 43 self.results.is_empty()
58 } 44 }
@@ -72,20 +58,7 @@ impl HoverResult {
72 /// Returns the results converted into markup 58 /// Returns the results converted into markup
73 /// for displaying in a UI 59 /// for displaying in a UI
74 pub fn to_markup(&self) -> String { 60 pub fn to_markup(&self) -> String {
75 let mut markup = if !self.exact { 61 self.results.join("\n\n---\n")
76 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support traits.");
77 if !self.results.is_empty() {
78 msg.push_str(" \nThese items were found instead:");
79 }
80 msg.push_str("\n\n---\n");
81 msg
82 } else {
83 String::new()
84 };
85
86 markup.push_str(&self.results.join("\n\n---\n"));
87
88 markup
89 } 62 }
90} 63}
91 64
@@ -94,10 +67,10 @@ fn hover_text(
94 desc: Option<String>, 67 desc: Option<String>,
95 mod_path: Option<String>, 68 mod_path: Option<String>,
96) -> Option<String> { 69) -> Option<String> {
97 match (desc, docs, mod_path) { 70 if let Some(desc) = desc {
98 (Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)), 71 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref()))
99 (None, Some(docs), _) => Some(docs), 72 } else {
100 _ => None, 73 docs
101 } 74 }
102} 75}
103 76
@@ -133,7 +106,7 @@ fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
133 .flatten() 106 .flatten()
134 .join("::") 107 .join("::")
135 }); 108 });
136 mod_path 109 mod_path // FIXME: replace dashes with underscores in crate display name
137} 110}
138 111
139fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { 112fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
@@ -170,9 +143,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
170 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 143 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
171 ModuleDef::BuiltinType(it) => Some(it.to_string()), 144 ModuleDef::BuiltinType(it) => Some(it.to_string()),
172 }, 145 },
173 Definition::Local(it) => { 146 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display_truncated(db, None))),
174 Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string()))
175 }
176 Definition::TypeParam(_) | Definition::SelfType(_) => { 147 Definition::TypeParam(_) | Definition::SelfType(_) => {
177 // FIXME: Hover for generic param 148 // FIXME: Hover for generic param
178 None 149 None
@@ -237,7 +208,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
237 } 208 }
238 }?; 209 }?;
239 210
240 res.extend(Some(rust_code_markup(ty.display_truncated(db, None).to_string()))); 211 res.extend(Some(rust_code_markup(&ty.display_truncated(db, None))));
241 let range = sema.original_range(&node).range; 212 let range = sema.original_range(&node).range;
242 Some(RangeInfo::new(range, res)) 213 Some(RangeInfo::new(range, res))
243} 214}
@@ -595,7 +566,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
595 ); 566 );
596 let hover = analysis.hover(position).unwrap().unwrap(); 567 let hover = analysis.hover(position).unwrap().unwrap();
597 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); 568 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing"));
598 assert_eq!(hover.info.is_exact(), true);
599 } 569 }
600 570
601 #[test] 571 #[test]
@@ -618,7 +588,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
618 ); 588 );
619 let hover = analysis.hover(position).unwrap().unwrap(); 589 let hover = analysis.hover(position).unwrap().unwrap();
620 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32")); 590 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32"));
621 assert_eq!(hover.info.is_exact(), true);
622 } 591 }
623 592
624 #[test] 593 #[test]
@@ -635,7 +604,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
635 ); 604 );
636 let hover = analysis.hover(position).unwrap().unwrap(); 605 let hover = analysis.hover(position).unwrap().unwrap();
637 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 606 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
638 assert_eq!(hover.info.is_exact(), true);
639 607
640 /* FIXME: revive these tests 608 /* FIXME: revive these tests
641 let (analysis, position) = single_file_with_position( 609 let (analysis, position) = single_file_with_position(
@@ -651,7 +619,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
651 619
652 let hover = analysis.hover(position).unwrap().unwrap(); 620 let hover = analysis.hover(position).unwrap().unwrap();
653 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 621 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
654 assert_eq!(hover.info.is_exact(), true);
655 622
656 let (analysis, position) = single_file_with_position( 623 let (analysis, position) = single_file_with_position(
657 " 624 "
@@ -665,7 +632,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
665 ); 632 );
666 let hover = analysis.hover(position).unwrap().unwrap(); 633 let hover = analysis.hover(position).unwrap().unwrap();
667 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 634 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
668 assert_eq!(hover.info.is_exact(), true);
669 635
670 let (analysis, position) = single_file_with_position( 636 let (analysis, position) = single_file_with_position(
671 " 637 "
@@ -678,7 +644,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
678 ); 644 );
679 let hover = analysis.hover(position).unwrap().unwrap(); 645 let hover = analysis.hover(position).unwrap().unwrap();
680 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 646 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
681 assert_eq!(hover.info.is_exact(), true);
682 */ 647 */
683 } 648 }
684 649
@@ -696,7 +661,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
696 ); 661 );
697 let hover = analysis.hover(position).unwrap().unwrap(); 662 let hover = analysis.hover(position).unwrap().unwrap();
698 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 663 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
699 assert_eq!(hover.info.is_exact(), true);
700 } 664 }
701 665
702 #[test] 666 #[test]
@@ -714,7 +678,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
714 ); 678 );
715 let hover = analysis.hover(position).unwrap().unwrap(); 679 let hover = analysis.hover(position).unwrap().unwrap();
716 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); 680 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo"));
717 assert_eq!(hover.info.is_exact(), true);
718 } 681 }
719 682
720 #[test] 683 #[test]
@@ -726,7 +689,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
726 ); 689 );
727 let hover = analysis.hover(position).unwrap().unwrap(); 690 let hover = analysis.hover(position).unwrap().unwrap();
728 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 691 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
729 assert_eq!(hover.info.is_exact(), true);
730 } 692 }
731 693
732 #[test] 694 #[test]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e9af80b6c..5ab06c6cf 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -473,9 +473,10 @@ impl Analysis {
473 pub fn structural_search_replace( 473 pub fn structural_search_replace(
474 &self, 474 &self,
475 query: &str, 475 query: &str,
476 parse_only: bool,
476 ) -> Cancelable<Result<SourceChange, SsrError>> { 477 ) -> Cancelable<Result<SourceChange, SsrError>> {
477 self.with_db(|db| { 478 self.with_db(|db| {
478 let edits = ssr::parse_search_replace(query, db)?; 479 let edits = ssr::parse_search_replace(query, parse_only, db)?;
479 Ok(SourceChange::source_file_edits("ssr", edits)) 480 Ok(SourceChange::source_file_edits("ssr", edits))
480 }) 481 })
481 } 482 }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index c011a2e74..1c9710a5d 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,8 +1,10 @@
1//! structural search replace 1//! structural search replace
2 2
3use crate::source_change::SourceFileEdit; 3use crate::source_change::SourceFileEdit;
4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase;
4use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
5use ra_syntax::ast::make::expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
6use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment};
7use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
8use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -10,9 +12,6 @@ use rustc_hash::FxHashMap;
10use std::collections::HashMap; 12use std::collections::HashMap;
11use std::str::FromStr; 13use std::str::FromStr;
12 14
13pub use ra_db::{SourceDatabase, SourceDatabaseExt};
14use ra_ide_db::symbol_index::SymbolsDatabase;
15
16#[derive(Debug, PartialEq)] 15#[derive(Debug, PartialEq)]
17pub struct SsrError(String); 16pub struct SsrError(String);
18 17
@@ -26,14 +25,17 @@ impl std::error::Error for SsrError {}
26 25
27pub fn parse_search_replace( 26pub fn parse_search_replace(
28 query: &str, 27 query: &str,
28 parse_only: bool,
29 db: &RootDatabase, 29 db: &RootDatabase,
30) -> Result<Vec<SourceFileEdit>, SsrError> { 30) -> Result<Vec<SourceFileEdit>, SsrError> {
31 let mut edits = vec![]; 31 let mut edits = vec![];
32 let query: SsrQuery = query.parse()?; 32 let query: SsrQuery = query.parse()?;
33 if parse_only {
34 return Ok(edits);
35 }
33 for &root in db.local_roots().iter() { 36 for &root in db.local_roots().iter() {
34 let sr = db.source_root(root); 37 let sr = db.source_root(root);
35 for file_id in sr.walk() { 38 for file_id in sr.walk() {
36 dbg!(db.file_relative_path(file_id));
37 let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); 39 let matches = find(&query.pattern, db.parse(file_id).tree().syntax());
38 if !matches.matches.is_empty() { 40 if !matches.matches.is_empty() {
39 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) }); 41 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
@@ -106,7 +108,10 @@ impl FromStr for SsrQuery {
106 template = replace_in_template(template, var, new_var); 108 template = replace_in_template(template, var, new_var);
107 } 109 }
108 110
109 let template = expr_from_text(&template).syntax().clone(); 111 let template = try_expr_from_text(&template)
112 .ok_or(SsrError("Template is not an expression".into()))?
113 .syntax()
114 .clone();
110 let mut placeholders = FxHashMap::default(); 115 let mut placeholders = FxHashMap::default();
111 116
112 traverse(&template, &mut |n| { 117 traverse(&template, &mut |n| {
@@ -118,7 +123,13 @@ impl FromStr for SsrQuery {
118 } 123 }
119 }); 124 });
120 125
121 let pattern = SsrPattern { pattern: expr_from_text(&pattern).syntax().clone(), vars }; 126 let pattern = SsrPattern {
127 pattern: try_expr_from_text(&pattern)
128 .ok_or(SsrError("Pattern is not an expression".into()))?
129 .syntax()
130 .clone(),
131 vars,
132 };
122 let template = SsrTemplate { template, placeholders }; 133 let template = SsrTemplate { template, placeholders };
123 Ok(SsrQuery { pattern, template }) 134 Ok(SsrQuery { pattern, template })
124 } 135 }
@@ -284,7 +295,6 @@ mod tests {
284 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 295 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
285 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b"); 296 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
286 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)"); 297 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
287 dbg!(result.template.placeholders);
288 } 298 }
289 299
290 #[test] 300 #[test]
@@ -335,6 +345,16 @@ mod tests {
335 } 345 }
336 346
337 #[test] 347 #[test]
348 fn parser_invlid_pattern() {
349 assert_eq!(parse_error_text(" ==>> ()"), "Parse error: Pattern is not an expression");
350 }
351
352 #[test]
353 fn parser_invlid_template() {
354 assert_eq!(parse_error_text("() ==>> )"), "Parse error: Template is not an expression");
355 }
356
357 #[test]
338 fn parse_match_replace() { 358 fn parse_match_replace() {
339 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 359 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();
340 let input = "fn main() { foo(1+2); }"; 360 let input = "fn main() { foo(1+2); }";
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 55966daf3..f58e436d1 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase;
5use ra_syntax::{ 5use ra_syntax::{
6 algo, AstNode, NodeOrToken, SourceFile, 6 algo, AstNode, NodeOrToken, SourceFile,
7 SyntaxKind::{RAW_STRING, STRING}, 7 SyntaxKind::{RAW_STRING, STRING},
8 SyntaxToken, TextRange, 8 SyntaxToken, TextRange, TextUnit,
9}; 9};
10 10
11pub use ra_db::FileId; 11pub use ra_db::FileId;
@@ -56,19 +56,23 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St
56 let start = text_range.start() - node_range.start(); 56 let start = text_range.start() - node_range.start();
57 57
58 // how many characters we have selected 58 // how many characters we have selected
59 let len = text_range.len().to_usize(); 59 let len = text_range.len();
60 60
61 let node_len = node_range.len().to_usize(); 61 let node_len = node_range.len();
62 62
63 let start = start.to_usize(); 63 let start = start;
64 64
65 // We want to cap our length 65 // We want to cap our length
66 let len = len.min(node_len); 66 let len = len.min(node_len);
67 67
68 // Ensure our slice is inside the actual string 68 // Ensure our slice is inside the actual string
69 let end = if start + len < text.len() { start + len } else { text.len() - start }; 69 let end = if start + len < TextUnit::of_str(&text) {
70 start + len
71 } else {
72 TextUnit::of_str(&text) - start
73 };
70 74
71 let text = &text[start..end]; 75 let text = &text[TextRange::from_to(start, end)];
72 76
73 // Remove possible extra string quotes from the start 77 // Remove possible extra string quotes from the start
74 // and the end of the string 78 // and the end of the string
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 53c65f8bc..cb2cd2479 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -213,14 +213,14 @@ fn foo() {
213 type_char( 213 type_char(
214 '.', 214 '.',
215 r" 215 r"
216 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 216 fn main() {
217 self.child_impl(db, name) 217 xs.foo()
218 <|> 218 <|>
219 } 219 }
220 ", 220 ",
221 r" 221 r"
222 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 222 fn main() {
223 self.child_impl(db, name) 223 xs.foo()
224 . 224 .
225 } 225 }
226 ", 226 ",
@@ -228,8 +228,8 @@ fn foo() {
228 type_char_noop( 228 type_char_noop(
229 '.', 229 '.',
230 r" 230 r"
231 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 231 fn main() {
232 self.child_impl(db, name) 232 xs.foo()
233 <|> 233 <|>
234 } 234 }
235 ", 235 ",
@@ -241,14 +241,14 @@ fn foo() {
241 type_char( 241 type_char(
242 '.', 242 '.',
243 r" 243 r"
244 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 244 fn main() {
245 self.child_impl(db, name) 245 xs.foo()
246 <|>; 246 <|>;
247 } 247 }
248 ", 248 ",
249 r" 249 r"
250 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 250 fn main() {
251 self.child_impl(db, name) 251 xs.foo()
252 .; 252 .;
253 } 253 }
254 ", 254 ",
@@ -256,8 +256,8 @@ fn foo() {
256 type_char_noop( 256 type_char_noop(
257 '.', 257 '.',
258 r" 258 r"
259 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 259 fn main() {
260 self.child_impl(db, name) 260 xs.foo()
261 <|>; 261 <|>;
262 } 262 }
263 ", 263 ",
@@ -269,15 +269,15 @@ fn foo() {
269 type_char( 269 type_char(
270 '.', 270 '.',
271 r" 271 r"
272 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 272 fn main() {
273 self.child_impl(db, name) 273 xs.foo()
274 .first() 274 .first()
275 <|> 275 <|>
276 } 276 }
277 ", 277 ",
278 r" 278 r"
279 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 279 fn main() {
280 self.child_impl(db, name) 280 xs.foo()
281 .first() 281 .first()
282 . 282 .
283 } 283 }
@@ -286,8 +286,8 @@ fn foo() {
286 type_char_noop( 286 type_char_noop(
287 '.', 287 '.',
288 r" 288 r"
289 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 289 fn main() {
290 self.child_impl(db, name) 290 xs.foo()
291 .first() 291 .first()
292 <|> 292 <|>
293 } 293 }
@@ -334,7 +334,7 @@ fn foo() {
334 type_char_noop( 334 type_char_noop(
335 '.', 335 '.',
336 r" 336 r"
337 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 337 fn main() {
338 <|> 338 <|>
339 } 339 }
340 ", 340 ",
@@ -342,7 +342,7 @@ fn foo() {
342 type_char_noop( 342 type_char_noop(
343 '.', 343 '.',
344 r" 344 r"
345 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 345 fn main() {
346 <|> 346 <|>
347 } 347 }
348 ", 348 ",