diff options
Diffstat (limited to 'crates/ra_analysis/src/hover.rs')
-rw-r--r-- | crates/ra_analysis/src/hover.rs | 85 |
1 files changed, 75 insertions, 10 deletions
diff --git a/crates/ra_analysis/src/hover.rs b/crates/ra_analysis/src/hover.rs index 758de376e..2cf79eebf 100644 --- a/crates/ra_analysis/src/hover.rs +++ b/crates/ra_analysis/src/hover.rs | |||
@@ -14,23 +14,28 @@ pub(crate) fn hover( | |||
14 | ) -> Cancelable<Option<RangeInfo<String>>> { | 14 | ) -> Cancelable<Option<RangeInfo<String>>> { |
15 | let file = db.source_file(position.file_id); | 15 | let file = db.source_file(position.file_id); |
16 | let mut res = Vec::new(); | 16 | let mut res = Vec::new(); |
17 | let range = if let Some(name_ref) = | 17 | |
18 | find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) | 18 | let mut range = None; |
19 | { | 19 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { |
20 | let navs = crate::goto_defenition::reference_defenition(db, position.file_id, name_ref)?; | 20 | let navs = crate::goto_defenition::reference_defenition(db, position.file_id, name_ref)?; |
21 | for nav in navs { | 21 | for nav in navs { |
22 | res.extend(doc_text_for(db, nav)?) | 22 | res.extend(doc_text_for(db, nav)?) |
23 | } | 23 | } |
24 | name_ref.syntax().range() | 24 | if !res.is_empty() { |
25 | } else { | 25 | range = Some(name_ref.syntax().range()) |
26 | } | ||
27 | } | ||
28 | if range.is_none() { | ||
26 | let expr: ast::Expr = ctry!(find_node_at_offset(file.syntax(), position.offset)); | 29 | let expr: ast::Expr = ctry!(find_node_at_offset(file.syntax(), position.offset)); |
27 | let frange = FileRange { | 30 | let frange = FileRange { |
28 | file_id: position.file_id, | 31 | file_id: position.file_id, |
29 | range: expr.syntax().range(), | 32 | range: expr.syntax().range(), |
30 | }; | 33 | }; |
31 | res.extend(type_of(db, frange)?); | 34 | res.extend(type_of(db, frange)?); |
32 | expr.syntax().range() | 35 | range = Some(expr.syntax().range()); |
33 | }; | 36 | }; |
37 | |||
38 | let range = ctry!(range); | ||
34 | if res.is_empty() { | 39 | if res.is_empty() { |
35 | return Ok(None); | 40 | return Ok(None); |
36 | } | 41 | } |
@@ -41,7 +46,13 @@ pub(crate) fn hover( | |||
41 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { | 46 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Cancelable<Option<String>> { |
42 | let file = db.source_file(frange.file_id); | 47 | let file = db.source_file(frange.file_id); |
43 | let syntax = file.syntax(); | 48 | let syntax = file.syntax(); |
44 | let node = find_covering_node(syntax, frange.range); | 49 | let leaf_node = find_covering_node(syntax, frange.range); |
50 | // if we picked identifier, expand to pattern/expression | ||
51 | let node = leaf_node | ||
52 | .ancestors() | ||
53 | .take_while(|it| it.range() == leaf_node.range()) | ||
54 | .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some()) | ||
55 | .unwrap_or(leaf_node); | ||
45 | let parent_fn = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | 56 | let parent_fn = ctry!(node.ancestors().find_map(ast::FnDef::cast)); |
46 | let function = ctry!(hir::source_binder::function_from_source( | 57 | let function = ctry!(hir::source_binder::function_from_source( |
47 | db, | 58 | db, |
@@ -156,8 +167,7 @@ impl NavigationTarget { | |||
156 | #[cfg(test)] | 167 | #[cfg(test)] |
157 | mod tests { | 168 | mod tests { |
158 | use ra_syntax::TextRange; | 169 | use ra_syntax::TextRange; |
159 | 170 | use crate::mock_analysis::{single_file_with_position, single_file_with_range}; | |
160 | use crate::mock_analysis::single_file_with_position; | ||
161 | 171 | ||
162 | #[test] | 172 | #[test] |
163 | fn hover_shows_type_of_an_expression() { | 173 | fn hover_shows_type_of_an_expression() { |
@@ -168,10 +178,65 @@ mod tests { | |||
168 | fn main() { | 178 | fn main() { |
169 | let foo_test = foo()<|>; | 179 | let foo_test = foo()<|>; |
170 | } | 180 | } |
171 | ", | 181 | ", |
172 | ); | 182 | ); |
173 | let hover = analysis.hover(position).unwrap().unwrap(); | 183 | let hover = analysis.hover(position).unwrap().unwrap(); |
174 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); | 184 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); |
175 | assert_eq!(hover.info, "u32"); | 185 | assert_eq!(hover.info, "u32"); |
176 | } | 186 | } |
187 | |||
188 | #[test] | ||
189 | fn hover_for_local_variable() { | ||
190 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); | ||
191 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
192 | assert_eq!(hover.info, "i32"); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn test_type_of_for_function() { | ||
197 | let (analysis, range) = single_file_with_range( | ||
198 | " | ||
199 | pub fn foo() -> u32 { 1 }; | ||
200 | |||
201 | fn main() { | ||
202 | let foo_test = <|>foo()<|>; | ||
203 | } | ||
204 | ", | ||
205 | ); | ||
206 | |||
207 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
208 | assert_eq!("u32", &type_name); | ||
209 | } | ||
210 | |||
211 | // FIXME: improve type_of to make this work | ||
212 | #[test] | ||
213 | fn test_type_of_for_expr_1() { | ||
214 | let (analysis, range) = single_file_with_range( | ||
215 | " | ||
216 | fn main() { | ||
217 | let foo = <|>1 + foo_test<|>; | ||
218 | } | ||
219 | ", | ||
220 | ); | ||
221 | |||
222 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
223 | assert_eq!("[unknown]", &type_name); | ||
224 | } | ||
225 | |||
226 | // FIXME: improve type_of to make this work | ||
227 | #[test] | ||
228 | fn test_type_of_for_expr_2() { | ||
229 | let (analysis, range) = single_file_with_range( | ||
230 | " | ||
231 | fn main() { | ||
232 | let foo: usize = 1; | ||
233 | let bar = <|>1 + foo_test<|>; | ||
234 | } | ||
235 | ", | ||
236 | ); | ||
237 | |||
238 | let type_name = analysis.type_of(range).unwrap().unwrap(); | ||
239 | assert_eq!("[unknown]", &type_name); | ||
240 | } | ||
241 | |||
177 | } | 242 | } |