aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/hover.rs')
-rw-r--r--crates/ide/src/hover.rs135
1 files changed, 122 insertions, 13 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 20b799490..a9454cfa3 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -5,6 +5,7 @@ use hir::{
5use ide_db::{ 5use ide_db::{
6 base_db::SourceDatabase, 6 base_db::SourceDatabase,
7 defs::{Definition, NameClass, NameRefClass}, 7 defs::{Definition, NameClass, NameRefClass},
8 helpers::FamousDefs,
8 RootDatabase, 9 RootDatabase,
9}; 10};
10use itertools::Itertools; 11use itertools::Itertools;
@@ -107,16 +108,14 @@ pub(crate) fn hover(
107 } 108 }
108 }; 109 };
109 if let Some(definition) = definition { 110 if let Some(definition) = definition {
110 if let Some(markup) = hover_for_definition(db, definition) { 111 let famous_defs = match &definition {
111 let markup = markup.as_str(); 112 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
112 let markup = if !markdown { 113 Some(FamousDefs(&sema, sema.scope(&node).krate()))
113 remove_markdown(markup) 114 }
114 } else if links_in_hover { 115 _ => None,
115 rewrite_links(db, markup, &definition) 116 };
116 } else { 117 if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
117 remove_links(markup) 118 res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
118 };
119 res.markup = Markup::from(markup);
120 if let Some(action) = show_implementations_action(db, definition) { 119 if let Some(action) = show_implementations_action(db, definition) {
121 res.actions.push(action); 120 res.actions.push(action);
122 } 121 }
@@ -138,6 +137,9 @@ pub(crate) fn hover(
138 // don't highlight the entire parent node on comment hover 137 // don't highlight the entire parent node on comment hover
139 return None; 138 return None;
140 } 139 }
140 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
141 return res;
142 }
141 143
142 let node = token 144 let node = token
143 .ancestors() 145 .ancestors()
@@ -272,6 +274,24 @@ fn hover_markup(
272 } 274 }
273} 275}
274 276
277fn 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
275fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { 295fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
276 match def { 296 match def {
277 Definition::Field(f) => Some(f.parent_def(db).name(db)), 297 Definition::Field(f) => Some(f.parent_def(db).name(db)),
@@ -304,7 +324,11 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
304 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) 324 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
305} 325}
306 326
307fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { 327fn hover_for_definition(
328 db: &RootDatabase,
329 def: Definition,
330 famous_defs: Option<&FamousDefs>,
331) -> Option<Markup> {
308 let mod_path = definition_mod_path(db, &def); 332 let mod_path = definition_mod_path(db, &def);
309 return match def { 333 return match def {
310 Definition::Macro(it) => { 334 Definition::Macro(it) => {
@@ -339,7 +363,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
339 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 363 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
340 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 364 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
341 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 365 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()))),
343 }, 369 },
344 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
345 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
@@ -380,11 +406,52 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
380 } 406 }
381} 407}
382 408
409fn 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
433fn 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
441fn 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
383fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 450fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
384 return tokens.max_by_key(priority); 451 return tokens.max_by_key(priority);
385 fn priority(n: &SyntaxToken) -> usize { 452 fn priority(n: &SyntaxToken) -> usize {
386 match n.kind() { 453 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,
388 T!['('] | T![')'] => 2, 455 T!['('] | T![')'] => 2,
389 kind if kind.is_trivia() => 0, 456 kind if kind.is_trivia() => 0,
390 _ => 1, 457 _ => 1,
@@ -3523,6 +3590,48 @@ use foo::bar::{self$0};
3523 3590
3524 But this should appear 3591 But this should appear
3525 "#]], 3592 "#]],
3593 )
3594 }
3595
3596 #[test]
3597 fn hover_keyword() {
3598 let ra_fixture = r#"//- /main.rs crate:main deps:std
3599fn 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
3620cosnt _: &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 "#]],
3526 ); 3635 );
3527 } 3636 }
3528} 3637}