aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/hover.rs179
-rw-r--r--crates/ide/src/lib.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs10
-rw-r--r--crates/rust-analyzer/src/handlers.rs16
-rw-r--r--docs/user/generated_config.adoc15
-rw-r--r--editors/code/package.json15
6 files changed, 125 insertions, 113 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c08516805..23f2b48b4 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,8 +1,5 @@
1use either::Either; 1use either::Either;
2use hir::{ 2use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay};
3 AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module,
4 ModuleDef, Semantics,
5};
6use ide_db::{ 3use ide_db::{
7 base_db::SourceDatabase, 4 base_db::SourceDatabase,
8 defs::{Definition, NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
@@ -40,6 +37,7 @@ pub struct HoverConfig {
40 pub goto_type_def: bool, 37 pub goto_type_def: bool,
41 pub links_in_hover: bool, 38 pub links_in_hover: bool,
42 pub markdown: bool, 39 pub markdown: bool,
40 pub documentation: bool,
43} 41}
44 42
45impl HoverConfig { 43impl HoverConfig {
@@ -51,14 +49,15 @@ impl HoverConfig {
51 goto_type_def: false, 49 goto_type_def: false,
52 links_in_hover: true, 50 links_in_hover: true,
53 markdown: true, 51 markdown: true,
52 documentation: true,
54 }; 53 };
55 54
56 pub fn any(&self) -> bool { 55 pub fn any_actions(&self) -> bool {
57 self.implementations || self.references || self.runnable() || self.goto_type_def 56 self.implementations || self.references || self.runnable() || self.goto_type_def
58 } 57 }
59 58
60 pub fn none(&self) -> bool { 59 pub fn no_actions(&self) -> bool {
61 !self.any() 60 !self.any_actions()
62 } 61 }
63 62
64 pub fn runnable(&self) -> bool { 63 pub fn runnable(&self) -> bool {
@@ -97,9 +96,10 @@ pub(crate) fn hover(
97 db: &RootDatabase, 96 db: &RootDatabase,
98 position: FilePosition, 97 position: FilePosition,
99 links_in_hover: bool, 98 links_in_hover: bool,
99 documentation: bool,
100 markdown: bool, 100 markdown: bool,
101) -> Option<RangeInfo<HoverResult>> { 101) -> Option<RangeInfo<HoverResult>> {
102 let sema = Semantics::new(db); 102 let sema = hir::Semantics::new(db);
103 let file = sema.parse(position.file_id).syntax().clone(); 103 let file = sema.parse(position.file_id).syntax().clone();
104 let token = pick_best(file.token_at_offset(position.offset))?; 104 let token = pick_best(file.token_at_offset(position.offset))?;
105 let token = sema.descend_into_macros(token); 105 let token = sema.descend_into_macros(token);
@@ -131,7 +131,7 @@ pub(crate) fn hover(
131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
132 let (idl_range, link, ns) = 132 let (idl_range, link, ns) =
133 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { 133 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| {
134 let InFile { file_id, value: range } = doc_mapping.map(range)?; 134 let hir::InFile { file_id, value: range } = doc_mapping.map(range)?;
135 if file_id == position.file_id.into() && range.contains(position.offset) { 135 if file_id == position.file_id.into() && range.contains(position.offset) {
136 Some((range, link, ns)) 136 Some((range, link, ns))
137 } else { 137 } else {
@@ -151,12 +151,14 @@ pub(crate) fn hover(
151 151
152 if let Some(definition) = definition { 152 if let Some(definition) = definition {
153 let famous_defs = match &definition { 153 let famous_defs = match &definition {
154 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => { 154 Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
155 Some(FamousDefs(&sema, sema.scope(&node).krate())) 155 Some(FamousDefs(&sema, sema.scope(&node).krate()))
156 } 156 }
157 _ => None, 157 _ => None,
158 }; 158 };
159 if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) { 159 if let Some(markup) =
160 hover_for_definition(db, definition, famous_defs.as_ref(), documentation)
161 {
160 res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown); 162 res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
161 if let Some(action) = show_implementations_action(db, definition) { 163 if let Some(action) = show_implementations_action(db, definition) {
162 res.actions.push(action); 164 res.actions.push(action);
@@ -261,8 +263,10 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
261 } 263 }
262 264
263 let adt = match def { 265 let adt = match def {
264 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action), 266 Definition::ModuleDef(hir::ModuleDef::Trait(it)) => {
265 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), 267 return it.try_to_nav(db).map(to_action)
268 }
269 Definition::ModuleDef(hir::ModuleDef::Adt(it)) => Some(it),
266 Definition::SelfType(it) => it.self_ty(db).as_adt(), 270 Definition::SelfType(it) => it.self_ty(db).as_adt(),
267 _ => None, 271 _ => None,
268 }?; 272 }?;
@@ -271,25 +275,27 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
271 275
272fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 276fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
273 match def { 277 match def {
274 Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| { 278 Definition::ModuleDef(hir::ModuleDef::Function(it)) => {
275 HoverAction::Reference(FilePosition { 279 it.try_to_nav(db).map(|nav_target| {
276 file_id: nav_target.file_id, 280 HoverAction::Reference(FilePosition {
277 offset: nav_target.focus_or_full_range().start(), 281 file_id: nav_target.file_id,
282 offset: nav_target.focus_or_full_range().start(),
283 })
278 }) 284 })
279 }), 285 }
280 _ => None, 286 _ => None,
281 } 287 }
282} 288}
283 289
284fn runnable_action( 290fn runnable_action(
285 sema: &Semantics<RootDatabase>, 291 sema: &hir::Semantics<RootDatabase>,
286 def: Definition, 292 def: Definition,
287 file_id: FileId, 293 file_id: FileId,
288) -> Option<HoverAction> { 294) -> Option<HoverAction> {
289 match def { 295 match def {
290 Definition::ModuleDef(it) => match it { 296 Definition::ModuleDef(it) => match it {
291 ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), 297 hir::ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
292 ModuleDef::Function(func) => { 298 hir::ModuleDef::Function(func) => {
293 let src = func.source(sema.db)?; 299 let src = func.source(sema.db)?;
294 if src.file_id != file_id.into() { 300 if src.file_id != file_id.into() {
295 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); 301 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
@@ -306,19 +312,19 @@ fn runnable_action(
306} 312}
307 313
308fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 314fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
309 let mut targets: Vec<ModuleDef> = Vec::new(); 315 let mut targets: Vec<hir::ModuleDef> = Vec::new();
310 let mut push_new_def = |item: ModuleDef| { 316 let mut push_new_def = |item: hir::ModuleDef| {
311 if !targets.contains(&item) { 317 if !targets.contains(&item) {
312 targets.push(item); 318 targets.push(item);
313 } 319 }
314 }; 320 };
315 321
316 if let Definition::GenericParam(GenericParam::TypeParam(it)) = def { 322 if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
317 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); 323 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
318 } else { 324 } else {
319 let ty = match def { 325 let ty = match def {
320 Definition::Local(it) => it.ty(db), 326 Definition::Local(it) => it.ty(db),
321 Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db), 327 Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
322 _ => return None, 328 _ => return None,
323 }; 329 };
324 330
@@ -348,29 +354,20 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
348 Some(HoverAction::GoToType(targets)) 354 Some(HoverAction::GoToType(targets))
349} 355}
350 356
351fn hover_markup( 357fn hover_markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
352 docs: Option<String>, 358 let mut buf = String::new();
353 desc: Option<String>,
354 mod_path: Option<String>,
355) -> Option<Markup> {
356 match desc {
357 Some(desc) => {
358 let mut buf = String::new();
359
360 if let Some(mod_path) = mod_path {
361 if !mod_path.is_empty() {
362 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
363 }
364 }
365 format_to!(buf, "```rust\n{}\n```", desc);
366 359
367 if let Some(doc) = docs { 360 if let Some(mod_path) = mod_path {
368 format_to!(buf, "\n___\n\n{}", doc); 361 if !mod_path.is_empty() {
369 } 362 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
370 Some(buf.into())
371 } 363 }
372 None => docs.map(Markup::from),
373 } 364 }
365 format_to!(buf, "```rust\n{}\n```", desc);
366
367 if let Some(doc) = docs {
368 format_to!(buf, "\n___\n\n{}", doc);
369 }
370 Some(buf.into())
374} 371}
375 372
376fn process_markup( 373fn process_markup(
@@ -396,11 +393,11 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
396 Definition::Field(f) => Some(f.parent_def(db).name(db)), 393 Definition::Field(f) => Some(f.parent_def(db).name(db)),
397 Definition::Local(l) => l.parent(db).name(db), 394 Definition::Local(l) => l.parent(db).name(db),
398 Definition::ModuleDef(md) => match md { 395 Definition::ModuleDef(md) => match md {
399 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { 396 hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
400 AssocItemContainer::Trait(t) => Some(t.name(db)), 397 hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
401 AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), 398 hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
402 }, 399 },
403 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), 400 hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
404 _ => None, 401 _ => None,
405 }, 402 },
406 _ => None, 403 _ => None,
@@ -408,7 +405,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
408 .map(|name| name.to_string()) 405 .map(|name| name.to_string())
409} 406}
410 407
411fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 408fn render_path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
412 let crate_name = 409 let crate_name =
413 db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string()); 410 db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
414 let module_path = module 411 let module_path = module
@@ -420,6 +417,9 @@ fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) ->
420} 417}
421 418
422fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 419fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
420 if let Definition::GenericParam(_) = def {
421 return None;
422 }
423 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) 423 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
424} 424}
425 425
@@ -427,60 +427,53 @@ fn hover_for_definition(
427 db: &RootDatabase, 427 db: &RootDatabase,
428 def: Definition, 428 def: Definition,
429 famous_defs: Option<&FamousDefs>, 429 famous_defs: Option<&FamousDefs>,
430 documentation: bool,
430) -> Option<Markup> { 431) -> Option<Markup> {
431 let mod_path = definition_mod_path(db, &def); 432 let mod_path = definition_mod_path(db, &def);
432 return match def { 433 let (label, docs) = match def {
433 Definition::Macro(it) => match &it.source(db)?.value { 434 Definition::Macro(it) => match &it.source(db)?.value {
434 Either::Left(mac) => { 435 Either::Left(mac) => {
435 let label = macro_label(mac); 436 let label = macro_label(mac);
436 from_def_source_labeled(db, it, Some(label), mod_path) 437 (label, it.attrs(db).docs())
437 } 438 }
438 Either::Right(_) => { 439 Either::Right(_) => {
439 // FIXME 440 // FIXME
440 None 441 return None;
441 } 442 }
442 }, 443 },
443 Definition::Field(def) => from_hir_fmt(db, def, mod_path), 444 Definition::Field(def) => label_and_docs(db, def),
444 Definition::ModuleDef(it) => match it { 445 Definition::ModuleDef(it) => match it {
445 ModuleDef::Module(it) => from_hir_fmt(db, it, mod_path), 446 hir::ModuleDef::Module(it) => label_and_docs(db, it),
446 ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path), 447 hir::ModuleDef::Function(it) => label_and_docs(db, it),
447 ModuleDef::Adt(it) => from_hir_fmt(db, it, mod_path), 448 hir::ModuleDef::Adt(it) => label_and_docs(db, it),
448 ModuleDef::Variant(it) => from_hir_fmt(db, it, mod_path), 449 hir::ModuleDef::Variant(it) => label_and_docs(db, it),
449 ModuleDef::Const(it) => from_hir_fmt(db, it, mod_path), 450 hir::ModuleDef::Const(it) => label_and_docs(db, it),
450 ModuleDef::Static(it) => from_hir_fmt(db, it, mod_path), 451 hir::ModuleDef::Static(it) => label_and_docs(db, it),
451 ModuleDef::Trait(it) => from_hir_fmt(db, it, mod_path), 452 hir::ModuleDef::Trait(it) => label_and_docs(db, it),
452 ModuleDef::TypeAlias(it) => from_hir_fmt(db, it, mod_path), 453 hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it),
453 ModuleDef::BuiltinType(it) => famous_defs 454 hir::ModuleDef::BuiltinType(it) => {
454 .and_then(|fd| hover_for_builtin(fd, it)) 455 return famous_defs
455 .or_else(|| Some(Markup::fenced_block(&it.name()))), 456 .and_then(|fd| hover_for_builtin(fd, it))
457 .or_else(|| Some(Markup::fenced_block(&it.name())))
458 }
456 }, 459 },
457 Definition::Local(it) => hover_for_local(it, db), 460 Definition::Local(it) => return hover_for_local(it, db),
458 Definition::SelfType(impl_def) => { 461 Definition::SelfType(impl_def) => {
459 impl_def.self_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path)) 462 impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
460 } 463 }
461 Definition::GenericParam(it) => from_hir_fmt(db, it, None), 464 Definition::GenericParam(it) => label_and_docs(db, it),
462 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 465 Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
463 }; 466 };
464 467
465 fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 468 return hover_markup(docs.filter(|_| documentation).map(Into::into), label, mod_path);
469
470 fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
466 where 471 where
467 D: HasAttrs + HirDisplay, 472 D: HasAttrs + HirDisplay,
468 { 473 {
469 let label = def.display(db).to_string(); 474 let label = def.display(db).to_string();
470 from_def_source_labeled(db, def, Some(label), mod_path) 475 let docs = def.attrs(db).docs();
471 } 476 (label, docs)
472
473 fn from_def_source_labeled<D>(
474 db: &RootDatabase,
475 def: D,
476 short_label: Option<String>,
477 mod_path: Option<String>,
478 ) -> Option<Markup>
479 where
480 D: HasAttrs,
481 {
482 let docs = def.attrs(db).docs().map(Into::into);
483 hover_markup(docs, short_label, mod_path)
484 } 477 }
485} 478}
486 479
@@ -504,11 +497,11 @@ fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
504 } 497 }
505 Either::Right(_) => format!("{}self: {}", is_mut, ty), 498 Either::Right(_) => format!("{}self: {}", is_mut, ty),
506 }; 499 };
507 hover_markup(None, Some(desc), None) 500 hover_markup(None, desc, None)
508} 501}
509 502
510fn hover_for_keyword( 503fn hover_for_keyword(
511 sema: &Semantics<RootDatabase>, 504 sema: &hir::Semantics<RootDatabase>,
512 links_in_hover: bool, 505 links_in_hover: bool,
513 markdown: bool, 506 markdown: bool,
514 token: &SyntaxToken, 507 token: &SyntaxToken,
@@ -524,7 +517,7 @@ fn hover_for_keyword(
524 let markup = process_markup( 517 let markup = process_markup(
525 sema.db, 518 sema.db,
526 Definition::ModuleDef(doc_owner.into()), 519 Definition::ModuleDef(doc_owner.into()),
527 &hover_markup(Some(docs.into()), Some(token.text().into()), None)?, 520 &hover_markup(Some(docs.into()), token.text().into(), None)?,
528 links_in_hover, 521 links_in_hover,
529 markdown, 522 markdown,
530 ); 523 );
@@ -536,7 +529,7 @@ fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Opt
536 let primitive_mod = format!("prim_{}", builtin.name()); 529 let primitive_mod = format!("prim_{}", builtin.name());
537 let doc_owner = find_std_module(famous_defs, &primitive_mod)?; 530 let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
538 let docs = doc_owner.attrs(famous_defs.0.db).docs()?; 531 let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
539 hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None) 532 hover_markup(Some(docs.into()), builtin.name().to_string(), None)
540} 533}
541 534
542fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { 535fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
@@ -572,12 +565,12 @@ mod tests {
572 565
573 fn check_hover_no_result(ra_fixture: &str) { 566 fn check_hover_no_result(ra_fixture: &str) {
574 let (analysis, position) = fixture::position(ra_fixture); 567 let (analysis, position) = fixture::position(ra_fixture);
575 assert!(analysis.hover(position, true, true).unwrap().is_none()); 568 assert!(analysis.hover(position, true, true, true).unwrap().is_none());
576 } 569 }
577 570
578 fn check(ra_fixture: &str, expect: Expect) { 571 fn check(ra_fixture: &str, expect: Expect) {
579 let (analysis, position) = fixture::position(ra_fixture); 572 let (analysis, position) = fixture::position(ra_fixture);
580 let hover = analysis.hover(position, true, true).unwrap().unwrap(); 573 let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
581 574
582 let content = analysis.db.file_text(position.file_id); 575 let content = analysis.db.file_text(position.file_id);
583 let hovered_element = &content[hover.range]; 576 let hovered_element = &content[hover.range];
@@ -588,7 +581,7 @@ mod tests {
588 581
589 fn check_hover_no_links(ra_fixture: &str, expect: Expect) { 582 fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
590 let (analysis, position) = fixture::position(ra_fixture); 583 let (analysis, position) = fixture::position(ra_fixture);
591 let hover = analysis.hover(position, false, true).unwrap().unwrap(); 584 let hover = analysis.hover(position, false, true, true).unwrap().unwrap();
592 585
593 let content = analysis.db.file_text(position.file_id); 586 let content = analysis.db.file_text(position.file_id);
594 let hovered_element = &content[hover.range]; 587 let hovered_element = &content[hover.range];
@@ -599,7 +592,7 @@ mod tests {
599 592
600 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { 593 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
601 let (analysis, position) = fixture::position(ra_fixture); 594 let (analysis, position) = fixture::position(ra_fixture);
602 let hover = analysis.hover(position, true, false).unwrap().unwrap(); 595 let hover = analysis.hover(position, true, true, false).unwrap().unwrap();
603 596
604 let content = analysis.db.file_text(position.file_id); 597 let content = analysis.db.file_text(position.file_id);
605 let hovered_element = &content[hover.range]; 598 let hovered_element = &content[hover.range];
@@ -610,7 +603,7 @@ mod tests {
610 603
611 fn check_actions(ra_fixture: &str, expect: Expect) { 604 fn check_actions(ra_fixture: &str, expect: Expect) {
612 let (analysis, position) = fixture::position(ra_fixture); 605 let (analysis, position) = fixture::position(ra_fixture);
613 let hover = analysis.hover(position, true, true).unwrap().unwrap(); 606 let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
614 expect.assert_debug_eq(&hover.info.actions) 607 expect.assert_debug_eq(&hover.info.actions)
615 } 608 }
616 609
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 0511efae3..8d0270319 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -407,9 +407,10 @@ impl Analysis {
407 &self, 407 &self,
408 position: FilePosition, 408 position: FilePosition,
409 links_in_hover: bool, 409 links_in_hover: bool,
410 documentation: bool,
410 markdown: bool, 411 markdown: bool,
411 ) -> Cancellable<Option<RangeInfo<HoverResult>>> { 412 ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
412 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) 413 self.with_db(|db| hover::hover(db, position, links_in_hover, documentation, markdown))
413 } 414 }
414 415
415 /// Return URL(s) for the documentation of the symbol under the cursor. 416 /// Return URL(s) for the documentation of the symbol under the cursor.
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3b20d741a..5d3deb232 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -141,6 +141,11 @@ config_data! {
141 /// their contents. 141 /// their contents.
142 highlighting_strings: bool = "true", 142 highlighting_strings: bool = "true",
143 143
144 /// Whether to show documentation on hover.
145 hover_documentation: bool = "true",
146 /// Use markdown syntax for links in hover.
147 hover_linksInHover: bool = "true",
148
144 /// Whether to show `Debug` action. Only applies when 149 /// Whether to show `Debug` action. Only applies when
145 /// `#rust-analyzer.hoverActions.enable#` is set. 150 /// `#rust-analyzer.hoverActions.enable#` is set.
146 hoverActions_debug: bool = "true", 151 hoverActions_debug: bool = "true",
@@ -158,8 +163,6 @@ config_data! {
158 /// Whether to show `Run` action. Only applies when 163 /// Whether to show `Run` action. Only applies when
159 /// `#rust-analyzer.hoverActions.enable#` is set. 164 /// `#rust-analyzer.hoverActions.enable#` is set.
160 hoverActions_run: bool = "true", 165 hoverActions_run: bool = "true",
161 /// Use markdown syntax for links in hover.
162 hoverActions_linksInHover: bool = "true",
163 166
164 /// Whether to show inlay type hints for method chains. 167 /// Whether to show inlay type hints for method chains.
165 inlayHints_chainingHints: bool = "true", 168 inlayHints_chainingHints: bool = "true",
@@ -726,7 +729,7 @@ impl Config {
726 run: self.data.hoverActions_enable && self.data.hoverActions_run, 729 run: self.data.hoverActions_enable && self.data.hoverActions_run,
727 debug: self.data.hoverActions_enable && self.data.hoverActions_debug, 730 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
728 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, 731 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
729 links_in_hover: self.data.hoverActions_linksInHover, 732 links_in_hover: self.data.hover_linksInHover,
730 markdown: try_or!( 733 markdown: try_or!(
731 self.caps 734 self.caps
732 .text_document 735 .text_document
@@ -739,6 +742,7 @@ impl Config {
739 &[] 742 &[]
740 ) 743 )
741 .contains(&MarkupKind::Markdown), 744 .contains(&MarkupKind::Markdown),
745 documentation: self.data.hover_documentation,
742 } 746 }
743 } 747 }
744 748
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index ccf66294f..eff1e6c93 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -862,11 +862,15 @@ pub(crate) fn handle_hover(
862 let _p = profile::span("handle_hover"); 862 let _p = profile::span("handle_hover");
863 let position = from_proto::file_position(&snap, params.text_document_position_params)?; 863 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
864 let hover_config = snap.config.hover(); 864 let hover_config = snap.config.hover();
865 let info = 865 let info = match snap.analysis.hover(
866 match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? { 866 position,
867 None => return Ok(None), 867 hover_config.links_in_hover,
868 Some(info) => info, 868 hover_config.documentation,
869 }; 869 hover_config.markdown,
870 )? {
871 None => return Ok(None),
872 Some(info) => info,
873 };
870 let line_index = snap.file_line_index(position.file_id)?; 874 let line_index = snap.file_line_index(position.file_id)?;
871 let range = to_proto::range(&line_index, info.range); 875 let range = to_proto::range(&line_index, info.range);
872 let hover = lsp_ext::Hover { 876 let hover = lsp_ext::Hover {
@@ -1587,7 +1591,7 @@ fn prepare_hover_actions(
1587 snap: &GlobalStateSnapshot, 1591 snap: &GlobalStateSnapshot,
1588 actions: &[HoverAction], 1592 actions: &[HoverAction],
1589) -> Vec<lsp_ext::CommandLinkGroup> { 1593) -> Vec<lsp_ext::CommandLinkGroup> {
1590 if snap.config.hover().none() || !snap.config.hover_actions() { 1594 if snap.config.hover().no_actions() || !snap.config.hover_actions() {
1591 return Vec::new(); 1595 return Vec::new();
1592 } 1596 }
1593 1597
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 34a91486b..4105d784f 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -205,6 +205,16 @@ In some editors (e.g. vscode) semantic tokens override other highlighting gramma
205By disabling semantic tokens for strings, other grammars can be used to highlight 205By disabling semantic tokens for strings, other grammars can be used to highlight
206their contents. 206their contents.
207-- 207--
208[[rust-analyzer.hover.documentation]]rust-analyzer.hover.documentation (default: `true`)::
209+
210--
211Whether to show documentation on hover.
212--
213[[rust-analyzer.hover.linksInHover]]rust-analyzer.hover.linksInHover (default: `true`)::
214+
215--
216Use markdown syntax for links in hover.
217--
208[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: 218[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
209+ 219+
210-- 220--
@@ -240,11 +250,6 @@ Whether to show `References` action. Only applies when
240Whether to show `Run` action. Only applies when 250Whether to show `Run` action. Only applies when
241`#rust-analyzer.hoverActions.enable#` is set. 251`#rust-analyzer.hoverActions.enable#` is set.
242-- 252--
243[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`)::
244+
245--
246Use markdown syntax for links in hover.
247--
248[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: 253[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
249+ 254+
250-- 255--
diff --git a/editors/code/package.json b/editors/code/package.json
index 0f3ed48a0..43a5cc2b5 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -640,6 +640,16 @@
640 "default": true, 640 "default": true,
641 "type": "boolean" 641 "type": "boolean"
642 }, 642 },
643 "rust-analyzer.hover.documentation": {
644 "markdownDescription": "Whether to show documentation on hover.",
645 "default": true,
646 "type": "boolean"
647 },
648 "rust-analyzer.hover.linksInHover": {
649 "markdownDescription": "Use markdown syntax for links in hover.",
650 "default": true,
651 "type": "boolean"
652 },
643 "rust-analyzer.hoverActions.debug": { 653 "rust-analyzer.hoverActions.debug": {
644 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", 654 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
645 "default": true, 655 "default": true,
@@ -670,11 +680,6 @@
670 "default": true, 680 "default": true,
671 "type": "boolean" 681 "type": "boolean"
672 }, 682 },
673 "rust-analyzer.hoverActions.linksInHover": {
674 "markdownDescription": "Use markdown syntax for links in hover.",
675 "default": true,
676 "type": "boolean"
677 },
678 "rust-analyzer.inlayHints.chainingHints": { 683 "rust-analyzer.inlayHints.chainingHints": {
679 "markdownDescription": "Whether to show inlay type hints for method chains.", 684 "markdownDescription": "Whether to show inlay type hints for method chains.",
680 "default": true, 685 "default": true,