From d688215f25b8da06b825da5942763c0faf2f7454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?= Date: Fri, 22 May 2020 20:11:24 +0200 Subject: Update tests to accept new hover formatting --- crates/ra_ide/src/hover.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index befa977c7..1f4f6b848 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -405,7 +405,7 @@ mod tests { }; } "#, - &["Foo\nfield_a: u32"], + &["Foo\n___\n\n```rust\nfield_a: u32"], ); // Hovering over the field in the definition @@ -422,7 +422,7 @@ mod tests { }; } "#, - &["Foo\nfield_a: u32"], + &["Foo\n___\n\n```rust\nfield_a: u32"], ); } @@ -475,7 +475,7 @@ fn main() { ", ); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); + assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome")); let (analysis, position) = single_file_with_position( " @@ -503,6 +503,9 @@ fn main() { "#, &[" Option +___ + +```rust None ``` @@ -524,6 +527,9 @@ The None variant "#, &[" Option +___ + +```rust Some ``` @@ -606,7 +612,10 @@ fn func(foo: i32) { if true { <|>foo; }; } ", ); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); + assert_eq!( + trim_markup_opt(hover.info.first()), + Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing") + ); } #[test] -- cgit v1.2.3 From a047f10839d64813213c70ed60c37c0a99f232b2 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Mon, 25 May 2020 23:18:45 -0400 Subject: Hover tooltip module name is monospace once again The line separator is moved below the function signature to split regions between the docs. This is very similar to how IntelliJ displays tooltips. Adding an additional separator between the module name and function signature currently has rendering issues. Fixes #4594 Alternative to #4615 --- crates/ra_ide/src/hover.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 1f4f6b848..3e721dcca 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -405,7 +405,7 @@ mod tests { }; } "#, - &["Foo\n___\n\n```rust\nfield_a: u32"], + &["Foo\n```\n\n```rust\nfield_a: u32"], ); // Hovering over the field in the definition @@ -422,7 +422,7 @@ mod tests { }; } "#, - &["Foo\n___\n\n```rust\nfield_a: u32"], + &["Foo\n```\n\n```rust\nfield_a: u32"], ); } @@ -475,7 +475,7 @@ fn main() { ", ); let hover = analysis.hover(position).unwrap().unwrap(); - assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome")); + assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome")); let (analysis, position) = single_file_with_position( " @@ -503,11 +503,12 @@ fn main() { "#, &[" Option -___ +``` ```rust None ``` +___ The None variant " @@ -527,11 +528,12 @@ The None variant "#, &[" Option -___ +``` ```rust Some ``` +___ The Some variant " @@ -614,7 +616,7 @@ fn func(foo: i32) { if true { <|>foo; }; } let hover = analysis.hover(position).unwrap().unwrap(); assert_eq!( trim_markup_opt(hover.info.first()), - Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing") + Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") ); } @@ -891,7 +893,7 @@ fn func(foo: i32) { if true { <|>foo; }; } fo<|>o(); } ", - &["fn foo()\n```\n\n<- `\u{3000}` here"], + &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], ); } -- cgit v1.2.3 From 1c6a2eb14a84c3a66972d1a6da429cca1aa8b40a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 11:29:19 +0200 Subject: Move the rest of the features to generated docs --- crates/ra_ide/src/hover.rs | 116 +++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 57 deletions(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 3e721dcca..d96cb5596 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,10 +1,10 @@ -//! Logic for computing info that is displayed when the user hovers over any -//! source code items (e.g. function call, struct field, variable symbol...) +use std::iter::once; use hir::{ Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, ModuleSource, Semantics, }; +use itertools::Itertools; use ra_db::SourceDatabase; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, @@ -21,8 +21,6 @@ use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, FilePosition, RangeInfo, }; -use itertools::Itertools; -use std::iter::once; /// Contains the results when hovering over an item #[derive(Debug, Default)] @@ -62,6 +60,63 @@ impl HoverResult { } } +// Feature: Hover +// +// Shows additional information, like type of an expression or documentation for definition when "focusing" code. +// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. +pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id).syntax().clone(); + let token = pick_best(file.token_at_offset(position.offset))?; + let token = sema.descend_into_macros(token); + + let mut res = HoverResult::new(); + + if let Some((node, name_kind)) = match_ast! { + match (token.parent()) { + ast::NameRef(name_ref) => { + classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) + }, + ast::Name(name) => { + classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) + }, + _ => None, + } + } { + let range = sema.original_range(&node).range; + res.extend(hover_text_from_name_kind(db, name_kind)); + + if !res.is_empty() { + return Some(RangeInfo::new(range, res)); + } + } + + let node = token + .ancestors() + .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; + + let ty = match_ast! { + match node { + ast::MacroCall(_it) => { + // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. + // (e.g expanding a builtin macro). So we give up here. + return None; + }, + ast::Expr(it) => { + sema.type_of_expr(&it) + }, + ast::Pat(it) => { + sema.type_of_pat(&it) + }, + _ => None, + } + }?; + + res.extend(Some(rust_code_markup(&ty.display(db)))); + let range = sema.original_range(&node).range; + Some(RangeInfo::new(range, res)) +} + fn hover_text( docs: Option, desc: Option, @@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option Option> { - let sema = Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let token = pick_best(file.token_at_offset(position.offset))?; - let token = sema.descend_into_macros(token); - - let mut res = HoverResult::new(); - - if let Some((node, name_kind)) = match_ast! { - match (token.parent()) { - ast::NameRef(name_ref) => { - classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) - }, - ast::Name(name) => { - classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) - }, - _ => None, - } - } { - let range = sema.original_range(&node).range; - res.extend(hover_text_from_name_kind(db, name_kind)); - - if !res.is_empty() { - return Some(RangeInfo::new(range, res)); - } - } - - let node = token - .ancestors() - .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; - - let ty = match_ast! { - match node { - ast::MacroCall(_it) => { - // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. - // (e.g expanding a builtin macro). So we give up here. - return None; - }, - ast::Expr(it) => { - sema.type_of_expr(&it) - }, - ast::Pat(it) => { - sema.type_of_pat(&it) - }, - _ => None, - } - }?; - - res.extend(Some(rust_code_markup(&ty.display(db)))); - let range = sema.original_range(&node).range; - Some(RangeInfo::new(range, res)) -} - fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { -- cgit v1.2.3 From 4c655c01f31ceffae4f8219f9706992e0e7f188a Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sat, 30 May 2020 14:21:06 -0400 Subject: Enable hover and autocomplete docs on macro generated items --- crates/ra_ide/src/hover.rs | 55 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index d96cb5596..e25a7dacf 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -169,13 +169,19 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option { let src = it.source(db); - hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) + let doc_comment_text = src.value.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&src.value); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, Some(macro_label(&src.value)), mod_path) } Definition::Field(it) => { let src = it.source(db); match src.value { FieldSource::Named(it) => { - hover_text(it.doc_comment_text(), it.short_label(), mod_path) + let doc_comment_text = it.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&it); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, it.short_label(), mod_path) } _ => None, } @@ -183,7 +189,10 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option match it { ModuleDef::Module(it) => match it.definition_source(db).value { ModuleSource::Module(it) => { - hover_text(it.doc_comment_text(), it.short_label(), mod_path) + let doc_comment_text = it.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&it); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, it.short_label(), mod_path) } _ => None, }, @@ -208,10 +217,46 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option(db: &RootDatabase, def: D, mod_path: Option) -> Option where D: HasSource, - A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, + A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner, { let src = def.source(db); - hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) + let doc_comment_text = src.value.doc_comment_text(); + let doc_attr_text = expand_doc_attrs(&src.value); + let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + hover_text(docs, src.value.short_label(), mod_path) + } +} + +fn merge_doc_comments_and_attrs( + doc_comment_text: Option, + doc_attr_text: Option, +) -> Option { + match (doc_comment_text, doc_attr_text) { + (Some(mut comment_text), Some(attr_text)) => { + comment_text.push_str("\n\n"); + comment_text.push_str(&attr_text); + Some(comment_text) + } + (Some(comment_text), None) => Some(comment_text), + (None, Some(attr_text)) => Some(attr_text), + (None, None) => None, + } +} + +fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { + let mut docs = String::new(); + for attr in owner.attrs() { + if let Some(("doc", value)) = + attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) + { + docs.push_str(value); + docs.push_str("\n\n"); + } + } + if docs.is_empty() { + None + } else { + Some(docs) } } -- cgit v1.2.3 From 5837acce532e0cd65a1c0cb8c03cc18a4c22f327 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 31 May 2020 11:33:48 -0400 Subject: Add basic hover and completion doc tests for macro generated items --- crates/ra_ide/src/hover.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index e25a7dacf..731fc3673 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -256,7 +256,7 @@ fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { if docs.is_empty() { None } else { - Some(docs) + Some(docs.trim_end_matches("\n\n").to_owned()) } } @@ -996,4 +996,106 @@ fn func(foo: i32) { if true { <|>foo; }; } &["mod my"], ); } + + #[test] + fn test_hover_struct_doc_comment() { + check_hover_result( + r#" + //- /lib.rs + /// bar docs + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs"], + ); + } + + #[test] + fn test_hover_struct_doc_attr() { + check_hover_result( + r#" + //- /lib.rs + #[doc = "bar docs"] + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs"], + ); + } + + #[test] + fn test_hover_struct_doc_attr_multiple_and_mixed() { + check_hover_result( + r#" + //- /lib.rs + /// bar docs 0 + #[doc = "bar docs 1"] + #[doc = "bar docs 2"] + struct Bar; + + fn foo() { + let bar = Ba<|>r; + } + "#, + &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"], + ); + } + + #[test] + fn test_hover_macro_generated_struct_fn_doc_comment() { + check_hover_result( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + /// Do the foo + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>o(); + } + "#, + &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"], + ); + } + + #[test] + fn test_hover_macro_generated_struct_fn_doc_attr() { + check_hover_result( + r#" + //- /lib.rs + macro_rules! bar { + () => { + struct Bar; + impl Bar { + #[doc = "Do the foo"] + fn foo(&self) {} + } + } + } + + bar!(); + + fn foo() { + let bar = Bar; + bar.fo<|>o(); + } + "#, + &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], + ); + } } -- cgit v1.2.3 From 85c4edb0afbc7cc855c434e5e7ec69aa469e0c4b Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Wed, 3 Jun 2020 06:14:56 -0400 Subject: Consolidate documentation expansion and merging Removes the duplicated `expand_doc_attrs` and `merge_doc_comments_and_attrs` functions from `ra_ide` and exposes the same functionality via `ra_hir::Documentation::from_ast`. --- crates/ra_ide/src/hover.rs | 60 ++++++---------------------------------------- 1 file changed, 7 insertions(+), 53 deletions(-) (limited to 'crates/ra_ide/src/hover.rs') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 731fc3673..9636cd0d6 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,8 +1,8 @@ use std::iter::once; use hir::{ - Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, - ModuleSource, Semantics, + Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, + ModuleDef, ModuleSource, Semantics, }; use itertools::Itertools; use ra_db::SourceDatabase; @@ -10,12 +10,7 @@ use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, RootDatabase, }; -use ra_syntax::{ - ast::{self, DocCommentsOwner}, - match_ast, AstNode, - SyntaxKind::*, - SyntaxToken, TokenAtOffset, -}; +use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, @@ -169,18 +164,14 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option { let src = it.source(db); - let doc_comment_text = src.value.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&src.value); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&src.value).map(Into::into); hover_text(docs, Some(macro_label(&src.value)), mod_path) } Definition::Field(it) => { let src = it.source(db); match src.value { FieldSource::Named(it) => { - let doc_comment_text = it.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&it); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&it).map(Into::into); hover_text(docs, it.short_label(), mod_path) } _ => None, @@ -189,9 +180,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option match it { ModuleDef::Module(it) => match it.definition_source(db).value { ModuleSource::Module(it) => { - let doc_comment_text = it.doc_comment_text(); - let doc_attr_text = expand_doc_attrs(&it); - let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); + let docs = Documentation::from_ast(&it).map(Into::into); hover_text(docs, it.short_label(), mod_path) } _ => None, @@ -220,46 +209,11 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option, - doc_attr_text: Option, -) -> Option { - match (doc_comment_text, doc_attr_text) { - (Some(mut comment_text), Some(attr_text)) => { - comment_text.push_str("\n\n"); - comment_text.push_str(&attr_text); - Some(comment_text) - } - (Some(comment_text), None) => Some(comment_text), - (None, Some(attr_text)) => Some(attr_text), - (None, None) => None, - } -} - -fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option { - let mut docs = String::new(); - for attr in owner.attrs() { - if let Some(("doc", value)) = - attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) - { - docs.push_str(value); - docs.push_str("\n\n"); - } - } - if docs.is_empty() { - None - } else { - Some(docs.trim_end_matches("\n\n").to_owned()) - } -} - fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { -- cgit v1.2.3