From 6f5fd6c9de07a2bcc315acae59845ffb79990bc5 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Tue, 26 Feb 2019 18:56:04 +0200 Subject: 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. --- crates/ra_ide_api/src/hover.rs | 160 ++++++++++++++++++++++--- crates/ra_ide_api/src/lib.rs | 3 +- crates/ra_lsp_server/src/main_loop/handlers.rs | 2 +- 3 files changed, 149 insertions(+), 16 deletions(-) (limited to 'crates') 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::{ use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; -pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { +/// Contains the results when hovering over an item +#[derive(Debug, Clone)] +pub struct HoverResult { + results: Vec, + exact: bool, +} + +impl HoverResult { + pub fn new() -> HoverResult { + HoverResult { + results: Vec::new(), + // We assume exact by default + exact: true, + } + } + + pub fn extend(&mut self, item: Option) { + self.results.extend(item); + } + + pub fn is_exact(&self) -> bool { + self.exact + } + + pub fn is_empty(&self) -> bool { + self.results.is_empty() + } + + pub fn len(&self) -> usize { + self.results.len() + } + + pub fn first(&self) -> Option<&str> { + self.results.first().map(String::as_str) + } + + pub fn results(&self) -> &[String] { + &self.results + } + + /// Returns the results converted into markup + /// for displaying in a UI + pub fn to_markup(&self) -> String { + let mut markup = if !self.exact { + 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."); + if !self.results.is_empty() { + msg.push_str(" \nThese items were found instead:"); + } + msg.push_str("\n\n---\n"); + msg + } else { + String::new() + }; + + markup.push_str(&self.results.join("\n\n---\n")); + + markup + } +} + +pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { let file = db.parse(position.file_id); - let mut res = Vec::new(); + let mut res = HoverResult::new(); let mut range = None; if let Some(name_ref) = find_node_at_offset::(file.syntax(), position.offset) { @@ -17,11 +77,9 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option res.extend(doc_text_for(db, nav)), Approximate(navs) => { - 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."); - if !navs.is_empty() { - msg.push_str(" \nThese items were found instead:"); - } - res.push(msg); + // We are no longer exact + res.exact = false; + for nav in navs { res.extend(doc_text_for(db, nav)) } @@ -31,6 +89,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option Option Option { let mut detail = node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); @@ -185,7 +245,24 @@ impl NavigationTarget { #[cfg(test)] mod tests { use ra_syntax::TextRange; - use crate::mock_analysis::{single_file_with_position, single_file_with_range}; + use crate::mock_analysis::{single_file_with_position, single_file_with_range, analysis_and_position}; + + fn trim_markup(s: &str) -> &str { + s.trim_start_matches("```rust\n").trim_end_matches("\n```") + } + + fn check_hover_result(fixture: &str, expected: &[&str]) { + let (analysis, position) = analysis_and_position(fixture); + let hover = analysis.hover(position).unwrap().unwrap(); + + for (markup, expected) in + hover.info.results().iter().zip(expected.iter().chain(std::iter::repeat(&""))) + { + assert_eq!(trim_markup(&markup), *expected); + } + + assert_eq!(hover.info.len(), expected.len()); + } #[test] fn hover_shows_type_of_an_expression() { @@ -200,7 +277,62 @@ mod tests { ); let hover = analysis.hover(position).unwrap().unwrap(); assert_eq!(hover.range, TextRange::from_to(95.into(), 100.into())); - assert_eq!(hover.info, "u32"); + assert_eq!(hover.info.first(), Some("u32")); + } + + #[test] + fn hover_shows_fn_signature() { + // Single file with result + check_hover_result( + r#" + //- /main.rs + pub fn foo() -> u32 { 1 } + + fn main() { + let foo_test = fo<|>o(); + } + "#, + &["pub fn foo() -> u32"], + ); + + // Multiple results + check_hover_result( + r#" + //- /a.rs + pub fn foo() -> u32 { 1 } + + //- /b.rs + pub fn foo() -> &str { "" } + + //- /c.rs + pub fn foo(a: u32, b: u32) {} + + //- /main.rs + mod a; + mod b; + mod c; + + fn main() { + let foo_test = fo<|>o(); + } + "#, + &["pub fn foo() -> &str", "pub fn foo() -> u32", "pub fn foo(a: u32, b: u32)"], + ); + } + + #[test] + fn hover_shows_fn_signature_with_type_params() { + check_hover_result( + r#" + //- /main.rs + pub fn foo<'a, T: AsRef>(b: &'a T) -> &'a str { } + + fn main() { + let foo_test = fo<|>o(); + } + "#, + &["pub fn foo<'a, T: AsRef>(b: &'a T) -> &'a str"], + ); } #[test] @@ -217,21 +349,21 @@ mod tests { ); let hover = analysis.hover(position).unwrap().unwrap(); // not the nicest way to show it currently - assert_eq!(hover.info, "Some(T) -> Option"); + assert_eq!(hover.info.first(), Some("Some(T) -> Option")); } #[test] fn hover_for_local_variable() { let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(hover.info, "i32"); + assert_eq!(hover.info.first(), Some("i32")); } #[test] fn hover_for_local_variable_pat() { let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(hover.info, "i32"); + assert_eq!(hover.info.first(), Some("i32")); } #[test] @@ -298,6 +430,6 @@ mod tests { ", ); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(hover.info, "Thing"); + assert_eq!(hover.info.first(), Some("Thing")); } } 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::{ navigation_target::NavigationTarget, references::ReferenceSearchResult, assists::{Assist, AssistId}, + hover::{HoverResult}, }; pub use ra_ide_api_light::{ Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit, @@ -328,7 +329,7 @@ impl Analysis { } /// Returns a short text describing element at position. - pub fn hover(&self, position: FilePosition) -> Cancelable>> { + pub fn hover(&self, position: FilePosition) -> Cancelable>> { self.with_db(|db| hover::hover(db, position)) } 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( let res = Hover { contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, - value: info.info, + value: info.info.to_markup(), }), range: Some(range), }; -- cgit v1.2.3