aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--docs/user/generated_config.adoc15
-rw-r--r--editors/code/package.json15
6 files changed, 132 insertions, 115 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
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 58cb46974..cc7fdd38f 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -215,6 +215,16 @@ In some editors (e.g. vscode) semantic tokens override other highlighting gramma
215By disabling semantic tokens for strings, other grammars can be used to highlight 215By disabling semantic tokens for strings, other grammars can be used to highlight
216their contents. 216their contents.
217-- 217--
218[[rust-analyzer.hover.documentation]]rust-analyzer.hover.documentation (default: `true`)::
219+
220--
221Whether to show documentation on hover.
222--
223[[rust-analyzer.hover.linksInHover]]rust-analyzer.hover.linksInHover (default: `true`)::
224+
225--
226Use markdown syntax for links in hover.
227--
218[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: 228[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
219+ 229+
220-- 230--
@@ -250,11 +260,6 @@ Whether to show `References` action. Only applies when
250Whether to show `Run` action. Only applies when 260Whether to show `Run` action. Only applies when
251`#rust-analyzer.hoverActions.enable#` is set. 261`#rust-analyzer.hoverActions.enable#` is set.
252-- 262--
253[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`)::
254+
255--
256Use markdown syntax for links in hover.
257--
258[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: 263[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
259+ 264+
260-- 265--
diff --git a/editors/code/package.json b/editors/code/package.json
index b20a39a95..666016ae4 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -655,6 +655,16 @@
655 "default": true, 655 "default": true,
656 "type": "boolean" 656 "type": "boolean"
657 }, 657 },
658 "rust-analyzer.hover.documentation": {
659 "markdownDescription": "Whether to show documentation on hover.",
660 "default": true,
661 "type": "boolean"
662 },
663 "rust-analyzer.hover.linksInHover": {
664 "markdownDescription": "Use markdown syntax for links in hover.",
665 "default": true,
666 "type": "boolean"
667 },
658 "rust-analyzer.hoverActions.debug": { 668 "rust-analyzer.hoverActions.debug": {
659 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", 669 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
660 "default": true, 670 "default": true,
@@ -685,11 +695,6 @@
685 "default": true, 695 "default": true,
686 "type": "boolean" 696 "type": "boolean"
687 }, 697 },
688 "rust-analyzer.hoverActions.linksInHover": {
689 "markdownDescription": "Use markdown syntax for links in hover.",
690 "default": true,
691 "type": "boolean"
692 },
693 "rust-analyzer.inlayHints.chainingHints": { 698 "rust-analyzer.inlayHints.chainingHints": {
694 "markdownDescription": "Whether to show inlay type hints for method chains.", 699 "markdownDescription": "Whether to show inlay type hints for method chains.",
695 "default": true, 700 "default": true,