aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/hover.rs
diff options
context:
space:
mode:
authorVille Penttinen <[email protected]>2019-02-26 16:56:04 +0000
committerVille Penttinen <[email protected]>2019-02-26 17:30:17 +0000
commit6f5fd6c9de07a2bcc315acae59845ffb79990bc5 (patch)
treee57fb232aa4d4f22c04da309853548c1bb1acc56 /crates/ra_ide_api/src/hover.rs
parent3ec25841488f9d4325ec25d737c488c18419787c (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/ra_ide_api/src/hover.rs')
-rw-r--r--crates/ra_ide_api/src/hover.rs160
1 files changed, 146 insertions, 14 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
7use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; 7use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget};
8 8
9pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<String>> { 9/// Contains the results when hovering over an item
10#[derive(Debug, Clone)]
11pub struct HoverResult {
12 results: Vec<String>,
13 exact: bool,
14}
15
16impl 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
69pub(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)]
186mod tests { 246mod 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}