Skip to content

Commit 6a585c6

Browse files
bors[bot]Veykril
andauthored
Merge #7795
7795: Show docs on hover for keywords and primitives r=matklad a=Veykril ![lAWFadkziX](https://user-images.githubusercontent.com/3757771/109369534-eeb4f500-789c-11eb-8f2b-2f9c4e129de3.gif) It's a bit annoying that this requires the `SyntaxNode` and `Semantics` to be pulled through `hover_for_definition` just so we can get the `std` crate but I couldn't think of a better way. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
2 parents 657ec36 + 8d30568 commit 6a585c6

File tree

4 files changed

+139
-19
lines changed

4 files changed

+139
-19
lines changed

crates/hir/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ mod has_source;
3333
pub use crate::{
3434
attrs::{HasAttrs, Namespace},
3535
code_model::{
36-
Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37-
ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
38-
GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
39-
Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
40-
Variant, VariantDef,
36+
Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
37+
CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
38+
FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
39+
Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
40+
TypeParam, Union, Variant, VariantDef,
4141
},
4242
has_source::HasSource,
4343
semantics::{PathResolution, Semantics, SemanticsScope},
@@ -47,7 +47,6 @@ pub use hir_def::{
4747
adt::StructKind,
4848
attr::{Attrs, Documentation},
4949
body::scope::ExprScopes,
50-
builtin_type::BuiltinType,
5150
find_path::PrefixKind,
5251
import_map,
5352
item_scope::ItemInNs,

crates/ide/src/hover.rs

Lines changed: 122 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use hir::{
55
use ide_db::{
66
base_db::SourceDatabase,
77
defs::{Definition, NameClass, NameRefClass},
8+
helpers::FamousDefs,
89
RootDatabase,
910
};
1011
use itertools::Itertools;
@@ -107,16 +108,14 @@ pub(crate) fn hover(
107108
}
108109
};
109110
if let Some(definition) = definition {
110-
if let Some(markup) = hover_for_definition(db, definition) {
111-
let markup = markup.as_str();
112-
let markup = if !markdown {
113-
remove_markdown(markup)
114-
} else if links_in_hover {
115-
rewrite_links(db, markup, &definition)
116-
} else {
117-
remove_links(markup)
118-
};
119-
res.markup = Markup::from(markup);
111+
let famous_defs = match &definition {
112+
Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
113+
Some(FamousDefs(&sema, sema.scope(&node).krate()))
114+
}
115+
_ => None,
116+
};
117+
if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
118+
res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
120119
if let Some(action) = show_implementations_action(db, definition) {
121120
res.actions.push(action);
122121
}
@@ -138,6 +137,9 @@ pub(crate) fn hover(
138137
// don't highlight the entire parent node on comment hover
139138
return None;
140139
}
140+
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
141+
return res;
142+
}
141143

142144
let node = token
143145
.ancestors()
@@ -272,6 +274,24 @@ fn hover_markup(
272274
}
273275
}
274276

277+
fn process_markup(
278+
db: &RootDatabase,
279+
def: Definition,
280+
markup: &Markup,
281+
links_in_hover: bool,
282+
markdown: bool,
283+
) -> Markup {
284+
let markup = markup.as_str();
285+
let markup = if !markdown {
286+
remove_markdown(markup)
287+
} else if links_in_hover {
288+
rewrite_links(db, markup, &def)
289+
} else {
290+
remove_links(markup)
291+
};
292+
Markup::from(markup)
293+
}
294+
275295
fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
276296
match def {
277297
Definition::Field(f) => Some(f.parent_def(db).name(db)),
@@ -304,7 +324,11 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
304324
def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
305325
}
306326

307-
fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
327+
fn hover_for_definition(
328+
db: &RootDatabase,
329+
def: Definition,
330+
famous_defs: Option<&FamousDefs>,
331+
) -> Option<Markup> {
308332
let mod_path = definition_mod_path(db, &def);
309333
return match def {
310334
Definition::Macro(it) => {
@@ -339,7 +363,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
339363
ModuleDef::Static(it) => from_def_source(db, it, mod_path),
340364
ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
341365
ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
342-
ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())),
366+
ModuleDef::BuiltinType(it) => famous_defs
367+
.and_then(|fd| hover_for_builtin(fd, it))
368+
.or_else(|| Some(Markup::fenced_block(&it.name()))),
343369
},
344370
Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
345371
Definition::SelfType(impl_def) => {
@@ -380,11 +406,52 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
380406
}
381407
}
382408

409+
fn hover_for_keyword(
410+
sema: &Semantics<RootDatabase>,
411+
links_in_hover: bool,
412+
markdown: bool,
413+
token: &SyntaxToken,
414+
) -> Option<RangeInfo<HoverResult>> {
415+
if !token.kind().is_keyword() {
416+
return None;
417+
}
418+
let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate());
419+
// std exposes {}_keyword modules with docstrings on the root to document keywords
420+
let keyword_mod = format!("{}_keyword", token.text());
421+
let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
422+
let docs = doc_owner.attrs(sema.db).docs()?;
423+
let markup = process_markup(
424+
sema.db,
425+
Definition::ModuleDef(doc_owner.into()),
426+
&hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
427+
links_in_hover,
428+
markdown,
429+
);
430+
Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
431+
}
432+
433+
fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
434+
// std exposes prim_{} modules with docstrings on the root to document the builtins
435+
let primitive_mod = format!("prim_{}", builtin.name());
436+
let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
437+
let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
438+
hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
439+
}
440+
441+
fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
442+
let db = famous_defs.0.db;
443+
let std_crate = famous_defs.std()?;
444+
let std_root_module = std_crate.root_module(db);
445+
std_root_module
446+
.children(db)
447+
.find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
448+
}
449+
383450
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
384451
return tokens.max_by_key(priority);
385452
fn priority(n: &SyntaxToken) -> usize {
386453
match n.kind() {
387-
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3,
454+
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
388455
T!['('] | T![')'] => 2,
389456
kind if kind.is_trivia() => 0,
390457
_ => 1,
@@ -3523,6 +3590,48 @@ use foo::bar::{self$0};
35233590
35243591
But this should appear
35253592
"#]],
3593+
)
3594+
}
3595+
3596+
#[test]
3597+
fn hover_keyword() {
3598+
let ra_fixture = r#"//- /main.rs crate:main deps:std
3599+
fn f() { retur$0n; }"#;
3600+
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3601+
check(
3602+
&fixture,
3603+
expect![[r#"
3604+
*return*
3605+
3606+
```rust
3607+
return
3608+
```
3609+
3610+
---
3611+
3612+
Docs for return_keyword
3613+
"#]],
3614+
);
3615+
}
3616+
3617+
#[test]
3618+
fn hover_builtin() {
3619+
let ra_fixture = r#"//- /main.rs crate:main deps:std
3620+
cosnt _: &str$0 = ""; }"#;
3621+
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3622+
check(
3623+
&fixture,
3624+
expect![[r#"
3625+
*str*
3626+
3627+
```rust
3628+
str
3629+
```
3630+
3631+
---
3632+
3633+
Docs for prim_str
3634+
"#]],
35263635
);
35273636
}
35283637
}

crates/ide_db/src/helpers.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
4141
impl FamousDefs<'_, '_> {
4242
pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
4343

44+
pub fn std(&self) -> Option<Crate> {
45+
self.find_crate("std")
46+
}
47+
4448
pub fn core(&self) -> Option<Crate> {
4549
self.find_crate("core")
4650
}

crates/ide_db/src/helpers/famous_defs_fixture.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,11 @@ pub mod prelude {
129129
}
130130
#[prelude_import]
131131
pub use prelude::*;
132+
//- /libstd.rs crate:std deps:core
133+
//! Signatures of traits, types and functions from the std lib for use in tests.
134+
135+
/// Docs for return_keyword
136+
mod return_keyword {}
137+
138+
/// Docs for prim_str
139+
mod prim_str {}

0 commit comments

Comments
 (0)