aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r--crates/ra_ide/src/hover.rs63
1 files changed, 45 insertions, 18 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 3f88bb260..1c6ca36df 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -2,7 +2,7 @@
2 2
3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; 3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{defs::NameDefinition, RootDatabase};
6use ra_syntax::{ 6use ra_syntax::{
7 algo::find_covering_element, 7 algo::find_covering_element,
8 ast::{self, DocCommentsOwner}, 8 ast::{self, DocCommentsOwner},
@@ -13,8 +13,8 @@ use ra_syntax::{
13 13
14use crate::{ 14use crate::{
15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
16 expand::descend_into_macros, 16 expand::{descend_into_macros, original_range},
17 references::{classify_name, classify_name_ref, NameKind, NameKind::*}, 17 references::{classify_name, classify_name_ref},
18 FilePosition, FileRange, RangeInfo, 18 FilePosition, FileRange, RangeInfo,
19}; 19};
20 20
@@ -92,20 +92,20 @@ fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> {
92 } 92 }
93} 93}
94 94
95fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<String> { 95fn hover_text_from_name_kind(db: &RootDatabase, def: NameDefinition) -> Option<String> {
96 return match name_kind { 96 return match def {
97 Macro(it) => { 97 NameDefinition::Macro(it) => {
98 let src = it.source(db); 98 let src = it.source(db);
99 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value))) 99 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)))
100 } 100 }
101 StructField(it) => { 101 NameDefinition::StructField(it) => {
102 let src = it.source(db); 102 let src = it.source(db);
103 match src.value { 103 match src.value {
104 hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), 104 hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()),
105 _ => None, 105 _ => None,
106 } 106 }
107 } 107 }
108 ModuleDef(it) => match it { 108 NameDefinition::ModuleDef(it) => match it {
109 hir::ModuleDef::Module(it) => match it.definition_source(db).value { 109 hir::ModuleDef::Module(it) => match it.definition_source(db).value {
110 hir::ModuleSource::Module(it) => { 110 hir::ModuleSource::Module(it) => {
111 hover_text(it.doc_comment_text(), it.short_label()) 111 hover_text(it.doc_comment_text(), it.short_label())
@@ -123,8 +123,10 @@ fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<S
123 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), 123 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it),
124 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), 124 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()),
125 }, 125 },
126 Local(it) => Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())), 126 NameDefinition::Local(it) => {
127 TypeParam(_) | SelfType(_) => { 127 Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string()))
128 }
129 NameDefinition::TypeParam(_) | NameDefinition::SelfType(_) => {
128 // FIXME: Hover for generic param 130 // FIXME: Hover for generic param
129 None 131 None
130 } 132 }
@@ -148,17 +150,18 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
148 let mut res = HoverResult::new(); 150 let mut res = HoverResult::new();
149 151
150 let mut sb = SourceBinder::new(db); 152 let mut sb = SourceBinder::new(db);
151 if let Some((range, name_kind)) = match_ast! { 153 if let Some((node, name_kind)) = match_ast! {
152 match (token.value.parent()) { 154 match (token.value.parent()) {
153 ast::NameRef(name_ref) => { 155 ast::NameRef(name_ref) => {
154 classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind)) 156 classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().clone(), d))
155 }, 157 },
156 ast::Name(name) => { 158 ast::Name(name) => {
157 classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind)) 159 classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().clone(), d))
158 }, 160 },
159 _ => None, 161 _ => None,
160 } 162 }
161 } { 163 } {
164 let range = original_range(db, token.with_value(&node)).range;
162 res.extend(hover_text_from_name_kind(db, name_kind)); 165 res.extend(hover_text_from_name_kind(db, name_kind));
163 166
164 if !res.is_empty() { 167 if !res.is_empty() {
@@ -171,8 +174,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
171 .ancestors() 174 .ancestors()
172 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 175 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
173 176
174 // The following logic will not work if token is coming from a macro 177 let frange = original_range(db, token.with_value(&node));
175 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
176 res.extend(type_of(db, frange).map(rust_code_markup)); 178 res.extend(type_of(db, frange).map(rust_code_markup));
177 if res.is_empty() { 179 if res.is_empty() {
178 return None; 180 return None;
@@ -220,6 +222,7 @@ mod tests {
220 use crate::mock_analysis::{ 222 use crate::mock_analysis::{
221 analysis_and_position, single_file_with_position, single_file_with_range, 223 analysis_and_position, single_file_with_position, single_file_with_range,
222 }; 224 };
225 use ra_db::FileLoader;
223 use ra_syntax::TextRange; 226 use ra_syntax::TextRange;
224 227
225 fn trim_markup(s: &str) -> &str { 228 fn trim_markup(s: &str) -> &str {
@@ -230,7 +233,7 @@ mod tests {
230 s.map(trim_markup) 233 s.map(trim_markup)
231 } 234 }
232 235
233 fn check_hover_result(fixture: &str, expected: &[&str]) { 236 fn check_hover_result(fixture: &str, expected: &[&str]) -> String {
234 let (analysis, position) = analysis_and_position(fixture); 237 let (analysis, position) = analysis_and_position(fixture);
235 let hover = analysis.hover(position).unwrap().unwrap(); 238 let hover = analysis.hover(position).unwrap().unwrap();
236 let mut results = Vec::from(hover.info.results()); 239 let mut results = Vec::from(hover.info.results());
@@ -243,6 +246,9 @@ mod tests {
243 } 246 }
244 247
245 assert_eq!(hover.info.len(), expected.len()); 248 assert_eq!(hover.info.len(), expected.len());
249
250 let content = analysis.db.file_text(position.file_id);
251 content[hover.range].to_string()
246 } 252 }
247 253
248 #[test] 254 #[test]
@@ -711,7 +717,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
711 717
712 #[test] 718 #[test]
713 fn test_hover_through_macro() { 719 fn test_hover_through_macro() {
714 check_hover_result( 720 let hover_on = check_hover_result(
715 " 721 "
716 //- /lib.rs 722 //- /lib.rs
717 macro_rules! id { 723 macro_rules! id {
@@ -726,11 +732,13 @@ fn func(foo: i32) { if true { <|>foo; }; }
726 ", 732 ",
727 &["fn foo()"], 733 &["fn foo()"],
728 ); 734 );
735
736 assert_eq!(hover_on, "foo")
729 } 737 }
730 738
731 #[test] 739 #[test]
732 fn test_hover_through_expr_in_macro() { 740 fn test_hover_through_expr_in_macro() {
733 check_hover_result( 741 let hover_on = check_hover_result(
734 " 742 "
735 //- /lib.rs 743 //- /lib.rs
736 macro_rules! id { 744 macro_rules! id {
@@ -742,5 +750,24 @@ fn func(foo: i32) { if true { <|>foo; }; }
742 ", 750 ",
743 &["u32"], 751 &["u32"],
744 ); 752 );
753
754 assert_eq!(hover_on, "bar")
755 }
756
757 #[test]
758 fn test_hover_non_ascii_space_doc() {
759 check_hover_result(
760 "
761 //- /lib.rs
762 /// <- `\u{3000}` here
763 fn foo() {
764 }
765
766 fn bar() {
767 fo<|>o();
768 }
769 ",
770 &["fn foo()\n```\n\n<- `\u{3000}` here"],
771 );
745 } 772 }
746} 773}