aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/cfg/src/lib.rs17
-rw-r--r--crates/ide/src/hover.rs185
-rw-r--r--crates/ide/src/lib.rs3
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs26
-rw-r--r--crates/project_model/src/cargo_workspace.rs17
-rw-r--r--crates/project_model/src/lib.rs2
-rw-r--r--crates/project_model/src/workspace.rs124
-rw-r--r--crates/rust-analyzer/src/config.rs17
-rw-r--r--crates/rust-analyzer/src/handlers.rs16
9 files changed, 247 insertions, 160 deletions
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 03b8dd767..916d39a0b 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -1,4 +1,4 @@
1//! cfg defines conditional compiling options, `cfg` attibute parser and evaluator 1//! cfg defines conditional compiling options, `cfg` attribute parser and evaluator
2 2
3mod cfg_expr; 3mod cfg_expr;
4mod dnf; 4mod dnf;
@@ -52,6 +52,7 @@ impl CfgOptions {
52 } 52 }
53} 53}
54 54
55#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct CfgDiff { 56pub struct CfgDiff {
56 // Invariants: No duplicates, no atom that's both in `enable` and `disable`. 57 // Invariants: No duplicates, no atom that's both in `enable` and `disable`.
57 enable: Vec<CfgAtom>, 58 enable: Vec<CfgAtom>,
@@ -59,6 +60,20 @@ pub struct CfgDiff {
59} 60}
60 61
61impl CfgDiff { 62impl CfgDiff {
63 /// Create a new CfgDiff. Will return None if the same item appears more than once in the set
64 /// of both.
65 pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> {
66 let mut occupied = FxHashSet::default();
67 for item in enable.iter().chain(disable.iter()) {
68 if !occupied.insert(item) {
69 // was present
70 return None;
71 }
72 }
73
74 Some(CfgDiff { enable, disable })
75 }
76
62 /// Returns the total number of atoms changed by this diff. 77 /// Returns the total number of atoms changed by this diff.
63 pub fn len(&self) -> usize { 78 pub fn len(&self) -> usize {
64 self.enable.len() + self.disable.len() 79 self.enable.len() + self.disable.len()
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/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index ac7f0959b..870d4f665 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -109,10 +109,15 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
109 109
110 let new_indent = IndentLevel::from_node(&insert_after); 110 let new_indent = IndentLevel::from_node(&insert_after);
111 let old_indent = fun.body.indent_level(); 111 let old_indent = fun.body.indent_level();
112 let body_contains_await = body_contains_await(&fun.body);
112 113
113 builder.replace(target_range, format_replacement(ctx, &fun, old_indent)); 114 builder.replace(
115 target_range,
116 format_replacement(ctx, &fun, old_indent, body_contains_await),
117 );
114 118
115 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); 119 let fn_def =
120 format_function(ctx, module, &fun, old_indent, new_indent, body_contains_await);
116 let insert_offset = insert_after.text_range().end(); 121 let insert_offset = insert_after.text_range().end();
117 match ctx.config.snippet_cap { 122 match ctx.config.snippet_cap {
118 Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def), 123 Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def),
@@ -954,7 +959,12 @@ fn scope_for_fn_insertion_node(node: &SyntaxNode, anchor: Anchor) -> Option<Synt
954 last_ancestor 959 last_ancestor
955} 960}
956 961
957fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) -> String { 962fn format_replacement(
963 ctx: &AssistContext,
964 fun: &Function,
965 indent: IndentLevel,
966 body_contains_await: bool,
967) -> String {
958 let ret_ty = fun.return_type(ctx); 968 let ret_ty = fun.return_type(ctx);
959 969
960 let args = fun.params.iter().map(|param| param.to_arg(ctx)); 970 let args = fun.params.iter().map(|param| param.to_arg(ctx));
@@ -994,6 +1004,9 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
994 } 1004 }
995 } 1005 }
996 format_to!(buf, "{}", expr); 1006 format_to!(buf, "{}", expr);
1007 if body_contains_await {
1008 buf.push_str(".await");
1009 }
997 if fun.ret_ty.is_unit() 1010 if fun.ret_ty.is_unit()
998 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) 1011 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like())
999 { 1012 {
@@ -1122,12 +1135,13 @@ fn format_function(
1122 fun: &Function, 1135 fun: &Function,
1123 old_indent: IndentLevel, 1136 old_indent: IndentLevel,
1124 new_indent: IndentLevel, 1137 new_indent: IndentLevel,
1138 body_contains_await: bool,
1125) -> String { 1139) -> String {
1126 let mut fn_def = String::new(); 1140 let mut fn_def = String::new();
1127 let params = make_param_list(ctx, module, fun); 1141 let params = make_param_list(ctx, module, fun);
1128 let ret_ty = make_ret_ty(ctx, module, fun); 1142 let ret_ty = make_ret_ty(ctx, module, fun);
1129 let body = make_body(ctx, old_indent, new_indent, fun); 1143 let body = make_body(ctx, old_indent, new_indent, fun);
1130 let async_kw = if body_contains_await(&fun.body) { "async " } else { "" }; 1144 let async_kw = if body_contains_await { "async " } else { "" };
1131 match ctx.config.snippet_cap { 1145 match ctx.config.snippet_cap {
1132 Some(_) => format_to!(fn_def, "\n\n{}{}fn $0{}{}", new_indent, async_kw, fun.name, params), 1146 Some(_) => format_to!(fn_def, "\n\n{}{}fn $0{}{}", new_indent, async_kw, fun.name, params),
1133 None => format_to!(fn_def, "\n\n{}{}fn {}{}", new_indent, async_kw, fun.name, params), 1147 None => format_to!(fn_def, "\n\n{}{}fn {}{}", new_indent, async_kw, fun.name, params),
@@ -3681,7 +3695,7 @@ async fn some_function() {
3681"#, 3695"#,
3682 r#" 3696 r#"
3683fn main() { 3697fn main() {
3684 fun_name(); 3698 fun_name().await;
3685} 3699}
3686 3700
3687async fn $0fun_name() { 3701async fn $0fun_name() {
@@ -3710,7 +3724,7 @@ async fn some_function() {
3710"#, 3724"#,
3711 r#" 3725 r#"
3712fn main() { 3726fn main() {
3713 fun_name(); 3727 fun_name().await;
3714} 3728}
3715 3729
3716async fn $0fun_name() { 3730async fn $0fun_name() {
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index ac079f83e..0935ea967 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,5 +1,6 @@
1//! See [`CargoWorkspace`]. 1//! See [`CargoWorkspace`].
2 2
3use std::iter;
3use std::path::PathBuf; 4use std::path::PathBuf;
4use std::{convert::TryInto, ops, process::Command, sync::Arc}; 5use std::{convert::TryInto, ops, process::Command, sync::Arc};
5 6
@@ -12,6 +13,7 @@ use rustc_hash::FxHashMap;
12use serde::Deserialize; 13use serde::Deserialize;
13use serde_json::from_value; 14use serde_json::from_value;
14 15
16use crate::CfgOverrides;
15use crate::{build_data::BuildDataConfig, utf8_stdout}; 17use crate::{build_data::BuildDataConfig, utf8_stdout};
16 18
17/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo 19/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
@@ -76,6 +78,21 @@ pub struct CargoConfig {
76 78
77 /// rustc private crate source 79 /// rustc private crate source
78 pub rustc_source: Option<RustcSource>, 80 pub rustc_source: Option<RustcSource>,
81
82 /// crates to disable `#[cfg(test)]` on
83 pub unset_test_crates: Vec<String>,
84}
85
86impl CargoConfig {
87 pub fn cfg_overrides(&self) -> CfgOverrides {
88 self.unset_test_crates
89 .iter()
90 .cloned()
91 .zip(iter::repeat_with(|| {
92 cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap()
93 }))
94 .collect()
95 }
79} 96}
80 97
81pub type Package = Idx<PackageData>; 98pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 8c6cf94c2..1d408dff2 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -41,7 +41,7 @@ pub use crate::{
41 }, 41 },
42 project_json::{ProjectJson, ProjectJsonData}, 42 project_json::{ProjectJson, ProjectJsonData},
43 sysroot::Sysroot, 43 sysroot::Sysroot,
44 workspace::{PackageRoot, ProjectWorkspace}, 44 workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
45}; 45};
46 46
47pub use proc_macro_api::ProcMacroClient; 47pub use proc_macro_api::ProcMacroClient;
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index ef0f3c9e4..d8217f714 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
7use anyhow::{format_err, Context, Result}; 7use anyhow::{format_err, Context, Result};
8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9use cargo_workspace::DepKind; 9use cargo_workspace::DepKind;
10use cfg::CfgOptions; 10use cfg::{CfgDiff, CfgOptions};
11use paths::{AbsPath, AbsPathBuf}; 11use paths::{AbsPath, AbsPathBuf};
12use proc_macro_api::ProcMacroClient; 12use proc_macro_api::ProcMacroClient;
13use rustc_hash::{FxHashMap, FxHashSet}; 13use rustc_hash::{FxHashMap, FxHashSet};
@@ -22,6 +22,8 @@ use crate::{
22 Sysroot, TargetKind, 22 Sysroot, TargetKind,
23}; 23};
24 24
25pub type CfgOverrides = FxHashMap<String, CfgDiff>;
26
25/// `PackageRoot` describes a package root folder. 27/// `PackageRoot` describes a package root folder.
26/// Which may be an external dependency, or a member of 28/// Which may be an external dependency, or a member of
27/// the current workspace. 29/// the current workspace.
@@ -46,6 +48,7 @@ pub enum ProjectWorkspace {
46 /// FIXME: make this a per-crate map, as, eg, build.rs might have a 48 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
47 /// different target. 49 /// different target.
48 rustc_cfg: Vec<CfgFlag>, 50 rustc_cfg: Vec<CfgFlag>,
51 cfg_overrides: CfgOverrides,
49 }, 52 },
50 /// Project workspace was manually specified using a `rust-project.json` file. 53 /// Project workspace was manually specified using a `rust-project.json` file.
51 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, 54 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
@@ -67,7 +70,7 @@ impl fmt::Debug for ProjectWorkspace {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 // Make sure this isn't too verbose. 71 // Make sure this isn't too verbose.
69 match self { 72 match self {
70 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f 73 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => f
71 .debug_struct("Cargo") 74 .debug_struct("Cargo")
72 .field("root", &cargo.workspace_root().file_name()) 75 .field("root", &cargo.workspace_root().file_name())
73 .field("n_packages", &cargo.packages().len()) 76 .field("n_packages", &cargo.packages().len())
@@ -77,6 +80,7 @@ impl fmt::Debug for ProjectWorkspace {
77 &rustc.as_ref().map_or(0, |rc| rc.packages().len()), 80 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
78 ) 81 )
79 .field("n_rustc_cfg", &rustc_cfg.len()) 82 .field("n_rustc_cfg", &rustc_cfg.len())
83 .field("n_cfg_overrides", &cfg_overrides.len())
80 .finish(), 84 .finish(),
81 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { 85 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
82 let mut debug_struct = f.debug_struct("Json"); 86 let mut debug_struct = f.debug_struct("Json");
@@ -164,7 +168,9 @@ impl ProjectWorkspace {
164 }; 168 };
165 169
166 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); 170 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
167 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } 171
172 let cfg_overrides = config.cfg_overrides();
173 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides }
168 } 174 }
169 }; 175 };
170 176
@@ -213,43 +219,45 @@ impl ProjectWorkspace {
213 }) 219 })
214 })) 220 }))
215 .collect::<Vec<_>>(), 221 .collect::<Vec<_>>(),
216 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo 222 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _, cfg_overrides: _ } => {
217 .packages() 223 cargo
218 .map(|pkg| { 224 .packages()
219 let is_member = cargo[pkg].is_member; 225 .map(|pkg| {
220 let pkg_root = cargo[pkg].root().to_path_buf(); 226 let is_member = cargo[pkg].is_member;
221 227 let pkg_root = cargo[pkg].root().to_path_buf();
222 let mut include = vec![pkg_root.clone()]; 228
223 include.extend( 229 let mut include = vec![pkg_root.clone()];
224 build_data 230 include.extend(
225 .and_then(|it| it.get(cargo.workspace_root())) 231 build_data
226 .and_then(|map| map.get(&cargo[pkg].id)) 232 .and_then(|it| it.get(cargo.workspace_root()))
227 .and_then(|it| it.out_dir.clone()), 233 .and_then(|map| map.get(&cargo[pkg].id))
228 ); 234 .and_then(|it| it.out_dir.clone()),
235 );
229 236
230 let mut exclude = vec![pkg_root.join(".git")]; 237 let mut exclude = vec![pkg_root.join(".git")];
231 if is_member { 238 if is_member {
232 exclude.push(pkg_root.join("target")); 239 exclude.push(pkg_root.join("target"));
233 } else { 240 } else {
234 exclude.push(pkg_root.join("tests")); 241 exclude.push(pkg_root.join("tests"));
235 exclude.push(pkg_root.join("examples")); 242 exclude.push(pkg_root.join("examples"));
236 exclude.push(pkg_root.join("benches")); 243 exclude.push(pkg_root.join("benches"));
237 } 244 }
238 PackageRoot { is_member, include, exclude } 245 PackageRoot { is_member, include, exclude }
239 }) 246 })
240 .chain(sysroot.crates().map(|krate| PackageRoot { 247 .chain(sysroot.crates().map(|krate| PackageRoot {
241 is_member: false,
242 include: vec![sysroot[krate].root_dir().to_path_buf()],
243 exclude: Vec::new(),
244 }))
245 .chain(rustc.into_iter().flat_map(|rustc| {
246 rustc.packages().map(move |krate| PackageRoot {
247 is_member: false, 248 is_member: false,
248 include: vec![rustc[krate].root().to_path_buf()], 249 include: vec![sysroot[krate].root_dir().to_path_buf()],
249 exclude: Vec::new(), 250 exclude: Vec::new(),
250 }) 251 }))
251 })) 252 .chain(rustc.into_iter().flat_map(|rustc| {
252 .collect(), 253 rustc.packages().map(move |krate| PackageRoot {
254 is_member: false,
255 include: vec![rustc[krate].root().to_path_buf()],
256 exclude: Vec::new(),
257 })
258 }))
259 .collect()
260 }
253 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files 261 ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
254 .into_iter() 262 .into_iter()
255 .map(|detached_file| PackageRoot { 263 .map(|detached_file| PackageRoot {
@@ -299,16 +307,22 @@ impl ProjectWorkspace {
299 project, 307 project,
300 sysroot, 308 sysroot,
301 ), 309 ),
302 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph( 310 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => {
303 rustc_cfg.clone(), 311 cargo_to_crate_graph(
304 &proc_macro_loader, 312 rustc_cfg.clone(),
305 load, 313 cfg_overrides,
306 cargo, 314 &proc_macro_loader,
307 build_data.and_then(|it| it.get(cargo.workspace_root())), 315 load,
308 sysroot, 316 cargo,
309 rustc, 317 build_data.and_then(|it| it.get(cargo.workspace_root())),
310 rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), 318 sysroot,
311 ), 319 rustc,
320 rustc
321 .as_ref()
322 .zip(build_data)
323 .and_then(|(it, map)| map.get(it.workspace_root())),
324 )
325 }
312 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { 326 ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
313 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) 327 detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
314 } 328 }
@@ -398,6 +412,7 @@ fn project_json_to_crate_graph(
398 412
399fn cargo_to_crate_graph( 413fn cargo_to_crate_graph(
400 rustc_cfg: Vec<CfgFlag>, 414 rustc_cfg: Vec<CfgFlag>,
415 override_cfg: &CfgOverrides,
401 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 416 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
402 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 417 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
403 cargo: &CargoWorkspace, 418 cargo: &CargoWorkspace,
@@ -425,6 +440,21 @@ fn cargo_to_crate_graph(
425 let mut has_private = false; 440 let mut has_private = false;
426 // Next, create crates for each package, target pair 441 // Next, create crates for each package, target pair
427 for pkg in cargo.packages() { 442 for pkg in cargo.packages() {
443 let mut cfg_options = &cfg_options;
444 let mut replaced_cfg_options;
445 if let Some(overrides) = override_cfg.get(&cargo[pkg].name) {
446 // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
447 // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
448 // working on rust-lang/rust as that's the only time it appears outside sysroot).
449 //
450 // A more ideal solution might be to reanalyze crates based on where the cursor is and
451 // figure out the set of cfgs that would have to apply to make it active.
452
453 replaced_cfg_options = cfg_options.clone();
454 replaced_cfg_options.apply_diff(overrides.clone());
455 cfg_options = &replaced_cfg_options;
456 };
457
428 has_private |= cargo[pkg].metadata.rustc_private; 458 has_private |= cargo[pkg].metadata.rustc_private;
429 let mut lib_tgt = None; 459 let mut lib_tgt = None;
430 for &tgt in cargo[pkg].targets.iter() { 460 for &tgt in cargo[pkg].targets.iter() {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 16c295639..3aeca8839 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -55,6 +55,8 @@ config_data! {
55 cargo_autoreload: bool = "true", 55 cargo_autoreload: bool = "true",
56 /// Activate all available features (`--all-features`). 56 /// Activate all available features (`--all-features`).
57 cargo_allFeatures: bool = "false", 57 cargo_allFeatures: bool = "false",
58 /// Unsets `#[cfg(test)]` for the specified crates.
59 cargo_unsetTest: Vec<String> = "[\"core\"]",
58 /// List of features to activate. 60 /// List of features to activate.
59 cargo_features: Vec<String> = "[]", 61 cargo_features: Vec<String> = "[]",
60 /// Run build scripts (`build.rs`) for more precise code analysis. 62 /// Run build scripts (`build.rs`) for more precise code analysis.
@@ -144,6 +146,12 @@ config_data! {
144 /// their contents. 146 /// their contents.
145 highlighting_strings: bool = "true", 147 highlighting_strings: bool = "true",
146 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
147 /// Whether to show `Debug` action. Only applies when 155 /// Whether to show `Debug` action. Only applies when
148 /// `#rust-analyzer.hoverActions.enable#` is set. 156 /// `#rust-analyzer.hoverActions.enable#` is set.
149 hoverActions_debug: bool = "true", 157 hoverActions_debug: bool = "true",
@@ -161,8 +169,6 @@ config_data! {
161 /// Whether to show `Run` action. Only applies when 169 /// Whether to show `Run` action. Only applies when
162 /// `#rust-analyzer.hoverActions.enable#` is set. 170 /// `#rust-analyzer.hoverActions.enable#` is set.
163 hoverActions_run: bool = "true", 171 hoverActions_run: bool = "true",
164 /// Use markdown syntax for links in hover.
165 hoverActions_linksInHover: bool = "true",
166 172
167 /// Whether to show inlay type hints for method chains. 173 /// Whether to show inlay type hints for method chains.
168 inlayHints_chainingHints: bool = "true", 174 inlayHints_chainingHints: bool = "true",
@@ -595,8 +601,10 @@ impl Config {
595 target: self.data.cargo_target.clone(), 601 target: self.data.cargo_target.clone(),
596 rustc_source, 602 rustc_source,
597 no_sysroot: self.data.cargo_noSysroot, 603 no_sysroot: self.data.cargo_noSysroot,
604 unset_test_crates: self.data.cargo_unsetTest.clone(),
598 } 605 }
599 } 606 }
607
600 pub fn rustfmt(&self) -> RustfmtConfig { 608 pub fn rustfmt(&self) -> RustfmtConfig {
601 match &self.data.rustfmt_overrideCommand { 609 match &self.data.rustfmt_overrideCommand {
602 Some(args) if !args.is_empty() => { 610 Some(args) if !args.is_empty() => {
@@ -730,7 +738,7 @@ impl Config {
730 run: self.data.hoverActions_enable && self.data.hoverActions_run, 738 run: self.data.hoverActions_enable && self.data.hoverActions_run,
731 debug: self.data.hoverActions_enable && self.data.hoverActions_debug, 739 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
732 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, 740 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
733 links_in_hover: self.data.hoverActions_linksInHover, 741 links_in_hover: self.data.hover_linksInHover,
734 markdown: try_or!( 742 markdown: try_or!(
735 self.caps 743 self.caps
736 .text_document 744 .text_document
@@ -743,6 +751,7 @@ impl Config {
743 &[] 751 &[]
744 ) 752 )
745 .contains(&MarkupKind::Markdown), 753 .contains(&MarkupKind::Markdown),
754 documentation: self.data.hover_documentation,
746 } 755 }
747 } 756 }
748 757
@@ -852,6 +861,7 @@ macro_rules! _config_data {
852 $({ 861 $({
853 let field = stringify!($field); 862 let field = stringify!($field);
854 let ty = stringify!($ty); 863 let ty = stringify!($ty);
864
855 (field, ty, &[$($doc),*], $default) 865 (field, ty, &[$($doc),*], $default)
856 },)* 866 },)*
857 ]) 867 ])
@@ -863,6 +873,7 @@ macro_rules! _config_data {
863 $({ 873 $({
864 let field = stringify!($field); 874 let field = stringify!($field);
865 let ty = stringify!($ty); 875 let ty = stringify!($ty);
876
866 (field, ty, &[$($doc),*], $default) 877 (field, ty, &[$($doc),*], $default)
867 },)* 878 },)*
868 ]) 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