diff options
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 122 |
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 @@ | |||
1 | use ra_db::{Cancelable, SyntaxDatabase}; | 1 | use ra_db::{SyntaxDatabase}; |
2 | use ra_syntax::{ | 2 | use 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 | ||
8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; |
9 | 9 | ||
10 | pub(crate) fn hover( | 10 | pub(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 | ||
60 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { | 56 | pub(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`. |
89 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Cancelable<Option<String>> { | 81 | fn 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 | ||
100 | impl NavigationTarget { | 90 | impl 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 | } |