aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--docs/user/generated_config.adoc20
-rw-r--r--editors/code/package.json25
11 files changed, 282 insertions, 170 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
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 18ea77266..cc7fdd38f 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -39,6 +39,11 @@ Automatically refresh project info via `cargo metadata` on
39-- 39--
40Activate all available features (`--all-features`). 40Activate all available features (`--all-features`).
41-- 41--
42[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
43+
44--
45Unsets `#[cfg(test)]` for the specified crates.
46--
42[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: 47[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
43+ 48+
44-- 49--
@@ -210,6 +215,16 @@ In some editors (e.g. vscode) semantic tokens override other highlighting gramma
210By 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
211their contents. 216their contents.
212-- 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--
213[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: 228[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
214+ 229+
215-- 230--
@@ -245,11 +260,6 @@ Whether to show `References` action. Only applies when
245Whether to show `Run` action. Only applies when 260Whether to show `Run` action. Only applies when
246`#rust-analyzer.hoverActions.enable#` is set. 261`#rust-analyzer.hoverActions.enable#` is set.
247-- 262--
248[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`)::
249+
250--
251Use markdown syntax for links in hover.
252--
253[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: 263[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
254+ 264+
255-- 265--
diff --git a/editors/code/package.json b/editors/code/package.json
index c077bd2c0..666016ae4 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -452,6 +452,16 @@
452 "default": false, 452 "default": false,
453 "type": "boolean" 453 "type": "boolean"
454 }, 454 },
455 "rust-analyzer.cargo.unsetTest": {
456 "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.",
457 "default": [
458 "core"
459 ],
460 "type": "array",
461 "items": {
462 "type": "string"
463 }
464 },
455 "rust-analyzer.cargo.features": { 465 "rust-analyzer.cargo.features": {
456 "markdownDescription": "List of features to activate.", 466 "markdownDescription": "List of features to activate.",
457 "default": [], 467 "default": [],
@@ -645,6 +655,16 @@
645 "default": true, 655 "default": true,
646 "type": "boolean" 656 "type": "boolean"
647 }, 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 },
648 "rust-analyzer.hoverActions.debug": { 668 "rust-analyzer.hoverActions.debug": {
649 "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.",
650 "default": true, 670 "default": true,
@@ -675,11 +695,6 @@
675 "default": true, 695 "default": true,
676 "type": "boolean" 696 "type": "boolean"
677 }, 697 },
678 "rust-analyzer.hoverActions.linksInHover": {
679 "markdownDescription": "Use markdown syntax for links in hover.",
680 "default": true,
681 "type": "boolean"
682 },
683 "rust-analyzer.inlayHints.chainingHints": { 698 "rust-analyzer.inlayHints.chainingHints": {
684 "markdownDescription": "Whether to show inlay type hints for method chains.", 699 "markdownDescription": "Whether to show inlay type hints for method chains.",
685 "default": true, 700 "default": true,