diff options
author | Aleksey Kladov <[email protected]> | 2019-01-08 19:33:36 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-01-08 19:33:36 +0000 |
commit | 5b573deb20b15451788dd2861e9fc6e69ed0472e (patch) | |
tree | 0b9438789c69af28fd1d91ca3c25c268ef103bac /crates/ra_ide_api/src/hover.rs | |
parent | 6bca91af532d79abbced5b151cb4188ff8625c04 (diff) |
fix usages after rename
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs new file mode 100644 index 000000000..475524ee1 --- /dev/null +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -0,0 +1,257 @@ | |||
1 | use ra_db::{Cancelable, SyntaxDatabase}; | ||
2 | use ra_syntax::{ | ||
3 | AstNode, SyntaxNode, TreePtr, | ||
4 | ast::{self, NameOwner}, | ||
5 | algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, | ||
6 | }; | ||
7 | |||
8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | ||
9 | |||
10 | pub(crate) fn hover( | ||
11 | db: &RootDatabase, | ||
12 | position: FilePosition, | ||
13 | ) -> Cancelable<Option<RangeInfo<String>>> { | ||
14 | let file = db.source_file(position.file_id); | ||
15 | let mut res = Vec::new(); | ||
16 | |||
17 | let mut range = None; | ||
18 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { | ||
19 | let navs = crate::goto_defenition::reference_defenition(db, position.file_id, name_ref)?; | ||
20 | for nav in navs { | ||
21 | res.extend(doc_text_for(db, nav)?) | ||
22 | } | ||
23 | if !res.is_empty() { | ||
24 | range = Some(name_ref.syntax().range()) | ||
25 | } | ||
26 | } | ||
27 | if range.is_none() { | ||
28 | let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| { | ||
29 | leaf.ancestors() | ||
30 | .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) | ||
31 | }); | ||
32 | let node = ctry!(node); | ||
33 | let frange = FileRange { | ||
34 | file_id: position.file_id, | ||
35 | range: node.range(), | ||
36 | }; | ||
37 | res.extend(type_of(db, frange)?); | ||
38 | range = Some(node.range()); | ||
39 | }; | ||
40 | |||
41 | let range = ctry!(range); | ||
42 | if res.is_empty() { | ||
43 | return Ok(None); | ||
44 | } | ||
45 | let res = RangeInfo::new(range, res.join("\n\n---\n")); | ||
46 | Ok(Some(res)) | ||
47 | } | ||
48 | |||
49 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { | ||
50 | let file = db.source_file(frange.file_id); | ||
51 | let syntax = file.syntax(); | ||
52 | let leaf_node = find_covering_node(syntax, frange.range); | ||
53 | // if we picked identifier, expand to pattern/expression | ||
54 | let node = leaf_node | ||
55 | .ancestors() | ||
56 | .take_while(|it| it.range() == leaf_node.range()) | ||
57 | .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some()) | ||
58 | .unwrap_or(leaf_node); | ||
59 | let parent_fn = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | ||
60 | let function = ctry!(hir::source_binder::function_from_source( | ||
61 | db, | ||
62 | frange.file_id, | ||
63 | parent_fn | ||
64 | )?); | ||
65 | let infer = function.infer(db)?; | ||
66 | let syntax_mapping = function.body_syntax_mapping(db)?; | ||
67 | if let Some(expr) = ast::Expr::cast(node).and_then(|e| syntax_mapping.node_expr(e)) { | ||
68 | Ok(Some(infer[expr].to_string())) | ||
69 | } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| syntax_mapping.node_pat(p)) { | ||
70 | Ok(Some(infer[pat].to_string())) | ||
71 | } else { | ||
72 | Ok(None) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | // FIXME: this should not really use navigation target. Rather, approximatelly | ||
77 | // resovled symbol should return a `DefId`. | ||
78 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Cancelable<Option<String>> { | ||
79 | let result = match (nav.description(db), nav.docs(db)) { | ||
80 | (Some(desc), Some(docs)) => Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs), | ||
81 | (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"), | ||
82 | (None, Some(docs)) => Some(docs), | ||
83 | _ => None, | ||
84 | }; | ||
85 | |||
86 | Ok(result) | ||
87 | } | ||
88 | |||
89 | impl NavigationTarget { | ||
90 | fn node(&self, db: &RootDatabase) -> Option<TreePtr<SyntaxNode>> { | ||
91 | let source_file = db.source_file(self.file_id); | ||
92 | let source_file = source_file.syntax(); | ||
93 | let node = source_file | ||
94 | .descendants() | ||
95 | .find(|node| node.kind() == self.kind && node.range() == self.range)? | ||
96 | .to_owned(); | ||
97 | Some(node) | ||
98 | } | ||
99 | |||
100 | fn docs(&self, db: &RootDatabase) -> Option<String> { | ||
101 | let node = self.node(db)?; | ||
102 | fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> { | ||
103 | let comments = node.doc_comment_text(); | ||
104 | if comments.is_empty() { | ||
105 | None | ||
106 | } else { | ||
107 | Some(comments) | ||
108 | } | ||
109 | } | ||
110 | |||
111 | visitor() | ||
112 | .visit(doc_comments::<ast::FnDef>) | ||
113 | .visit(doc_comments::<ast::StructDef>) | ||
114 | .visit(doc_comments::<ast::EnumDef>) | ||
115 | .visit(doc_comments::<ast::TraitDef>) | ||
116 | .visit(doc_comments::<ast::Module>) | ||
117 | .visit(doc_comments::<ast::TypeDef>) | ||
118 | .visit(doc_comments::<ast::ConstDef>) | ||
119 | .visit(doc_comments::<ast::StaticDef>) | ||
120 | .accept(&node)? | ||
121 | } | ||
122 | |||
123 | /// Get a description of this node. | ||
124 | /// | ||
125 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
126 | fn description(&self, db: &RootDatabase) -> Option<String> { | ||
127 | // TODO: After type inference is done, add type information to improve the output | ||
128 | let node = self.node(db)?; | ||
129 | // TODO: Refactor to be have less repetition | ||
130 | visitor() | ||
131 | .visit(|node: &ast::FnDef| { | ||
132 | let mut string = "fn ".to_string(); | ||
133 | node.name()?.syntax().text().push_to(&mut string); | ||
134 | Some(string) | ||
135 | }) | ||
136 | .visit(|node: &ast::StructDef| { | ||
137 | let mut string = "struct ".to_string(); | ||
138 | node.name()?.syntax().text().push_to(&mut string); | ||
139 | Some(string) | ||
140 | }) | ||
141 | .visit(|node: &ast::EnumDef| { | ||
142 | let mut string = "enum ".to_string(); | ||
143 | node.name()?.syntax().text().push_to(&mut string); | ||
144 | Some(string) | ||
145 | }) | ||
146 | .visit(|node: &ast::TraitDef| { | ||
147 | let mut string = "trait ".to_string(); | ||
148 | node.name()?.syntax().text().push_to(&mut string); | ||
149 | Some(string) | ||
150 | }) | ||
151 | .visit(|node: &ast::Module| { | ||
152 | let mut string = "mod ".to_string(); | ||
153 | node.name()?.syntax().text().push_to(&mut string); | ||
154 | Some(string) | ||
155 | }) | ||
156 | .visit(|node: &ast::TypeDef| { | ||
157 | let mut string = "type ".to_string(); | ||
158 | node.name()?.syntax().text().push_to(&mut string); | ||
159 | Some(string) | ||
160 | }) | ||
161 | .visit(|node: &ast::ConstDef| { | ||
162 | let mut string = "const ".to_string(); | ||
163 | node.name()?.syntax().text().push_to(&mut string); | ||
164 | Some(string) | ||
165 | }) | ||
166 | .visit(|node: &ast::StaticDef| { | ||
167 | let mut string = "static ".to_string(); | ||
168 | node.name()?.syntax().text().push_to(&mut string); | ||
169 | Some(string) | ||
170 | }) | ||
171 | .accept(&node)? | ||
172 | } | ||
173 | } | ||
174 | |||
175 | #[cfg(test)] | ||
176 | mod tests { | ||
177 | use ra_syntax::TextRange; | ||
178 | use crate::mock_analysis::{single_file_with_position, single_file_with_range}; | ||
179 | |||
180 | #[test] | ||
181 | fn hover_shows_type_of_an_expression() { | ||
182 | let (analysis, position) = single_file_with_position( | ||
183 | " | ||
184 | pub fn foo() -> u32 { 1 } | ||
185 | |||
186 | fn main() { | ||
187 | let foo_test = foo()<|>; | ||
188 | } | ||
189 | ", | ||
190 | ); | ||
191 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
192 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); | ||
193 | assert_eq!(hover.info, "u32"); | ||
194 | } | ||
195 | |||
196 | #[test] | ||
197 | fn hover_for_local_variable() { | ||
198 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); | ||
199 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
200 | assert_eq!(hover.info, "i32"); | ||
201 | } | ||
202 | |||
203 | #[test] | ||
204 | fn hover_for_local_variable_pat() { | ||
205 | let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); | ||
206 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
207 | assert_eq!(hover.info, "i32"); | ||
208 | } | ||
209 | |||
210 | #[test] | ||
211 | fn test_type_of_for_function() { | ||
212 | let (analysis, range) = single_file_with_range( | ||
213 | " | ||
214 | pub fn foo() -> u32 { 1 }; | ||
215 | |||
216 | fn main() { | ||
217 | let foo_test = <|>foo()<|>; | ||
218 | } | ||
219 | ", | ||
220 | ); | ||
221 | |||
222 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
223 | assert_eq!("u32", &type_name); | ||
224 | } | ||
225 | |||
226 | // FIXME: improve type_of to make this work | ||
227 | #[test] | ||
228 | fn test_type_of_for_expr_1() { | ||
229 | let (analysis, range) = single_file_with_range( | ||
230 | " | ||
231 | fn main() { | ||
232 | let foo = <|>1 + foo_test<|>; | ||
233 | } | ||
234 | ", | ||
235 | ); | ||
236 | |||
237 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
238 | assert_eq!("[unknown]", &type_name); | ||
239 | } | ||
240 | |||
241 | // FIXME: improve type_of to make this work | ||
242 | #[test] | ||
243 | fn test_type_of_for_expr_2() { | ||
244 | let (analysis, range) = single_file_with_range( | ||
245 | " | ||
246 | fn main() { | ||
247 | let foo: usize = 1; | ||
248 | let bar = <|>1 + foo_test<|>; | ||
249 | } | ||
250 | ", | ||
251 | ); | ||
252 | |||
253 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
254 | assert_eq!("[unknown]", &type_name); | ||
255 | } | ||
256 | |||
257 | } | ||