diff options
author | Ville Penttinen <[email protected]> | 2019-02-26 16:56:04 +0000 |
---|---|---|
committer | Ville Penttinen <[email protected]> | 2019-02-26 17:30:17 +0000 |
commit | 6f5fd6c9de07a2bcc315acae59845ffb79990bc5 (patch) | |
tree | e57fb232aa4d4f22c04da309853548c1bb1acc56 /crates | |
parent | 3ec25841488f9d4325ec25d737c488c18419787c (diff) |
Add new type HoverResult to contain the results of hovering
This makes testing hovers easier as well as allows us to do more things with the
results if needed.
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 160 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 2 |
3 files changed, 149 insertions, 16 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 729f435d3..91d8e2ffa 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -6,9 +6,69 @@ use ra_syntax::{ | |||
6 | 6 | ||
7 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 7 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; |
8 | 8 | ||
9 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<String>> { | 9 | /// Contains the results when hovering over an item |
10 | #[derive(Debug, Clone)] | ||
11 | pub struct HoverResult { | ||
12 | results: Vec<String>, | ||
13 | exact: bool, | ||
14 | } | ||
15 | |||
16 | impl HoverResult { | ||
17 | pub fn new() -> HoverResult { | ||
18 | HoverResult { | ||
19 | results: Vec::new(), | ||
20 | // We assume exact by default | ||
21 | exact: true, | ||
22 | } | ||
23 | } | ||
24 | |||
25 | pub fn extend(&mut self, item: Option<String>) { | ||
26 | self.results.extend(item); | ||
27 | } | ||
28 | |||
29 | pub fn is_exact(&self) -> bool { | ||
30 | self.exact | ||
31 | } | ||
32 | |||
33 | pub fn is_empty(&self) -> bool { | ||
34 | self.results.is_empty() | ||
35 | } | ||
36 | |||
37 | pub fn len(&self) -> usize { | ||
38 | self.results.len() | ||
39 | } | ||
40 | |||
41 | pub fn first(&self) -> Option<&str> { | ||
42 | self.results.first().map(String::as_str) | ||
43 | } | ||
44 | |||
45 | pub fn results(&self) -> &[String] { | ||
46 | &self.results | ||
47 | } | ||
48 | |||
49 | /// Returns the results converted into markup | ||
50 | /// for displaying in a UI | ||
51 | pub fn to_markup(&self) -> String { | ||
52 | let mut markup = if !self.exact { | ||
53 | 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."); | ||
54 | if !self.results.is_empty() { | ||
55 | msg.push_str(" \nThese items were found instead:"); | ||
56 | } | ||
57 | msg.push_str("\n\n---\n"); | ||
58 | msg | ||
59 | } else { | ||
60 | String::new() | ||
61 | }; | ||
62 | |||
63 | markup.push_str(&self.results.join("\n\n---\n")); | ||
64 | |||
65 | markup | ||
66 | } | ||
67 | } | ||
68 | |||
69 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | ||
10 | let file = db.parse(position.file_id); | 70 | let file = db.parse(position.file_id); |
11 | let mut res = Vec::new(); | 71 | let mut res = HoverResult::new(); |
12 | 72 | ||
13 | let mut range = None; | 73 | let mut range = None; |
14 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { | 74 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { |
@@ -17,11 +77,9 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
17 | match ref_result { | 77 | match ref_result { |
18 | Exact(nav) => res.extend(doc_text_for(db, nav)), | 78 | Exact(nav) => res.extend(doc_text_for(db, nav)), |
19 | Approximate(navs) => { | 79 | Approximate(navs) => { |
20 | 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."); | 80 | // We are no longer exact |
21 | if !navs.is_empty() { | 81 | res.exact = false; |
22 | msg.push_str(" \nThese items were found instead:"); | 82 | |
23 | } | ||
24 | res.push(msg); | ||
25 | for nav in navs { | 83 | for nav in navs { |
26 | res.extend(doc_text_for(db, nav)) | 84 | res.extend(doc_text_for(db, nav)) |
27 | } | 85 | } |
@@ -31,6 +89,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
31 | range = Some(name_ref.syntax().range()) | 89 | range = Some(name_ref.syntax().range()) |
32 | } | 90 | } |
33 | } | 91 | } |
92 | |||
34 | if range.is_none() { | 93 | if range.is_none() { |
35 | let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| { | 94 | let node = find_leaf_at_offset(file.syntax(), position.offset).find_map(|leaf| { |
36 | leaf.ancestors().find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) | 95 | leaf.ancestors().find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some()) |
@@ -44,7 +103,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
44 | if res.is_empty() { | 103 | if res.is_empty() { |
45 | return None; | 104 | return None; |
46 | } | 105 | } |
47 | let res = RangeInfo::new(range, res.join("\n\n---\n")); | 106 | let res = RangeInfo::new(range, res); |
48 | Some(res) | 107 | Some(res) |
49 | } | 108 | } |
50 | 109 | ||
@@ -136,6 +195,7 @@ impl NavigationTarget { | |||
136 | } | 195 | } |
137 | } | 196 | } |
138 | 197 | ||
198 | // FIXME: This is also partially copied from `structure.rs` | ||
139 | fn visit_fn(node: &ast::FnDef) -> Option<String> { | 199 | fn visit_fn(node: &ast::FnDef) -> Option<String> { |
140 | let mut detail = | 200 | let mut detail = |
141 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); | 201 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); |
@@ -185,7 +245,24 @@ impl NavigationTarget { | |||
185 | #[cfg(test)] | 245 | #[cfg(test)] |
186 | mod tests { | 246 | mod tests { |
187 | use ra_syntax::TextRange; | 247 | use ra_syntax::TextRange; |
188 | use crate::mock_analysis::{single_file_with_position, single_file_with_range}; | 248 | use crate::mock_analysis::{single_file_with_position, single_file_with_range, analysis_and_position}; |
249 | |||
250 | fn trim_markup(s: &str) -> &str { | ||
251 | s.trim_start_matches("```rust\n").trim_end_matches("\n```") | ||
252 | } | ||
253 | |||
254 | fn check_hover_result(fixture: &str, expected: &[&str]) { | ||
255 | let (analysis, position) = analysis_and_position(fixture); | ||
256 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
257 | |||
258 | for (markup, expected) in | ||
259 | hover.info.results().iter().zip(expected.iter().chain(std::iter::repeat(&"<missing>"))) | ||
260 | { | ||
261 | assert_eq!(trim_markup(&markup), *expected); | ||
262 | } | ||
263 | |||
264 | assert_eq!(hover.info.len(), expected.len()); | ||
265 | } | ||
189 | 266 | ||
190 | #[test] | 267 | #[test] |
191 | fn hover_shows_type_of_an_expression() { | 268 | fn hover_shows_type_of_an_expression() { |
@@ -200,7 +277,62 @@ mod tests { | |||
200 | ); | 277 | ); |
201 | let hover = analysis.hover(position).unwrap().unwrap(); | 278 | let hover = analysis.hover(position).unwrap().unwrap(); |
202 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); | 279 | assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); |
203 | assert_eq!(hover.info, "u32"); | 280 | assert_eq!(hover.info.first(), Some("u32")); |
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn hover_shows_fn_signature() { | ||
285 | // Single file with result | ||
286 | check_hover_result( | ||
287 | r#" | ||
288 | //- /main.rs | ||
289 | pub fn foo() -> u32 { 1 } | ||
290 | |||
291 | fn main() { | ||
292 | let foo_test = fo<|>o(); | ||
293 | } | ||
294 | "#, | ||
295 | &["pub fn foo() -> u32"], | ||
296 | ); | ||
297 | |||
298 | // Multiple results | ||
299 | check_hover_result( | ||
300 | r#" | ||
301 | //- /a.rs | ||
302 | pub fn foo() -> u32 { 1 } | ||
303 | |||
304 | //- /b.rs | ||
305 | pub fn foo() -> &str { "" } | ||
306 | |||
307 | //- /c.rs | ||
308 | pub fn foo(a: u32, b: u32) {} | ||
309 | |||
310 | //- /main.rs | ||
311 | mod a; | ||
312 | mod b; | ||
313 | mod c; | ||
314 | |||
315 | fn main() { | ||
316 | let foo_test = fo<|>o(); | ||
317 | } | ||
318 | "#, | ||
319 | &["pub fn foo() -> &str", "pub fn foo() -> u32", "pub fn foo(a: u32, b: u32)"], | ||
320 | ); | ||
321 | } | ||
322 | |||
323 | #[test] | ||
324 | fn hover_shows_fn_signature_with_type_params() { | ||
325 | check_hover_result( | ||
326 | r#" | ||
327 | //- /main.rs | ||
328 | pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { } | ||
329 | |||
330 | fn main() { | ||
331 | let foo_test = fo<|>o(); | ||
332 | } | ||
333 | "#, | ||
334 | &["pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str"], | ||
335 | ); | ||
204 | } | 336 | } |
205 | 337 | ||
206 | #[test] | 338 | #[test] |
@@ -217,21 +349,21 @@ mod tests { | |||
217 | ); | 349 | ); |
218 | let hover = analysis.hover(position).unwrap().unwrap(); | 350 | let hover = analysis.hover(position).unwrap().unwrap(); |
219 | // not the nicest way to show it currently | 351 | // not the nicest way to show it currently |
220 | assert_eq!(hover.info, "Some<i32>(T) -> Option<T>"); | 352 | assert_eq!(hover.info.first(), Some("Some<i32>(T) -> Option<T>")); |
221 | } | 353 | } |
222 | 354 | ||
223 | #[test] | 355 | #[test] |
224 | fn hover_for_local_variable() { | 356 | fn hover_for_local_variable() { |
225 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); | 357 | let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); |
226 | let hover = analysis.hover(position).unwrap().unwrap(); | 358 | let hover = analysis.hover(position).unwrap().unwrap(); |
227 | assert_eq!(hover.info, "i32"); | 359 | assert_eq!(hover.info.first(), Some("i32")); |
228 | } | 360 | } |
229 | 361 | ||
230 | #[test] | 362 | #[test] |
231 | fn hover_for_local_variable_pat() { | 363 | fn hover_for_local_variable_pat() { |
232 | let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); | 364 | let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); |
233 | let hover = analysis.hover(position).unwrap().unwrap(); | 365 | let hover = analysis.hover(position).unwrap().unwrap(); |
234 | assert_eq!(hover.info, "i32"); | 366 | assert_eq!(hover.info.first(), Some("i32")); |
235 | } | 367 | } |
236 | 368 | ||
237 | #[test] | 369 | #[test] |
@@ -298,6 +430,6 @@ mod tests { | |||
298 | ", | 430 | ", |
299 | ); | 431 | ); |
300 | let hover = analysis.hover(position).unwrap().unwrap(); | 432 | let hover = analysis.hover(position).unwrap().unwrap(); |
301 | assert_eq!(hover.info, "Thing"); | 433 | assert_eq!(hover.info.first(), Some("Thing")); |
302 | } | 434 | } |
303 | } | 435 | } |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 076a8396c..6546d0644 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -58,6 +58,7 @@ pub use crate::{ | |||
58 | navigation_target::NavigationTarget, | 58 | navigation_target::NavigationTarget, |
59 | references::ReferenceSearchResult, | 59 | references::ReferenceSearchResult, |
60 | assists::{Assist, AssistId}, | 60 | assists::{Assist, AssistId}, |
61 | hover::{HoverResult}, | ||
61 | }; | 62 | }; |
62 | pub use ra_ide_api_light::{ | 63 | pub use ra_ide_api_light::{ |
63 | Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit, | 64 | Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit, |
@@ -328,7 +329,7 @@ impl Analysis { | |||
328 | } | 329 | } |
329 | 330 | ||
330 | /// Returns a short text describing element at position. | 331 | /// Returns a short text describing element at position. |
331 | pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<String>>> { | 332 | pub fn hover(&self, position: FilePosition) -> Cancelable<Option<RangeInfo<HoverResult>>> { |
332 | self.with_db(|db| hover::hover(db, position)) | 333 | self.with_db(|db| hover::hover(db, position)) |
333 | } | 334 | } |
334 | 335 | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 5da731801..dce6fcc67 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -441,7 +441,7 @@ pub fn handle_hover( | |||
441 | let res = Hover { | 441 | let res = Hover { |
442 | contents: HoverContents::Markup(MarkupContent { | 442 | contents: HoverContents::Markup(MarkupContent { |
443 | kind: MarkupKind::Markdown, | 443 | kind: MarkupKind::Markdown, |
444 | value: info.info, | 444 | value: info.info.to_markup(), |
445 | }), | 445 | }), |
446 | range: Some(range), | 446 | range: Some(range), |
447 | }; | 447 | }; |