aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-06-21 15:15:49 +0100
committerGitHub <[email protected]>2021-06-21 15:15:49 +0100
commit25bf451c8426a68ecfdc3a1d1be1db9702dd41d8 (patch)
tree1d9d6c49faca0c36178d2bf5a6e20de0f88df094 /crates
parent5567b8a63214ce9bf2d4fdd6b4a8bd9c2c3eded7 (diff)
parent43098d99ae9ee3d102ce54510d7885bae58e5ac7 (diff)
Merge #9264
9264: feat: Make documentation on hover configurable r=Veykril a=Veykril This also implements deprecation support for config options as this renames `hoverActions_linksInHover` to `hover_linksInHover`. Fixes #9232 Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ide/src/hover.rs185
-rw-r--r--crates/ide/src/lib.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs16
4 files changed, 112 insertions, 105 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2e1359eef..0c1da8774 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);
@@ -179,7 +181,8 @@ pub(crate) fn hover(
179 } 181 }
180 } 182 }
181 183
182 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { 184 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, documentation, &token)
185 {
183 return res; 186 return res;
184 } 187 }
185 188
@@ -261,8 +264,10 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
261 } 264 }
262 265
263 let adt = match def { 266 let adt = match def {
264 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action), 267 Definition::ModuleDef(hir::ModuleDef::Trait(it)) => {
265 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), 268 return it.try_to_nav(db).map(to_action)
269 }
270 Definition::ModuleDef(hir::ModuleDef::Adt(it)) => Some(it),
266 Definition::SelfType(it) => it.self_ty(db).as_adt(), 271 Definition::SelfType(it) => it.self_ty(db).as_adt(),
267 _ => None, 272 _ => None,
268 }?; 273 }?;
@@ -271,25 +276,27 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
271 276
272fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 277fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
273 match def { 278 match def {
274 Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| { 279 Definition::ModuleDef(hir::ModuleDef::Function(it)) => {
275 HoverAction::Reference(FilePosition { 280 it.try_to_nav(db).map(|nav_target| {
276 file_id: nav_target.file_id, 281 HoverAction::Reference(FilePosition {
277 offset: nav_target.focus_or_full_range().start(), 282 file_id: nav_target.file_id,
283 offset: nav_target.focus_or_full_range().start(),
284 })
278 }) 285 })
279 }), 286 }
280 _ => None, 287 _ => None,
281 } 288 }
282} 289}
283 290
284fn runnable_action( 291fn runnable_action(
285 sema: &Semantics<RootDatabase>, 292 sema: &hir::Semantics<RootDatabase>,
286 def: Definition, 293 def: Definition,
287 file_id: FileId, 294 file_id: FileId,
288) -> Option<HoverAction> { 295) -> Option<HoverAction> {
289 match def { 296 match def {
290 Definition::ModuleDef(it) => match it { 297 Definition::ModuleDef(it) => match it {
291 ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), 298 hir::ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
292 ModuleDef::Function(func) => { 299 hir::ModuleDef::Function(func) => {
293 let src = func.source(sema.db)?; 300 let src = func.source(sema.db)?;
294 if src.file_id != file_id.into() { 301 if src.file_id != file_id.into() {
295 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); 302 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
@@ -306,19 +313,19 @@ fn runnable_action(
306} 313}
307 314
308fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 315fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
309 let mut targets: Vec<ModuleDef> = Vec::new(); 316 let mut targets: Vec<hir::ModuleDef> = Vec::new();
310 let mut push_new_def = |item: ModuleDef| { 317 let mut push_new_def = |item: hir::ModuleDef| {
311 if !targets.contains(&item) { 318 if !targets.contains(&item) {
312 targets.push(item); 319 targets.push(item);
313 } 320 }
314 }; 321 };
315 322
316 if let Definition::GenericParam(GenericParam::TypeParam(it)) = def { 323 if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
317 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); 324 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
318 } else { 325 } else {
319 let ty = match def { 326 let ty = match def {
320 Definition::Local(it) => it.ty(db), 327 Definition::Local(it) => it.ty(db),
321 Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db), 328 Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
322 _ => return None, 329 _ => return None,
323 }; 330 };
324 331
@@ -348,29 +355,20 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
348 Some(HoverAction::GoToType(targets)) 355 Some(HoverAction::GoToType(targets))
349} 356}
350 357
351fn hover_markup( 358fn hover_markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
352 docs: Option<String>, 359 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 360
367 if let Some(doc) = docs { 361 if let Some(mod_path) = mod_path {
368 format_to!(buf, "\n___\n\n{}", doc); 362 if !mod_path.is_empty() {
369 } 363 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
370 Some(buf.into())
371 } 364 }
372 None => docs.map(Markup::from),
373 } 365 }
366 format_to!(buf, "```rust\n{}\n```", desc);
367
368 if let Some(doc) = docs {
369 format_to!(buf, "\n___\n\n{}", doc);
370 }
371 Some(buf.into())
374} 372}
375 373
376fn process_markup( 374fn process_markup(
@@ -396,11 +394,11 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
396 Definition::Field(f) => Some(f.parent_def(db).name(db)), 394 Definition::Field(f) => Some(f.parent_def(db).name(db)),
397 Definition::Local(l) => l.parent(db).name(db), 395 Definition::Local(l) => l.parent(db).name(db),
398 Definition::ModuleDef(md) => match md { 396 Definition::ModuleDef(md) => match md {
399 ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { 397 hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
400 AssocItemContainer::Trait(t) => Some(t.name(db)), 398 hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
401 AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), 399 hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
402 }, 400 },
403 ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)), 401 hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
404 _ => None, 402 _ => None,
405 }, 403 },
406 _ => None, 404 _ => None,
@@ -408,7 +406,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
408 .map(|name| name.to_string()) 406 .map(|name| name.to_string())
409} 407}
410 408
411fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 409fn render_path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
412 let crate_name = 410 let crate_name =
413 db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string()); 411 db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
414 let module_path = module 412 let module_path = module
@@ -420,6 +418,9 @@ fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) ->
420} 418}
421 419
422fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 420fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
421 if let Definition::GenericParam(_) = def {
422 return None;
423 }
423 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) 424 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
424} 425}
425 426
@@ -427,60 +428,53 @@ fn hover_for_definition(
427 db: &RootDatabase, 428 db: &RootDatabase,
428 def: Definition, 429 def: Definition,
429 famous_defs: Option<&FamousDefs>, 430 famous_defs: Option<&FamousDefs>,
431 documentation: bool,
430) -> Option<Markup> { 432) -> Option<Markup> {
431 let mod_path = definition_mod_path(db, &def); 433 let mod_path = definition_mod_path(db, &def);
432 return match def { 434 let (label, docs) = match def {
433 Definition::Macro(it) => match &it.source(db)?.value { 435 Definition::Macro(it) => match &it.source(db)?.value {
434 Either::Left(mac) => { 436 Either::Left(mac) => {
435 let label = macro_label(mac); 437 let label = macro_label(mac);
436 from_def_source_labeled(db, it, Some(label), mod_path) 438 (label, it.attrs(db).docs())
437 } 439 }
438 Either::Right(_) => { 440 Either::Right(_) => {
439 // FIXME 441 // FIXME
440 None 442 return None;
441 } 443 }
442 }, 444 },
443 Definition::Field(def) => from_hir_fmt(db, def, mod_path), 445 Definition::Field(def) => label_and_docs(db, def),
444 Definition::ModuleDef(it) => match it { 446 Definition::ModuleDef(it) => match it {
445 ModuleDef::Module(it) => from_hir_fmt(db, it, mod_path), 447 hir::ModuleDef::Module(it) => label_and_docs(db, it),
446 ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path), 448 hir::ModuleDef::Function(it) => label_and_docs(db, it),
447 ModuleDef::Adt(it) => from_hir_fmt(db, it, mod_path), 449 hir::ModuleDef::Adt(it) => label_and_docs(db, it),
448 ModuleDef::Variant(it) => from_hir_fmt(db, it, mod_path), 450 hir::ModuleDef::Variant(it) => label_and_docs(db, it),
449 ModuleDef::Const(it) => from_hir_fmt(db, it, mod_path), 451 hir::ModuleDef::Const(it) => label_and_docs(db, it),
450 ModuleDef::Static(it) => from_hir_fmt(db, it, mod_path), 452 hir::ModuleDef::Static(it) => label_and_docs(db, it),
451 ModuleDef::Trait(it) => from_hir_fmt(db, it, mod_path), 453 hir::ModuleDef::Trait(it) => label_and_docs(db, it),
452 ModuleDef::TypeAlias(it) => from_hir_fmt(db, it, mod_path), 454 hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it),
453 ModuleDef::BuiltinType(it) => famous_defs 455 hir::ModuleDef::BuiltinType(it) => {
454 .and_then(|fd| hover_for_builtin(fd, it)) 456 return famous_defs
455 .or_else(|| Some(Markup::fenced_block(&it.name()))), 457 .and_then(|fd| hover_for_builtin(fd, it))
458 .or_else(|| Some(Markup::fenced_block(&it.name())))
459 }
456 }, 460 },
457 Definition::Local(it) => hover_for_local(it, db), 461 Definition::Local(it) => return hover_for_local(it, db),
458 Definition::SelfType(impl_def) => { 462 Definition::SelfType(impl_def) => {
459 impl_def.self_ty(db).as_adt().and_then(|adt| from_hir_fmt(db, adt, mod_path)) 463 impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
460 } 464 }
461 Definition::GenericParam(it) => from_hir_fmt(db, it, None), 465 Definition::GenericParam(it) => label_and_docs(db, it),
462 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 466 Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
463 }; 467 };
464 468
465 fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 469 return hover_markup(docs.filter(|_| documentation).map(Into::into), label, mod_path);
470
471 fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
466 where 472 where
467 D: HasAttrs + HirDisplay, 473 D: HasAttrs + HirDisplay,
468 { 474 {
469 let label = def.display(db).to_string(); 475 let label = def.display(db).to_string();
470 from_def_source_labeled(db, def, Some(label), mod_path) 476 let docs = def.attrs(db).docs();
471 } 477 (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 } 478 }
485} 479}
486 480
@@ -504,16 +498,17 @@ fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
504 } 498 }
505 Either::Right(_) => format!("{}self: {}", is_mut, ty), 499 Either::Right(_) => format!("{}self: {}", is_mut, ty),
506 }; 500 };
507 hover_markup(None, Some(desc), None) 501 hover_markup(None, desc, None)
508} 502}
509 503
510fn hover_for_keyword( 504fn hover_for_keyword(
511 sema: &Semantics<RootDatabase>, 505 sema: &hir::Semantics<RootDatabase>,
512 links_in_hover: bool, 506 links_in_hover: bool,
513 markdown: bool, 507 markdown: bool,
508 documentation: bool,
514 token: &SyntaxToken, 509 token: &SyntaxToken,
515) -> Option<RangeInfo<HoverResult>> { 510) -> Option<RangeInfo<HoverResult>> {
516 if !token.kind().is_keyword() { 511 if !token.kind().is_keyword() || !documentation {
517 return None; 512 return None;
518 } 513 }
519 let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate()); 514 let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
@@ -524,7 +519,7 @@ fn hover_for_keyword(
524 let markup = process_markup( 519 let markup = process_markup(
525 sema.db, 520 sema.db,
526 Definition::ModuleDef(doc_owner.into()), 521 Definition::ModuleDef(doc_owner.into()),
527 &hover_markup(Some(docs.into()), Some(token.text().into()), None)?, 522 &hover_markup(Some(docs.into()), token.text().into(), None)?,
528 links_in_hover, 523 links_in_hover,
529 markdown, 524 markdown,
530 ); 525 );
@@ -536,7 +531,7 @@ fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Opt
536 let primitive_mod = format!("prim_{}", builtin.name()); 531 let primitive_mod = format!("prim_{}", builtin.name());
537 let doc_owner = find_std_module(famous_defs, &primitive_mod)?; 532 let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
538 let docs = doc_owner.attrs(famous_defs.0.db).docs()?; 533 let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
539 hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None) 534 hover_markup(Some(docs.into()), builtin.name().to_string(), None)
540} 535}
541 536
542fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { 537fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
@@ -570,12 +565,12 @@ mod tests {
570 565
571 fn check_hover_no_result(ra_fixture: &str) { 566 fn check_hover_no_result(ra_fixture: &str) {
572 let (analysis, position) = fixture::position(ra_fixture); 567 let (analysis, position) = fixture::position(ra_fixture);
573 assert!(analysis.hover(position, true, true).unwrap().is_none()); 568 assert!(analysis.hover(position, true, true, true).unwrap().is_none());
574 } 569 }
575 570
576 fn check(ra_fixture: &str, expect: Expect) { 571 fn check(ra_fixture: &str, expect: Expect) {
577 let (analysis, position) = fixture::position(ra_fixture); 572 let (analysis, position) = fixture::position(ra_fixture);
578 let hover = analysis.hover(position, true, true).unwrap().unwrap(); 573 let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
579 574
580 let content = analysis.db.file_text(position.file_id); 575 let content = analysis.db.file_text(position.file_id);
581 let hovered_element = &content[hover.range]; 576 let hovered_element = &content[hover.range];
@@ -586,7 +581,7 @@ mod tests {
586 581
587 fn check_hover_no_links(ra_fixture: &str, expect: Expect) { 582 fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
588 let (analysis, position) = fixture::position(ra_fixture); 583 let (analysis, position) = fixture::position(ra_fixture);
589 let hover = analysis.hover(position, false, true).unwrap().unwrap(); 584 let hover = analysis.hover(position, false, true, true).unwrap().unwrap();
590 585
591 let content = analysis.db.file_text(position.file_id); 586 let content = analysis.db.file_text(position.file_id);
592 let hovered_element = &content[hover.range]; 587 let hovered_element = &content[hover.range];
@@ -597,7 +592,7 @@ mod tests {
597 592
598 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { 593 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
599 let (analysis, position) = fixture::position(ra_fixture); 594 let (analysis, position) = fixture::position(ra_fixture);
600 let hover = analysis.hover(position, true, false).unwrap().unwrap(); 595 let hover = analysis.hover(position, true, true, false).unwrap().unwrap();
601 596
602 let content = analysis.db.file_text(position.file_id); 597 let content = analysis.db.file_text(position.file_id);
603 let hovered_element = &content[hover.range]; 598 let hovered_element = &content[hover.range];
@@ -608,7 +603,7 @@ mod tests {
608 603
609 fn check_actions(ra_fixture: &str, expect: Expect) { 604 fn check_actions(ra_fixture: &str, expect: Expect) {
610 let (analysis, position) = fixture::position(ra_fixture); 605 let (analysis, position) = fixture::position(ra_fixture);
611 let hover = analysis.hover(position, true, true).unwrap().unwrap(); 606 let hover = analysis.hover(position, true, true, true).unwrap().unwrap();
612 expect.assert_debug_eq(&hover.info.actions) 607 expect.assert_debug_eq(&hover.info.actions)
613 } 608 }
614 609
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 4bd073cc3..3798f32cc 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -408,9 +408,10 @@ impl Analysis {
408 &self, 408 &self,
409 position: FilePosition, 409 position: FilePosition,
410 links_in_hover: bool, 410 links_in_hover: bool,
411 documentation: bool,
411 markdown: bool, 412 markdown: bool,
412 ) -> Cancellable<Option<RangeInfo<HoverResult>>> { 413 ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
413 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) 414 self.with_db(|db| hover::hover(db, position, links_in_hover, documentation, markdown))
414 } 415 }
415 416
416 /// Return URL(s) for the documentation of the symbol under the cursor. 417 /// 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 7e0276c10..3aeca8839 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -146,6 +146,12 @@ config_data! {
146 /// their contents. 146 /// their contents.
147 highlighting_strings: bool = "true", 147 highlighting_strings: bool = "true",
148 148
149 /// Whether to show documentation on hover.
150 hover_documentation: bool = "true",
151 /// Use markdown syntax for links in hover.
152 hover_linksInHover |
153 hoverActions_linksInHover: bool = "true",
154
149 /// Whether to show `Debug` action. Only applies when 155 /// Whether to show `Debug` action. Only applies when
150 /// `#rust-analyzer.hoverActions.enable#` is set. 156 /// `#rust-analyzer.hoverActions.enable#` is set.
151 hoverActions_debug: bool = "true", 157 hoverActions_debug: bool = "true",
@@ -163,8 +169,6 @@ config_data! {
163 /// Whether to show `Run` action. Only applies when 169 /// Whether to show `Run` action. Only applies when
164 /// `#rust-analyzer.hoverActions.enable#` is set. 170 /// `#rust-analyzer.hoverActions.enable#` is set.
165 hoverActions_run: bool = "true", 171 hoverActions_run: bool = "true",
166 /// Use markdown syntax for links in hover.
167 hoverActions_linksInHover: bool = "true",
168 172
169 /// Whether to show inlay type hints for method chains. 173 /// Whether to show inlay type hints for method chains.
170 inlayHints_chainingHints: bool = "true", 174 inlayHints_chainingHints: bool = "true",
@@ -734,7 +738,7 @@ impl Config {
734 run: self.data.hoverActions_enable && self.data.hoverActions_run, 738 run: self.data.hoverActions_enable && self.data.hoverActions_run,
735 debug: self.data.hoverActions_enable && self.data.hoverActions_debug, 739 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
736 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, 740 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
737 links_in_hover: self.data.hoverActions_linksInHover, 741 links_in_hover: self.data.hover_linksInHover,
738 markdown: try_or!( 742 markdown: try_or!(
739 self.caps 743 self.caps
740 .text_document 744 .text_document
@@ -747,6 +751,7 @@ impl Config {
747 &[] 751 &[]
748 ) 752 )
749 .contains(&MarkupKind::Markdown), 753 .contains(&MarkupKind::Markdown),
754 documentation: self.data.hover_documentation,
750 } 755 }
751 } 756 }
752 757
@@ -856,6 +861,7 @@ macro_rules! _config_data {
856 $({ 861 $({
857 let field = stringify!($field); 862 let field = stringify!($field);
858 let ty = stringify!($ty); 863 let ty = stringify!($ty);
864
859 (field, ty, &[$($doc),*], $default) 865 (field, ty, &[$($doc),*], $default)
860 },)* 866 },)*
861 ]) 867 ])
@@ -867,6 +873,7 @@ macro_rules! _config_data {
867 $({ 873 $({
868 let field = stringify!($field); 874 let field = stringify!($field);
869 let ty = stringify!($ty); 875 let ty = stringify!($ty);
876
870 (field, ty, &[$($doc),*], $default) 877 (field, ty, &[$($doc),*], $default)
871 },)* 878 },)*
872 ]) 879 ])
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