aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r--crates/ra_ide_api/src/hover.rs122
1 files changed, 46 insertions, 76 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index c445d4fa1..d91151c15 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,25 +1,22 @@
1use ra_db::{Cancelable, SyntaxDatabase}; 1use ra_db::{SyntaxDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, SyntaxNode, TreeArc, 3 AstNode, SyntaxNode, TreeArc,
4 ast::{self, NameOwner}, 4 ast::self,
5 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, 5 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}},
6}; 6};
7 7
8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 8use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget};
9 9
10pub(crate) fn hover( 10pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<String>> {
11 db: &RootDatabase,
12 position: FilePosition,
13) -> Cancelable<Option<RangeInfo<String>>> {
14 let file = db.source_file(position.file_id); 11 let file = db.source_file(position.file_id);
15 let mut res = Vec::new(); 12 let mut res = Vec::new();
16 13
17 let mut range = None; 14 let mut range = None;
18 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { 15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
19 use crate::goto_definition::{ReferenceResult::*, reference_definition}; 16 use crate::goto_definition::{ReferenceResult::*, reference_definition};
20 let ref_result = reference_definition(db, position.file_id, name_ref)?; 17 let ref_result = reference_definition(db, position.file_id, name_ref);
21 match ref_result { 18 match ref_result {
22 Exact(nav) => res.extend(doc_text_for(db, nav)?), 19 Exact(nav) => res.extend(doc_text_for(db, nav)),
23 Approximate(navs) => { 20 Approximate(navs) => {
24 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits."); 21 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits.");
25 if !navs.is_empty() { 22 if !navs.is_empty() {
@@ -27,7 +24,7 @@ pub(crate) fn hover(
27 } 24 }
28 res.push(msg); 25 res.push(msg);
29 for nav in navs { 26 for nav in navs {
30 res.extend(doc_text_for(db, nav)?) 27 res.extend(doc_text_for(db, nav))
31 } 28 }
32 } 29 }
33 } 30 }
@@ -39,25 +36,24 @@ pub(crate) fn hover(
39 let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| { 36 let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| {
40 leaf.ancestors() 37 leaf.ancestors()
41 .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) 38 .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
42 }); 39 })?;
43 let node = ctry!(node);
44 let frange = FileRange { 40 let frange = FileRange {
45 file_id: position.file_id, 41 file_id: position.file_id,
46 range: node.range(), 42 range: node.range(),
47 }; 43 };
48 res.extend(type_of(db, frange)?.map(Into::into)); 44 res.extend(type_of(db, frange).map(Into::into));
49 range = Some(node.range()); 45 range = Some(node.range());
50 }; 46 };
51 47
52 let range = ctry!(range); 48 let range = range?;
53 if res.is_empty() { 49 if res.is_empty() {
54 return Ok(None); 50 return None;
55 } 51 }
56 let res = RangeInfo::new(range, res.join("\n\n---\n")); 52 let res = RangeInfo::new(range, res.join("\n\n---\n"));
57 Ok(Some(res)) 53 Some(res)
58} 54}
59 55
60pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { 56pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
61 let file = db.source_file(frange.file_id); 57 let file = db.source_file(frange.file_id);
62 let syntax = file.syntax(); 58 let syntax = file.syntax();
63 let leaf_node = find_covering_node(syntax, frange.range); 59 let leaf_node = find_covering_node(syntax, frange.range);
@@ -67,34 +63,28 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option
67 .take_while(|it| it.range() == leaf_node.range()) 63 .take_while(|it| it.range() == leaf_node.range())
68 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some()) 64 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())
69 .unwrap_or(leaf_node); 65 .unwrap_or(leaf_node);
70 let parent_fn = ctry!(node.ancestors().find_map(ast::FnDef::cast)); 66 let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?;
71 let function = ctry!(hir::source_binder::function_from_source( 67 let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?;
72 db, 68 let infer = function.infer(db);
73 frange.file_id, 69 let syntax_mapping = function.body_syntax_mapping(db);
74 parent_fn
75 )?);
76 let infer = function.infer(db)?;
77 let syntax_mapping = function.body_syntax_mapping(db)?;
78 if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) { 70 if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) {
79 Ok(Some(infer[expr].to_string())) 71 Some(infer[expr].to_string())
80 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) { 72 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) {
81 Ok(Some(infer[pat].to_string())) 73 Some(infer[pat].to_string())
82 } else { 74 } else {
83 Ok(None) 75 None
84 } 76 }
85} 77}
86 78
87// FIXME: this should not really use navigation target. Rather, approximatelly 79// FIXME: this should not really use navigation target. Rather, approximatelly
88// resovled symbol should return a `DefId`. 80// resovled symbol should return a `DefId`.
89fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Cancelable<Option<String>> { 81fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> {
90 let result = match (nav.description(db), nav.docs(db)) { 82 match (nav.description(db), nav.docs(db)) {
91 (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs), 83 (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs),
92 (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"), 84 (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"),
93 (None, Some(docs)) => Some(docs), 85 (None, Some(docs)) => Some(docs),
94 _ => None, 86 _ => None,
95 }; 87 }
96
97 Ok(result)
98} 88}
99 89
100impl NavigationTarget { 90impl NavigationTarget {
@@ -137,48 +127,29 @@ impl NavigationTarget {
137 fn description(&self, db: &RootDatabase) -> Option<String> { 127 fn description(&self, db: &RootDatabase) -> Option<String> {
138 // TODO: After type inference is done, add type information to improve the output 128 // TODO: After type inference is done, add type information to improve the output
139 let node = self.node(db)?; 129 let node = self.node(db)?;
140 // TODO: Refactor to be have less repetition 130
131 fn visit_node<T>(node: &T, label: &str) -> Option<String>
132 where
133 T: ast::NameOwner + ast::VisibilityOwner,
134 {
135 let mut string = node
136 .visibility()
137 .map(|v| format!("{} ", v.syntax().text()))
138 .unwrap_or_default();
139 string.push_str(label);
140 node.name()?.syntax().text().push_to(&mut string);
141 Some(string)
142 }
143
141 visitor() 144 visitor()
142 .visit(|node: &ast::FnDef| { 145 .visit(|node: &ast::FnDef| visit_node(node, "fn "))
143 let mut string = "fn ".to_string(); 146 .visit(|node: &ast::StructDef| visit_node(node, "struct "))
144 node.name()?.syntax().text().push_to(&mut string); 147 .visit(|node: &ast::EnumDef| visit_node(node, "enum "))
145 Some(string) 148 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
146 }) 149 .visit(|node: &ast::Module| visit_node(node, "mod "))
147 .visit(|node: &ast::StructDef| { 150 .visit(|node: &ast::TypeDef| visit_node(node, "type "))
148 let mut string = "struct ".to_string(); 151 .visit(|node: &ast::ConstDef| visit_node(node, "const "))
149 node.name()?.syntax().text().push_to(&mut string); 152 .visit(|node: &ast::StaticDef| visit_node(node, "static "))
150 Some(string)
151 })
152 .visit(|node: &ast::EnumDef| {
153 let mut string = "enum ".to_string();
154 node.name()?.syntax().text().push_to(&mut string);
155 Some(string)
156 })
157 .visit(|node: &ast::TraitDef| {
158 let mut string = "trait ".to_string();
159 node.name()?.syntax().text().push_to(&mut string);
160 Some(string)
161 })
162 .visit(|node: &ast::Module| {
163 let mut string = "mod ".to_string();
164 node.name()?.syntax().text().push_to(&mut string);
165 Some(string)
166 })
167 .visit(|node: &ast::TypeDef| {
168 let mut string = "type ".to_string();
169 node.name()?.syntax().text().push_to(&mut string);
170 Some(string)
171 })
172 .visit(|node: &ast::ConstDef| {
173 let mut string = "const ".to_string();
174 node.name()?.syntax().text().push_to(&mut string);
175 Some(string)
176 })
177 .visit(|node: &ast::StaticDef| {
178 let mut string = "static ".to_string();
179 node.name()?.syntax().text().push_to(&mut string);
180 Some(string)
181 })
182 .accept(&node)? 153 .accept(&node)?
183 } 154 }
184} 155}
@@ -249,20 +220,19 @@ mod tests {
249 assert_eq!("[unknown]", &type_name); 220 assert_eq!("[unknown]", &type_name);
250 } 221 }
251 222
252 // FIXME: improve type_of to make this work
253 #[test] 223 #[test]
254 fn test_type_of_for_expr_2() { 224 fn test_type_of_for_expr_2() {
255 let (analysis, range) = single_file_with_range( 225 let (analysis, range) = single_file_with_range(
256 " 226 "
257 fn main() { 227 fn main() {
258 let foo: usize = 1; 228 let foo: usize = 1;
259 let bar = <|>1 + foo_test<|>; 229 let bar = <|>1 + foo<|>;
260 } 230 }
261 ", 231 ",
262 ); 232 );
263 233
264 let type_name = analysis.type_of(range).unwrap().unwrap(); 234 let type_name = analysis.type_of(range).unwrap().unwrap();
265 assert_eq!("[unknown]", &type_name); 235 assert_eq!("usize", &type_name);
266 } 236 }
267 237
268} 238}