diff options
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r-- | crates/ra_analysis/src/hover.rs | 35 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 2 |
2 files changed, 27 insertions, 10 deletions
diff --git a/crates/ra_analysis/src/hover.rs b/crates/ra_analysis/src/hover.rs index 758de376e..8217df305 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,7 +167,6 @@ 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 | |||
160 | use crate::mock_analysis::single_file_with_position; | 170 | use crate::mock_analysis::single_file_with_position; |
161 | 171 | ||
162 | #[test] | 172 | #[test] |
@@ -168,10 +178,17 @@ 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 | } | ||
177 | } | 194 | } |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 4d895b004..390c31c3f 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -228,7 +228,7 @@ impl Query { | |||
228 | /// | 228 | /// |
229 | /// Typically, a `NavigationTarget` corresponds to some element in the source | 229 | /// Typically, a `NavigationTarget` corresponds to some element in the source |
230 | /// code, like a function or a struct, but this is not strictly required. | 230 | /// code, like a function or a struct, but this is not strictly required. |
231 | #[derive(Debug)] | 231 | #[derive(Debug, Clone)] |
232 | pub struct NavigationTarget { | 232 | pub struct NavigationTarget { |
233 | file_id: FileId, | 233 | file_id: FileId, |
234 | name: SmolStr, | 234 | name: SmolStr, |