Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show docs on hover for keywords and primitives #7795

Merged
merged 1 commit into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ mod has_source;
pub use crate::{
attrs::{HasAttrs, Namespace},
code_model::{
Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
Variant, VariantDef,
Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
TypeParam, Union, Variant, VariantDef,
},
has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope},
Expand All @@ -47,7 +47,6 @@ pub use hir_def::{
adt::StructKind,
attr::{Attrs, Documentation},
body::scope::ExprScopes,
builtin_type::BuiltinType,
find_path::PrefixKind,
import_map,
item_scope::ItemInNs,
Expand Down
135 changes: 122 additions & 13 deletions crates/ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use hir::{
use ide_db::{
base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
helpers::FamousDefs,
RootDatabase,
};
use itertools::Itertools;
Expand Down Expand Up @@ -107,16 +108,14 @@ pub(crate) fn hover(
}
};
if let Some(definition) = definition {
if let Some(markup) = hover_for_definition(db, definition) {
let markup = markup.as_str();
let markup = if !markdown {
remove_markdown(markup)
} else if links_in_hover {
rewrite_links(db, markup, &definition)
} else {
remove_links(markup)
};
res.markup = Markup::from(markup);
let famous_defs = match &definition {
Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
Some(FamousDefs(&sema, sema.scope(&node).krate()))
}
_ => None,
};
if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
if let Some(action) = show_implementations_action(db, definition) {
res.actions.push(action);
}
Expand All @@ -138,6 +137,9 @@ pub(crate) fn hover(
// don't highlight the entire parent node on comment hover
return None;
}
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
return res;
}

let node = token
.ancestors()
Expand Down Expand Up @@ -272,6 +274,24 @@ fn hover_markup(
}
}

fn process_markup(
db: &RootDatabase,
def: Definition,
markup: &Markup,
links_in_hover: bool,
markdown: bool,
) -> Markup {
let markup = markup.as_str();
let markup = if !markdown {
remove_markdown(markup)
} else if links_in_hover {
rewrite_links(db, markup, &def)
} else {
remove_links(markup)
};
Markup::from(markup)
}
Comment on lines +277 to +293
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also feel that our docs pipeline is in the need of refactor. In particular, markdown removal really should be a methond on markap, called from an ide:

// in handlers.rs

if snap.config.caps.support_markdown() {
  markup.as_markdown()
} else {
  markup.as_plain_text()
}

bors r+

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #4326 and #6576


fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
match def {
Definition::Field(f) => Some(f.parent_def(db).name(db)),
Expand Down Expand Up @@ -304,7 +324,11 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
}

fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
fn hover_for_definition(
db: &RootDatabase,
def: Definition,
famous_defs: Option<&FamousDefs>,
) -> Option<Markup> {
let mod_path = definition_mod_path(db, &def);
return match def {
Definition::Macro(it) => {
Expand Down Expand Up @@ -339,7 +363,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
ModuleDef::Static(it) => from_def_source(db, it, mod_path),
ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())),
ModuleDef::BuiltinType(it) => famous_defs
.and_then(|fd| hover_for_builtin(fd, it))
.or_else(|| Some(Markup::fenced_block(&it.name()))),
},
Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
Definition::SelfType(impl_def) => {
Expand Down Expand Up @@ -380,11 +406,52 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
}
}

fn hover_for_keyword(
sema: &Semantics<RootDatabase>,
links_in_hover: bool,
markdown: bool,
token: &SyntaxToken,
) -> Option<RangeInfo<HoverResult>> {
if !token.kind().is_keyword() {
return None;
}
let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate());
// std exposes {}_keyword modules with docstrings on the root to document keywords
let keyword_mod = format!("{}_keyword", token.text());
let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
let docs = doc_owner.attrs(sema.db).docs()?;
let markup = process_markup(
sema.db,
Definition::ModuleDef(doc_owner.into()),
&hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
links_in_hover,
markdown,
);
Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
}

fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
// std exposes prim_{} modules with docstrings on the root to document the builtins
let primitive_mod = format!("prim_{}", builtin.name());
let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
}

fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
let db = famous_defs.0.db;
let std_crate = famous_defs.std()?;
let std_root_module = std_crate.root_module(db);
std_root_module
.children(db)
.find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
}

fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3,
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
Expand Down Expand Up @@ -3523,6 +3590,48 @@ use foo::bar::{self$0};

But this should appear
"#]],
)
}

#[test]
fn hover_keyword() {
let ra_fixture = r#"//- /main.rs crate:main deps:std
fn f() { retur$0n; }"#;
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
check(
&fixture,
expect![[r#"
*return*

```rust
return
```

---

Docs for return_keyword
"#]],
);
}

#[test]
fn hover_builtin() {
let ra_fixture = r#"//- /main.rs crate:main deps:std
cosnt _: &str$0 = ""; }"#;
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
check(
&fixture,
expect![[r#"
*str*

```rust
str
```

---

Docs for prim_str
"#]],
);
}
}
4 changes: 4 additions & 0 deletions crates/ide_db/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
impl FamousDefs<'_, '_> {
pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");

pub fn std(&self) -> Option<Crate> {
self.find_crate("std")
}

pub fn core(&self) -> Option<Crate> {
self.find_crate("core")
}
Expand Down
8 changes: 8 additions & 0 deletions crates/ide_db/src/helpers/famous_defs_fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,11 @@ pub mod prelude {
}
#[prelude_import]
pub use prelude::*;
//- /libstd.rs crate:std deps:core
//! Signatures of traits, types and functions from the std lib for use in tests.

/// Docs for return_keyword
mod return_keyword {}

/// Docs for prim_str
mod prim_str {}