Skip to content

Commit 7d3fe83

Browse files
committed
Show docs on hover for keywords and primitives
1 parent 2a4076c commit 7d3fe83

File tree

4 files changed

+141
-20
lines changed

4 files changed

+141
-20
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: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ 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;
1112
use stdx::format_to;
12-
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
13+
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TokenAtOffset, T};
1314
use test_utils::mark;
1415

1516
use crate::{
@@ -107,16 +108,8 @@ 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+
if let Some(markup) = hover_for_definition(&sema, definition, &node) {
112+
res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
120113
if let Some(action) = show_implementations_action(db, definition) {
121114
res.actions.push(action);
122115
}
@@ -138,6 +131,9 @@ pub(crate) fn hover(
138131
// don't highlight the entire parent node on comment hover
139132
return None;
140133
}
134+
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
135+
return res;
136+
}
141137

142138
let node = token
143139
.ancestors()
@@ -272,6 +268,24 @@ fn hover_markup(
272268
}
273269
}
274270

271+
fn process_markup(
272+
db: &RootDatabase,
273+
def: Definition,
274+
markup: &Markup,
275+
links_in_hover: bool,
276+
markdown: bool,
277+
) -> Markup {
278+
let markup = markup.as_str();
279+
let markup = if !markdown {
280+
remove_markdown(markup)
281+
} else if links_in_hover {
282+
rewrite_links(db, markup, &def)
283+
} else {
284+
remove_links(markup)
285+
};
286+
Markup::from(markup)
287+
}
288+
275289
fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
276290
match def {
277291
Definition::Field(f) => Some(f.parent_def(db).name(db)),
@@ -304,7 +318,12 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
304318
def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
305319
}
306320

307-
fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
321+
fn hover_for_definition(
322+
sema: &Semantics<RootDatabase>,
323+
def: Definition,
324+
node: &SyntaxNode,
325+
) -> Option<Markup> {
326+
let db = sema.db;
308327
let mod_path = definition_mod_path(db, &def);
309328
return match def {
310329
Definition::Macro(it) => {
@@ -339,7 +358,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
339358
ModuleDef::Static(it) => from_def_source(db, it, mod_path),
340359
ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
341360
ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
342-
ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())),
361+
ModuleDef::BuiltinType(it) => {
362+
hover_for_builtin(sema, it, node).or_else(|| Some(Markup::fenced_block(&it.name())))
363+
}
343364
},
344365
Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
345366
Definition::SelfType(impl_def) => {
@@ -380,11 +401,58 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
380401
}
381402
}
382403

404+
fn hover_for_keyword(
405+
sema: &Semantics<RootDatabase>,
406+
links_in_hover: bool,
407+
markdown: bool,
408+
token: &SyntaxToken,
409+
) -> Option<RangeInfo<HoverResult>> {
410+
if !token.kind().is_keyword() {
411+
return None;
412+
}
413+
// std exposes {}_keyword modules with docstrings on the root to document keywords
414+
let keyword_mod = format!("{}_keyword", token.text());
415+
let doc_owner = find_std_module(sema, &token.parent(), &keyword_mod)?;
416+
let docs = doc_owner.attrs(sema.db).docs()?;
417+
let markup = process_markup(
418+
sema.db,
419+
Definition::ModuleDef(doc_owner.into()),
420+
&hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
421+
links_in_hover,
422+
markdown,
423+
);
424+
Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
425+
}
426+
427+
fn hover_for_builtin(
428+
sema: &Semantics<RootDatabase>,
429+
builtin: hir::BuiltinType,
430+
node: &SyntaxNode,
431+
) -> Option<Markup> {
432+
// std exposes prim_{} modules with docstrings on the root to document the builtins
433+
let primitive_mod = format!("prim_{}", builtin.name());
434+
let doc_owner = find_std_module(sema, node, &primitive_mod)?;
435+
let docs = doc_owner.attrs(sema.db).docs()?;
436+
hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
437+
}
438+
439+
fn find_std_module(
440+
sema: &Semantics<RootDatabase>,
441+
node: &SyntaxNode,
442+
name: &str,
443+
) -> Option<hir::Module> {
444+
let std_crate = FamousDefs(sema, sema.scope(node).krate()).std()?;
445+
let std_root_module = std_crate.root_module(sema.db);
446+
std_root_module
447+
.children(sema.db)
448+
.find(|module| module.name(sema.db).map_or(false, |module| module.to_string() == name))
449+
}
450+
383451
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
384452
return tokens.max_by_key(priority);
385453
fn priority(n: &SyntaxToken) -> usize {
386454
match n.kind() {
387-
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3,
455+
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
388456
T!['('] | T![')'] => 2,
389457
kind if kind.is_trivia() => 0,
390458
_ => 1,
@@ -3496,4 +3564,46 @@ mod foo$0;
34963564
"#]],
34973565
);
34983566
}
3567+
3568+
#[test]
3569+
fn hover_keyword() {
3570+
let ra_fixture = r#"//- /main.rs crate:main deps:std
3571+
fn f() { retur$0n; }"#;
3572+
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3573+
check(
3574+
&fixture,
3575+
expect![[r#"
3576+
*return*
3577+
3578+
```rust
3579+
return
3580+
```
3581+
3582+
---
3583+
3584+
Docs for return_keyword
3585+
"#]],
3586+
);
3587+
}
3588+
3589+
#[test]
3590+
fn hover_builtin() {
3591+
let ra_fixture = r#"//- /main.rs crate:main deps:std
3592+
cosnt _: &str$0 = ""; }"#;
3593+
let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
3594+
check(
3595+
&fixture,
3596+
expect![[r#"
3597+
*str*
3598+
3599+
```rust
3600+
str
3601+
```
3602+
3603+
---
3604+
3605+
Docs for prim_str
3606+
"#]],
3607+
);
3608+
}
34993609
}

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)