diff options
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 48 |
1 files changed, 19 insertions, 29 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 6b5887bda..d91151c15 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,4 +1,4 @@ | |||
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, | 4 | ast::self, |
@@ -7,19 +7,16 @@ use ra_syntax::{ | |||
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, | ||
73 | frange.file_id, | ||
74 | parent_fn | ||
75 | )); | ||
76 | let infer = function.infer(db); | 68 | let infer = function.infer(db); |
77 | let syntax_mapping = function.body_syntax_mapping(db); | 69 | 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 { |