aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/paths/src/lib.rs5
-rw-r--r--crates/ra_hir/src/code_model.rs115
-rw-r--r--crates/ra_hir_def/src/nameres.rs7
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs5
-rw-r--r--crates/ra_hir_ty/src/_match.rs77
-rw-r--r--crates/ra_hir_ty/src/lib.rs63
-rw-r--r--crates/ra_ide/src/hover.rs1142
-rw-r--r--crates/ra_ide/src/lib.rs2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html76
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs9
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs48
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--crates/stdx/src/lib.rs1
-rw-r--r--crates/vfs/src/file_set.rs32
-rw-r--r--crates/vfs/src/lib.rs3
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/commands.ts14
-rw-r--r--editors/code/src/config.ts5
-rw-r--r--editors/code/src/debug.ts2
-rw-r--r--editors/code/src/main.ts1
23 files changed, 1499 insertions, 126 deletions
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index c7ce0c42f..190c50913 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -2,7 +2,7 @@
2//! relative paths. 2//! relative paths.
3use std::{ 3use std::{
4 convert::{TryFrom, TryInto}, 4 convert::{TryFrom, TryInto},
5 ops, 5 io, ops,
6 path::{Component, Path, PathBuf}, 6 path::{Component, Path, PathBuf},
7}; 7};
8 8
@@ -46,6 +46,9 @@ impl TryFrom<&str> for AbsPathBuf {
46} 46}
47 47
48impl AbsPathBuf { 48impl AbsPathBuf {
49 pub fn canonicalized(path: &Path) -> io::Result<AbsPathBuf> {
50 path.canonicalize().map(|it| AbsPathBuf::try_from(it).unwrap())
51 }
49 pub fn as_path(&self) -> &AbsPath { 52 pub fn as_path(&self) -> &AbsPath {
50 AbsPath::new_unchecked(self.0.as_path()) 53 AbsPath::new_unchecked(self.0.as_path())
51 } 54 }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 1a9f6cc76..ffd5278ec 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -26,8 +26,8 @@ use hir_ty::{
26 autoderef, 26 autoderef,
27 display::{HirDisplayError, HirFormatter}, 27 display::{HirDisplayError, HirFormatter},
28 expr::ExprValidator, 28 expr::ExprValidator,
29 method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, 29 method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
30 TyDefId, TypeCtor, 30 TraitEnvironment, Ty, TyDefId, TypeCtor,
31}; 31};
32use ra_db::{CrateId, CrateName, Edition, FileId}; 32use ra_db::{CrateId, CrateName, Edition, FileId};
33use ra_prof::profile; 33use ra_prof::profile;
@@ -186,6 +186,22 @@ impl ModuleDef {
186 186
187 module.visibility_of(db, self) 187 module.visibility_of(db, self)
188 } 188 }
189
190 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
191 match self {
192 ModuleDef::Adt(it) => Some(it.name(db)),
193 ModuleDef::Trait(it) => Some(it.name(db)),
194 ModuleDef::Function(it) => Some(it.name(db)),
195 ModuleDef::EnumVariant(it) => Some(it.name(db)),
196 ModuleDef::TypeAlias(it) => Some(it.name(db)),
197
198 ModuleDef::Module(it) => it.name(db),
199 ModuleDef::Const(it) => it.name(db),
200 ModuleDef::Static(it) => it.name(db),
201
202 ModuleDef::BuiltinType(it) => Some(it.as_name()),
203 }
204 }
189} 205}
190 206
191pub use hir_def::{ 207pub use hir_def::{
@@ -1359,6 +1375,27 @@ impl Type {
1359 Some(adt.into()) 1375 Some(adt.into())
1360 } 1376 }
1361 1377
1378 pub fn as_dyn_trait(&self) -> Option<Trait> {
1379 self.ty.value.dyn_trait().map(Into::into)
1380 }
1381
1382 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1383 self.ty.value.impl_trait_bounds(db).map(|it| {
1384 it.into_iter()
1385 .filter_map(|pred| match pred {
1386 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1387 Some(Trait::from(trait_ref.trait_))
1388 }
1389 _ => None,
1390 })
1391 .collect()
1392 })
1393 }
1394
1395 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1396 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1397 }
1398
1362 // FIXME: provide required accessors such that it becomes implementable from outside. 1399 // FIXME: provide required accessors such that it becomes implementable from outside.
1363 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { 1400 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1364 match (&self.ty.value, &other.ty.value) { 1401 match (&self.ty.value, &other.ty.value) {
@@ -1380,6 +1417,80 @@ impl Type {
1380 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, 1417 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1381 } 1418 }
1382 } 1419 }
1420
1421 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1422 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1423 // We need a different order here.
1424
1425 fn walk_substs(
1426 db: &dyn HirDatabase,
1427 type_: &Type,
1428 substs: &Substs,
1429 cb: &mut impl FnMut(Type),
1430 ) {
1431 for ty in substs.iter() {
1432 walk_type(db, &type_.derived(ty.clone()), cb);
1433 }
1434 }
1435
1436 fn walk_bounds(
1437 db: &dyn HirDatabase,
1438 type_: &Type,
1439 bounds: &[GenericPredicate],
1440 cb: &mut impl FnMut(Type),
1441 ) {
1442 for pred in bounds {
1443 match pred {
1444 GenericPredicate::Implemented(trait_ref) => {
1445 cb(type_.clone());
1446 walk_substs(db, type_, &trait_ref.substs, cb);
1447 }
1448 _ => (),
1449 }
1450 }
1451 }
1452
1453 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1454 let ty = type_.ty.value.strip_references();
1455 match ty {
1456 Ty::Apply(ApplicationTy { ctor, parameters }) => {
1457 match ctor {
1458 TypeCtor::Adt(_) => {
1459 cb(type_.derived(ty.clone()));
1460 }
1461 TypeCtor::AssociatedType(_) => {
1462 if let Some(_) = ty.associated_type_parent_trait(db) {
1463 cb(type_.derived(ty.clone()));
1464 }
1465 }
1466 _ => (),
1467 }
1468
1469 // adt params, tuples, etc...
1470 walk_substs(db, type_, parameters, cb);
1471 }
1472 Ty::Opaque(opaque_ty) => {
1473 if let Some(bounds) = ty.impl_trait_bounds(db) {
1474 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1475 }
1476
1477 walk_substs(db, type_, &opaque_ty.parameters, cb);
1478 }
1479 Ty::Placeholder(_) => {
1480 if let Some(bounds) = ty.impl_trait_bounds(db) {
1481 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1482 }
1483 }
1484 Ty::Dyn(bounds) => {
1485 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1486 }
1487
1488 _ => (),
1489 }
1490 }
1491
1492 walk_type(db, self, &mut cb);
1493 }
1383} 1494}
1384 1495
1385impl HirDisplay for Type { 1496impl HirDisplay for Type {
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index b3e5f491a..b8560fdc9 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -119,13 +119,6 @@ impl Default for ModuleOrigin {
119} 119}
120 120
121impl ModuleOrigin { 121impl ModuleOrigin {
122 pub(crate) fn not_sure_file(file: Option<FileId>, declaration: AstId<ast::Module>) -> Self {
123 match file {
124 None => ModuleOrigin::Inline { definition: declaration },
125 Some(definition) => ModuleOrigin::File { declaration, definition },
126 }
127 }
128
129 fn declaration(&self) -> Option<AstId<ast::Module>> { 122 fn declaration(&self) -> Option<AstId<ast::Module>> {
130 match self { 123 match self {
131 ModuleOrigin::File { declaration: module, .. } 124 ModuleOrigin::File { declaration: module, .. }
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 976e5e585..77baa4c69 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -825,7 +825,10 @@ impl ModCollector<'_, '_> {
825 let modules = &mut self.def_collector.def_map.modules; 825 let modules = &mut self.def_collector.def_map.modules;
826 let res = modules.alloc(ModuleData::default()); 826 let res = modules.alloc(ModuleData::default());
827 modules[res].parent = Some(self.module_id); 827 modules[res].parent = Some(self.module_id);
828 modules[res].origin = ModuleOrigin::not_sure_file(definition, declaration); 828 modules[res].origin = match definition {
829 None => ModuleOrigin::Inline { definition: declaration },
830 Some(definition) => ModuleOrigin::File { declaration, definition },
831 };
829 for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() { 832 for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
830 modules[res].scope.define_legacy_macro(name, mac) 833 modules[res].scope.define_legacy_macro(name, mac)
831 } 834 }
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 02a7a61f1..5495ce284 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -312,20 +312,16 @@ impl PatStack {
312 Self(v) 312 Self(v)
313 } 313 }
314 314
315 fn is_empty(&self) -> bool {
316 self.0.is_empty()
317 }
318
319 fn head(&self) -> PatIdOrWild {
320 self.0[0]
321 }
322
323 fn get_head(&self) -> Option<PatIdOrWild> { 315 fn get_head(&self) -> Option<PatIdOrWild> {
324 self.0.first().copied() 316 self.0.first().copied()
325 } 317 }
326 318
319 fn tail(&self) -> &[PatIdOrWild] {
320 self.0.get(1..).unwrap_or(&[])
321 }
322
327 fn to_tail(&self) -> PatStack { 323 fn to_tail(&self) -> PatStack {
328 Self::from_slice(&self.0[1..]) 324 Self::from_slice(self.tail())
329 } 325 }
330 326
331 fn replace_head_with<I, T>(&self, pats: I) -> PatStack 327 fn replace_head_with<I, T>(&self, pats: I) -> PatStack
@@ -347,7 +343,7 @@ impl PatStack {
347 /// 343 ///
348 /// See the module docs and the associated documentation in rustc for details. 344 /// See the module docs and the associated documentation in rustc for details.
349 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> { 345 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
350 if matches!(self.head().as_pat(cx), Pat::Wild) { 346 if matches!(self.get_head()?.as_pat(cx), Pat::Wild) {
351 Some(self.to_tail()) 347 Some(self.to_tail())
352 } else { 348 } else {
353 None 349 None
@@ -362,11 +358,12 @@ impl PatStack {
362 cx: &MatchCheckCtx, 358 cx: &MatchCheckCtx,
363 constructor: &Constructor, 359 constructor: &Constructor,
364 ) -> MatchCheckResult<Option<PatStack>> { 360 ) -> MatchCheckResult<Option<PatStack>> {
365 if self.is_empty() { 361 let head = match self.get_head() {
366 return Ok(None); 362 Some(head) => head,
367 } 363 None => return Ok(None),
364 };
368 365
369 let head_pat = self.head().as_pat(cx); 366 let head_pat = head.as_pat(cx);
370 let result = match (head_pat, constructor) { 367 let result = match (head_pat, constructor) {
371 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 368 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
372 if ellipsis.is_some() { 369 if ellipsis.is_some() {
@@ -394,7 +391,7 @@ impl PatStack {
394 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), 391 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
395 (Pat::Path(_), Constructor::Enum(constructor)) => { 392 (Pat::Path(_), Constructor::Enum(constructor)) => {
396 // unit enum variants become `Pat::Path` 393 // unit enum variants become `Pat::Path`
397 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 394 let pat_id = head.as_id().expect("we know this isn't a wild");
398 if !enum_variant_matches(cx, pat_id, *constructor) { 395 if !enum_variant_matches(cx, pat_id, *constructor) {
399 None 396 None
400 } else { 397 } else {
@@ -405,7 +402,7 @@ impl PatStack {
405 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, 402 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
406 Constructor::Enum(enum_constructor), 403 Constructor::Enum(enum_constructor),
407 ) => { 404 ) => {
408 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 405 let pat_id = head.as_id().expect("we know this isn't a wild");
409 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 406 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
410 None 407 None
411 } else { 408 } else {
@@ -445,7 +442,7 @@ impl PatStack {
445 } 442 }
446 } 443 }
447 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { 444 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
448 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 445 let pat_id = head.as_id().expect("we know this isn't a wild");
449 if !enum_variant_matches(cx, pat_id, *e) { 446 if !enum_variant_matches(cx, pat_id, *e) {
450 None 447 None
451 } else { 448 } else {
@@ -491,7 +488,7 @@ impl PatStack {
491 ) -> MatchCheckResult<PatStack> { 488 ) -> MatchCheckResult<PatStack> {
492 assert_eq!( 489 assert_eq!(
493 Pat::Wild, 490 Pat::Wild,
494 self.head().as_pat(cx), 491 self.get_head().expect("expand_wildcard called on empty PatStack").as_pat(cx),
495 "expand_wildcard must only be called on PatStack with wild at head", 492 "expand_wildcard must only be called on PatStack with wild at head",
496 ); 493 );
497 494
@@ -509,7 +506,6 @@ impl PatStack {
509 } 506 }
510} 507}
511 508
512#[derive(Debug)]
513/// A collection of PatStack. 509/// A collection of PatStack.
514/// 510///
515/// This type is modeled from the struct of the same name in `rustc`. 511/// This type is modeled from the struct of the same name in `rustc`.
@@ -623,13 +619,16 @@ pub(crate) fn is_useful(
623 _ => (), 619 _ => (),
624 } 620 }
625 621
626 if v.is_empty() { 622 let head = match v.get_head() {
627 let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful }; 623 Some(head) => head,
624 None => {
625 let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
628 626
629 return Ok(result); 627 return Ok(result);
630 } 628 }
629 };
631 630
632 if let Pat::Or(pat_ids) = v.head().as_pat(cx) { 631 if let Pat::Or(pat_ids) = head.as_pat(cx) {
633 let mut found_unimplemented = false; 632 let mut found_unimplemented = false;
634 let any_useful = pat_ids.iter().any(|&pat_id| { 633 let any_useful = pat_ids.iter().any(|&pat_id| {
635 let v = PatStack::from_pattern(pat_id); 634 let v = PatStack::from_pattern(pat_id);
@@ -653,7 +652,7 @@ pub(crate) fn is_useful(
653 }; 652 };
654 } 653 }
655 654
656 if let Some(constructor) = pat_constructor(cx, v.head())? { 655 if let Some(constructor) = pat_constructor(cx, head)? {
657 let matrix = matrix.specialize_constructor(&cx, &constructor)?; 656 let matrix = matrix.specialize_constructor(&cx, &constructor)?;
658 let v = v 657 let v = v
659 .specialize_constructor(&cx, &constructor)? 658 .specialize_constructor(&cx, &constructor)?
@@ -854,10 +853,10 @@ mod tests {
854 } 853 }
855 854
856 pub(super) fn check_no_diagnostic(ra_fixture: &str) { 855 pub(super) fn check_no_diagnostic(ra_fixture: &str) {
857 let diagnostic_count = 856 let (s, diagnostic_count) =
858 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; 857 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>();
859 858
860 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); 859 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s);
861 } 860 }
862 861
863 #[test] 862 #[test]
@@ -2014,6 +2013,28 @@ mod tests {
2014 ", 2013 ",
2015 ); 2014 );
2016 } 2015 }
2016
2017 #[test]
2018 fn or_pattern_panic_2() {
2019 // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
2020 // so this acts as a regression test for that.
2021 check_diagnostic(
2022 r"
2023 pub enum Category {
2024 Infinity,
2025 Zero,
2026 }
2027
2028 fn panic(a: Category, b: Category) {
2029 match (a, b) {
2030 (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {}
2031
2032 (Category::Infinity | Category::Zero, _) => {}
2033 }
2034 }
2035 ",
2036 );
2037 }
2017} 2038}
2018 2039
2019#[cfg(test)] 2040#[cfg(test)]
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 2b9372b4b..f22232324 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -73,6 +73,7 @@ pub use lower::{
73pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; 73pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
74 74
75pub use chalk_ir::{BoundVar, DebruijnIndex}; 75pub use chalk_ir::{BoundVar, DebruijnIndex};
76use itertools::Itertools;
76 77
77/// A type constructor or type name: this might be something like the primitive 78/// A type constructor or type name: this might be something like the primitive
78/// type `bool`, a struct like `Vec`, or things like function pointers or 79/// type `bool`, a struct like `Vec`, or things like function pointers or
@@ -815,6 +816,11 @@ impl Ty {
815 } 816 }
816 } 817 }
817 818
819 /// If this is a `dyn Trait`, returns that trait.
820 pub fn dyn_trait(&self) -> Option<TraitId> {
821 self.dyn_trait_ref().map(|it| it.trait_)
822 }
823
818 fn builtin_deref(&self) -> Option<Ty> { 824 fn builtin_deref(&self) -> Option<Ty> {
819 match self { 825 match self {
820 Ty::Apply(a_ty) => match a_ty.ctor { 826 Ty::Apply(a_ty) => match a_ty.ctor {
@@ -867,13 +873,56 @@ impl Ty {
867 } 873 }
868 } 874 }
869 875
870 /// If this is a `dyn Trait`, returns that trait. 876 pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<GenericPredicate>> {
871 pub fn dyn_trait(&self) -> Option<TraitId> {
872 match self { 877 match self {
873 Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { 878 Ty::Opaque(opaque_ty) => {
874 GenericPredicate::Implemented(tr) => Some(tr.trait_), 879 let predicates = match opaque_ty.opaque_ty_id {
875 _ => None, 880 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
876 }), 881 db.return_type_impl_traits(func).map(|it| {
882 let data = (*it)
883 .as_ref()
884 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
885 data.clone().subst(&opaque_ty.parameters)
886 })
887 }
888 };
889
890 predicates.map(|it| it.value)
891 }
892 Ty::Placeholder(id) => {
893 let generic_params = db.generic_params(id.parent);
894 let param_data = &generic_params.types[id.local_id];
895 match param_data.provenance {
896 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
897 let predicates = db
898 .generic_predicates_for_param(*id)
899 .into_iter()
900 .map(|pred| pred.value.clone())
901 .collect_vec();
902
903 Some(predicates)
904 }
905 _ => None,
906 }
907 }
908 _ => None,
909 }
910 }
911
912 pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
913 match self {
914 Ty::Apply(ApplicationTy { ctor: TypeCtor::AssociatedType(type_alias_id), .. }) => {
915 match type_alias_id.lookup(db.upcast()).container {
916 AssocContainerId::TraitId(trait_id) => Some(trait_id),
917 _ => None,
918 }
919 }
920 Ty::Projection(projection_ty) => {
921 match projection_ty.associated_ty.lookup(db.upcast()).container {
922 AssocContainerId::TraitId(trait_id) => Some(trait_id),
923 _ => None,
924 }
925 }
877 _ => None, 926 _ => None,
878 } 927 }
879 } 928 }
@@ -1057,5 +1106,5 @@ pub struct ReturnTypeImplTraits {
1057 1106
1058#[derive(Clone, PartialEq, Eq, Debug, Hash)] 1107#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1059pub(crate) struct ReturnTypeImplTrait { 1108pub(crate) struct ReturnTypeImplTrait {
1060 pub(crate) bounds: Binders<Vec<GenericPredicate>>, 1109 pub bounds: Binders<Vec<GenericPredicate>>,
1061} 1110}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index ad78b7671..d870e4cbc 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -2,7 +2,7 @@ use std::iter::once;
2 2
3use hir::{ 3use hir::{
4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, 4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleDef, ModuleSource, Semantics, 5 Module, ModuleDef, ModuleSource, Semantics,
6}; 6};
7use itertools::Itertools; 7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
@@ -13,7 +13,9 @@ use ra_ide_db::{
13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, 16 display::{
17 macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav, TryToNav,
18 },
17 runnables::runnable, 19 runnables::runnable,
18 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 20 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
19}; 21};
@@ -24,19 +26,21 @@ pub struct HoverConfig {
24 pub implementations: bool, 26 pub implementations: bool,
25 pub run: bool, 27 pub run: bool,
26 pub debug: bool, 28 pub debug: bool,
29 pub goto_type_def: bool,
27} 30}
28 31
29impl Default for HoverConfig { 32impl Default for HoverConfig {
30 fn default() -> Self { 33 fn default() -> Self {
31 Self { implementations: true, run: true, debug: true } 34 Self { implementations: true, run: true, debug: true, goto_type_def: true }
32 } 35 }
33} 36}
34 37
35impl HoverConfig { 38impl HoverConfig {
36 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false }; 39 pub const NO_ACTIONS: Self =
40 Self { implementations: false, run: false, debug: false, goto_type_def: false };
37 41
38 pub fn any(&self) -> bool { 42 pub fn any(&self) -> bool {
39 self.implementations || self.runnable() 43 self.implementations || self.runnable() || self.goto_type_def
40 } 44 }
41 45
42 pub fn none(&self) -> bool { 46 pub fn none(&self) -> bool {
@@ -52,6 +56,13 @@ impl HoverConfig {
52pub enum HoverAction { 56pub enum HoverAction {
53 Runnable(Runnable), 57 Runnable(Runnable),
54 Implementaion(FilePosition), 58 Implementaion(FilePosition),
59 GoToType(Vec<HoverGotoTypeData>),
60}
61
62#[derive(Debug, Clone, Eq, PartialEq)]
63pub struct HoverGotoTypeData {
64 pub mod_path: String,
65 pub nav: NavigationTarget,
55} 66}
56 67
57/// Contains the results when hovering over an item 68/// Contains the results when hovering over an item
@@ -138,6 +149,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
138 res.push_action(action); 149 res.push_action(action);
139 } 150 }
140 151
152 if let Some(action) = goto_type_action(db, name_kind) {
153 res.push_action(action);
154 }
155
141 return Some(RangeInfo::new(range, res)); 156 return Some(RangeInfo::new(range, res));
142 } 157 }
143 } 158 }
@@ -218,6 +233,44 @@ fn runnable_action(
218 } 233 }
219} 234}
220 235
236fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
237 match def {
238 Definition::Local(it) => {
239 let mut targets: Vec<ModuleDef> = Vec::new();
240 let mut push_new_def = |item: ModuleDef| {
241 if !targets.contains(&item) {
242 targets.push(item);
243 }
244 };
245
246 it.ty(db).walk(db, |t| {
247 if let Some(adt) = t.as_adt() {
248 push_new_def(adt.into());
249 } else if let Some(trait_) = t.as_dyn_trait() {
250 push_new_def(trait_.into());
251 } else if let Some(traits) = t.as_impl_traits(db) {
252 traits.into_iter().for_each(|it| push_new_def(it.into()));
253 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
254 push_new_def(trait_.into());
255 }
256 });
257
258 let targets = targets
259 .into_iter()
260 .filter_map(|it| {
261 Some(HoverGotoTypeData {
262 mod_path: mod_path(db, &it)?,
263 nav: it.try_to_nav(db)?,
264 })
265 })
266 .collect();
267
268 Some(HoverAction::GoToType(targets))
269 }
270 _ => None,
271 }
272}
273
221fn hover_text( 274fn hover_text(
222 docs: Option<String>, 275 docs: Option<String>,
223 desc: Option<String>, 276 desc: Option<String>,
@@ -248,25 +301,31 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
248 .map(|name| name.to_string()) 301 .map(|name| name.to_string())
249} 302}
250 303
251fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 304fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String {
252 let mod_path = def.module(db).map(|module| { 305 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string))
253 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) 306 .chain(
254 .chain( 307 module
255 module 308 .path_to_root(db)
256 .path_to_root(db) 309 .into_iter()
257 .into_iter() 310 .rev()
258 .rev() 311 .map(|it| it.name(db).map(|name| name.to_string())),
259 .map(|it| it.name(db).map(|name| name.to_string())), 312 )
260 ) 313 .chain(once(name))
261 .chain(once(definition_owner_name(db, def))) 314 .flatten()
262 .flatten() 315 .join("::")
263 .join("::") 316}
264 }); 317
265 mod_path 318// returns None only for ModuleDef::BuiltinType
319fn mod_path(db: &RootDatabase, item: &ModuleDef) -> Option<String> {
320 Some(determine_mod_path(db, item.module(db)?, item.name(db).map(|name| name.to_string())))
321}
322
323fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
324 def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def)))
266} 325}
267 326
268fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { 327fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
269 let mod_path = determine_mod_path(db, &def); 328 let mod_path = definition_mod_path(db, &def);
270 return match def { 329 return match def {
271 Definition::Macro(it) => { 330 Definition::Macro(it) => {
272 let src = it.source(db); 331 let src = it.source(db);
@@ -1310,4 +1369,1045 @@ fn func(foo: i32) { if true { <|>foo; }; }
1310 ] 1369 ]
1311 "###); 1370 "###);
1312 } 1371 }
1372
1373 #[test]
1374 fn test_hover_struct_has_goto_type_action() {
1375 let (_, actions) = check_hover_result(
1376 "
1377 //- /main.rs
1378 struct S{ f1: u32 }
1379
1380 fn main() {
1381 let s<|>t = S{ f1:0 };
1382 }
1383 ",
1384 &["S"],
1385 );
1386 assert_debug_snapshot!(actions,
1387 @r###"
1388 [
1389 GoToType(
1390 [
1391 HoverGotoTypeData {
1392 mod_path: "S",
1393 nav: NavigationTarget {
1394 file_id: FileId(
1395 1,
1396 ),
1397 full_range: 0..19,
1398 name: "S",
1399 kind: STRUCT_DEF,
1400 focus_range: Some(
1401 7..8,
1402 ),
1403 container_name: None,
1404 description: Some(
1405 "struct S",
1406 ),
1407 docs: None,
1408 },
1409 },
1410 ],
1411 ),
1412 ]
1413 "###);
1414 }
1415
1416 #[test]
1417 fn test_hover_generic_struct_has_goto_type_actions() {
1418 let (_, actions) = check_hover_result(
1419 "
1420 //- /main.rs
1421 struct Arg(u32);
1422 struct S<T>{ f1: T }
1423
1424 fn main() {
1425 let s<|>t = S{ f1:Arg(0) };
1426 }
1427 ",
1428 &["S<Arg>"],
1429 );
1430 assert_debug_snapshot!(actions,
1431 @r###"
1432 [
1433 GoToType(
1434 [
1435 HoverGotoTypeData {
1436 mod_path: "S",
1437 nav: NavigationTarget {
1438 file_id: FileId(
1439 1,
1440 ),
1441 full_range: 17..37,
1442 name: "S",
1443 kind: STRUCT_DEF,
1444 focus_range: Some(
1445 24..25,
1446 ),
1447 container_name: None,
1448 description: Some(
1449 "struct S",
1450 ),
1451 docs: None,
1452 },
1453 },
1454 HoverGotoTypeData {
1455 mod_path: "Arg",
1456 nav: NavigationTarget {
1457 file_id: FileId(
1458 1,
1459 ),
1460 full_range: 0..16,
1461 name: "Arg",
1462 kind: STRUCT_DEF,
1463 focus_range: Some(
1464 7..10,
1465 ),
1466 container_name: None,
1467 description: Some(
1468 "struct Arg",
1469 ),
1470 docs: None,
1471 },
1472 },
1473 ],
1474 ),
1475 ]
1476 "###);
1477 }
1478
1479 #[test]
1480 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
1481 let (_, actions) = check_hover_result(
1482 "
1483 //- /main.rs
1484 struct Arg(u32);
1485 struct S<T>{ f1: T }
1486
1487 fn main() {
1488 let s<|>t = S{ f1: S{ f1: Arg(0) } };
1489 }
1490 ",
1491 &["S<S<Arg>>"],
1492 );
1493 assert_debug_snapshot!(actions,
1494 @r###"
1495 [
1496 GoToType(
1497 [
1498 HoverGotoTypeData {
1499 mod_path: "S",
1500 nav: NavigationTarget {
1501 file_id: FileId(
1502 1,
1503 ),
1504 full_range: 17..37,
1505 name: "S",
1506 kind: STRUCT_DEF,
1507 focus_range: Some(
1508 24..25,
1509 ),
1510 container_name: None,
1511 description: Some(
1512 "struct S",
1513 ),
1514 docs: None,
1515 },
1516 },
1517 HoverGotoTypeData {
1518 mod_path: "Arg",
1519 nav: NavigationTarget {
1520 file_id: FileId(
1521 1,
1522 ),
1523 full_range: 0..16,
1524 name: "Arg",
1525 kind: STRUCT_DEF,
1526 focus_range: Some(
1527 7..10,
1528 ),
1529 container_name: None,
1530 description: Some(
1531 "struct Arg",
1532 ),
1533 docs: None,
1534 },
1535 },
1536 ],
1537 ),
1538 ]
1539 "###);
1540 }
1541
1542 #[test]
1543 fn test_hover_tuple_has_goto_type_actions() {
1544 let (_, actions) = check_hover_result(
1545 "
1546 //- /main.rs
1547 struct A(u32);
1548 struct B(u32);
1549 mod M {
1550 pub struct C(u32);
1551 }
1552
1553 fn main() {
1554 let s<|>t = (A(1), B(2), M::C(3) );
1555 }
1556 ",
1557 &["(A, B, C)"],
1558 );
1559 assert_debug_snapshot!(actions,
1560 @r###"
1561 [
1562 GoToType(
1563 [
1564 HoverGotoTypeData {
1565 mod_path: "A",
1566 nav: NavigationTarget {
1567 file_id: FileId(
1568 1,
1569 ),
1570 full_range: 0..14,
1571 name: "A",
1572 kind: STRUCT_DEF,
1573 focus_range: Some(
1574 7..8,
1575 ),
1576 container_name: None,
1577 description: Some(
1578 "struct A",
1579 ),
1580 docs: None,
1581 },
1582 },
1583 HoverGotoTypeData {
1584 mod_path: "B",
1585 nav: NavigationTarget {
1586 file_id: FileId(
1587 1,
1588 ),
1589 full_range: 15..29,
1590 name: "B",
1591 kind: STRUCT_DEF,
1592 focus_range: Some(
1593 22..23,
1594 ),
1595 container_name: None,
1596 description: Some(
1597 "struct B",
1598 ),
1599 docs: None,
1600 },
1601 },
1602 HoverGotoTypeData {
1603 mod_path: "M::C",
1604 nav: NavigationTarget {
1605 file_id: FileId(
1606 1,
1607 ),
1608 full_range: 42..60,
1609 name: "C",
1610 kind: STRUCT_DEF,
1611 focus_range: Some(
1612 53..54,
1613 ),
1614 container_name: None,
1615 description: Some(
1616 "pub struct C",
1617 ),
1618 docs: None,
1619 },
1620 },
1621 ],
1622 ),
1623 ]
1624 "###);
1625 }
1626
1627 #[test]
1628 fn test_hover_return_impl_trait_has_goto_type_action() {
1629 let (_, actions) = check_hover_result(
1630 "
1631 //- /main.rs
1632 trait Foo {}
1633
1634 fn foo() -> impl Foo {}
1635
1636 fn main() {
1637 let s<|>t = foo();
1638 }
1639 ",
1640 &["impl Foo"],
1641 );
1642 assert_debug_snapshot!(actions,
1643 @r###"
1644 [
1645 GoToType(
1646 [
1647 HoverGotoTypeData {
1648 mod_path: "Foo",
1649 nav: NavigationTarget {
1650 file_id: FileId(
1651 1,
1652 ),
1653 full_range: 0..12,
1654 name: "Foo",
1655 kind: TRAIT_DEF,
1656 focus_range: Some(
1657 6..9,
1658 ),
1659 container_name: None,
1660 description: Some(
1661 "trait Foo",
1662 ),
1663 docs: None,
1664 },
1665 },
1666 ],
1667 ),
1668 ]
1669 "###);
1670 }
1671
1672 #[test]
1673 fn test_hover_generic_return_impl_trait_has_goto_type_action() {
1674 let (_, actions) = check_hover_result(
1675 "
1676 //- /main.rs
1677 trait Foo<T> {}
1678 struct S;
1679
1680 fn foo() -> impl Foo<S> {}
1681
1682 fn main() {
1683 let s<|>t = foo();
1684 }
1685 ",
1686 &["impl Foo<S>"],
1687 );
1688 assert_debug_snapshot!(actions,
1689 @r###"
1690 [
1691 GoToType(
1692 [
1693 HoverGotoTypeData {
1694 mod_path: "Foo",
1695 nav: NavigationTarget {
1696 file_id: FileId(
1697 1,
1698 ),
1699 full_range: 0..15,
1700 name: "Foo",
1701 kind: TRAIT_DEF,
1702 focus_range: Some(
1703 6..9,
1704 ),
1705 container_name: None,
1706 description: Some(
1707 "trait Foo",
1708 ),
1709 docs: None,
1710 },
1711 },
1712 HoverGotoTypeData {
1713 mod_path: "S",
1714 nav: NavigationTarget {
1715 file_id: FileId(
1716 1,
1717 ),
1718 full_range: 16..25,
1719 name: "S",
1720 kind: STRUCT_DEF,
1721 focus_range: Some(
1722 23..24,
1723 ),
1724 container_name: None,
1725 description: Some(
1726 "struct S",
1727 ),
1728 docs: None,
1729 },
1730 },
1731 ],
1732 ),
1733 ]
1734 "###);
1735 }
1736
1737 #[test]
1738 fn test_hover_return_impl_traits_has_goto_type_action() {
1739 let (_, actions) = check_hover_result(
1740 "
1741 //- /main.rs
1742 trait Foo {}
1743 trait Bar {}
1744
1745 fn foo() -> impl Foo + Bar {}
1746
1747 fn main() {
1748 let s<|>t = foo();
1749 }
1750 ",
1751 &["impl Foo + Bar"],
1752 );
1753 assert_debug_snapshot!(actions,
1754 @r###"
1755 [
1756 GoToType(
1757 [
1758 HoverGotoTypeData {
1759 mod_path: "Foo",
1760 nav: NavigationTarget {
1761 file_id: FileId(
1762 1,
1763 ),
1764 full_range: 0..12,
1765 name: "Foo",
1766 kind: TRAIT_DEF,
1767 focus_range: Some(
1768 6..9,
1769 ),
1770 container_name: None,
1771 description: Some(
1772 "trait Foo",
1773 ),
1774 docs: None,
1775 },
1776 },
1777 HoverGotoTypeData {
1778 mod_path: "Bar",
1779 nav: NavigationTarget {
1780 file_id: FileId(
1781 1,
1782 ),
1783 full_range: 13..25,
1784 name: "Bar",
1785 kind: TRAIT_DEF,
1786 focus_range: Some(
1787 19..22,
1788 ),
1789 container_name: None,
1790 description: Some(
1791 "trait Bar",
1792 ),
1793 docs: None,
1794 },
1795 },
1796 ],
1797 ),
1798 ]
1799 "###);
1800 }
1801
1802 #[test]
1803 fn test_hover_generic_return_impl_traits_has_goto_type_action() {
1804 let (_, actions) = check_hover_result(
1805 "
1806 //- /main.rs
1807 trait Foo<T> {}
1808 trait Bar<T> {}
1809 struct S1 {}
1810 struct S2 {}
1811
1812 fn foo() -> impl Foo<S1> + Bar<S2> {}
1813
1814 fn main() {
1815 let s<|>t = foo();
1816 }
1817 ",
1818 &["impl Foo<S1> + Bar<S2>"],
1819 );
1820 assert_debug_snapshot!(actions,
1821 @r###"
1822 [
1823 GoToType(
1824 [
1825 HoverGotoTypeData {
1826 mod_path: "Foo",
1827 nav: NavigationTarget {
1828 file_id: FileId(
1829 1,
1830 ),
1831 full_range: 0..15,
1832 name: "Foo",
1833 kind: TRAIT_DEF,
1834 focus_range: Some(
1835 6..9,
1836 ),
1837 container_name: None,
1838 description: Some(
1839 "trait Foo",
1840 ),
1841 docs: None,
1842 },
1843 },
1844 HoverGotoTypeData {
1845 mod_path: "Bar",
1846 nav: NavigationTarget {
1847 file_id: FileId(
1848 1,
1849 ),
1850 full_range: 16..31,
1851 name: "Bar",
1852 kind: TRAIT_DEF,
1853 focus_range: Some(
1854 22..25,
1855 ),
1856 container_name: None,
1857 description: Some(
1858 "trait Bar",
1859 ),
1860 docs: None,
1861 },
1862 },
1863 HoverGotoTypeData {
1864 mod_path: "S1",
1865 nav: NavigationTarget {
1866 file_id: FileId(
1867 1,
1868 ),
1869 full_range: 32..44,
1870 name: "S1",
1871 kind: STRUCT_DEF,
1872 focus_range: Some(
1873 39..41,
1874 ),
1875 container_name: None,
1876 description: Some(
1877 "struct S1",
1878 ),
1879 docs: None,
1880 },
1881 },
1882 HoverGotoTypeData {
1883 mod_path: "S2",
1884 nav: NavigationTarget {
1885 file_id: FileId(
1886 1,
1887 ),
1888 full_range: 45..57,
1889 name: "S2",
1890 kind: STRUCT_DEF,
1891 focus_range: Some(
1892 52..54,
1893 ),
1894 container_name: None,
1895 description: Some(
1896 "struct S2",
1897 ),
1898 docs: None,
1899 },
1900 },
1901 ],
1902 ),
1903 ]
1904 "###);
1905 }
1906
1907 #[test]
1908 fn test_hover_arg_impl_trait_has_goto_type_action() {
1909 let (_, actions) = check_hover_result(
1910 "
1911 //- /lib.rs
1912 trait Foo {}
1913 fn foo(ar<|>g: &impl Foo) {}
1914 ",
1915 &["&impl Foo"],
1916 );
1917 assert_debug_snapshot!(actions,
1918 @r###"
1919 [
1920 GoToType(
1921 [
1922 HoverGotoTypeData {
1923 mod_path: "Foo",
1924 nav: NavigationTarget {
1925 file_id: FileId(
1926 1,
1927 ),
1928 full_range: 0..12,
1929 name: "Foo",
1930 kind: TRAIT_DEF,
1931 focus_range: Some(
1932 6..9,
1933 ),
1934 container_name: None,
1935 description: Some(
1936 "trait Foo",
1937 ),
1938 docs: None,
1939 },
1940 },
1941 ],
1942 ),
1943 ]
1944 "###);
1945 }
1946
1947 #[test]
1948 fn test_hover_arg_impl_traits_has_goto_type_action() {
1949 let (_, actions) = check_hover_result(
1950 "
1951 //- /lib.rs
1952 trait Foo {}
1953 trait Bar<T> {}
1954 struct S{}
1955
1956 fn foo(ar<|>g: &impl Foo + Bar<S>) {}
1957 ",
1958 &["&impl Foo + Bar<S>"],
1959 );
1960 assert_debug_snapshot!(actions,
1961 @r###"
1962 [
1963 GoToType(
1964 [
1965 HoverGotoTypeData {
1966 mod_path: "Foo",
1967 nav: NavigationTarget {
1968 file_id: FileId(
1969 1,
1970 ),
1971 full_range: 0..12,
1972 name: "Foo",
1973 kind: TRAIT_DEF,
1974 focus_range: Some(
1975 6..9,
1976 ),
1977 container_name: None,
1978 description: Some(
1979 "trait Foo",
1980 ),
1981 docs: None,
1982 },
1983 },
1984 HoverGotoTypeData {
1985 mod_path: "Bar",
1986 nav: NavigationTarget {
1987 file_id: FileId(
1988 1,
1989 ),
1990 full_range: 13..28,
1991 name: "Bar",
1992 kind: TRAIT_DEF,
1993 focus_range: Some(
1994 19..22,
1995 ),
1996 container_name: None,
1997 description: Some(
1998 "trait Bar",
1999 ),
2000 docs: None,
2001 },
2002 },
2003 HoverGotoTypeData {
2004 mod_path: "S",
2005 nav: NavigationTarget {
2006 file_id: FileId(
2007 1,
2008 ),
2009 full_range: 29..39,
2010 name: "S",
2011 kind: STRUCT_DEF,
2012 focus_range: Some(
2013 36..37,
2014 ),
2015 container_name: None,
2016 description: Some(
2017 "struct S",
2018 ),
2019 docs: None,
2020 },
2021 },
2022 ],
2023 ),
2024 ]
2025 "###);
2026 }
2027
2028 #[test]
2029 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2030 let (_, actions) = check_hover_result(
2031 "
2032 //- /lib.rs
2033 trait Foo<T> {}
2034 struct S {}
2035 fn foo(ar<|>g: &impl Foo<S>) {}
2036 ",
2037 &["&impl Foo<S>"],
2038 );
2039 assert_debug_snapshot!(actions,
2040 @r###"
2041 [
2042 GoToType(
2043 [
2044 HoverGotoTypeData {
2045 mod_path: "Foo",
2046 nav: NavigationTarget {
2047 file_id: FileId(
2048 1,
2049 ),
2050 full_range: 0..15,
2051 name: "Foo",
2052 kind: TRAIT_DEF,
2053 focus_range: Some(
2054 6..9,
2055 ),
2056 container_name: None,
2057 description: Some(
2058 "trait Foo",
2059 ),
2060 docs: None,
2061 },
2062 },
2063 HoverGotoTypeData {
2064 mod_path: "S",
2065 nav: NavigationTarget {
2066 file_id: FileId(
2067 1,
2068 ),
2069 full_range: 16..27,
2070 name: "S",
2071 kind: STRUCT_DEF,
2072 focus_range: Some(
2073 23..24,
2074 ),
2075 container_name: None,
2076 description: Some(
2077 "struct S",
2078 ),
2079 docs: None,
2080 },
2081 },
2082 ],
2083 ),
2084 ]
2085 "###);
2086 }
2087
2088 #[test]
2089 fn test_hover_dyn_return_has_goto_type_action() {
2090 let (_, actions) = check_hover_result(
2091 "
2092 //- /main.rs
2093 trait Foo {}
2094 struct S;
2095 impl Foo for S {}
2096
2097 struct B<T>{}
2098
2099 fn foo() -> B<dyn Foo> {}
2100
2101 fn main() {
2102 let s<|>t = foo();
2103 }
2104 ",
2105 &["B<dyn Foo>"],
2106 );
2107 assert_debug_snapshot!(actions,
2108 @r###"
2109 [
2110 GoToType(
2111 [
2112 HoverGotoTypeData {
2113 mod_path: "B",
2114 nav: NavigationTarget {
2115 file_id: FileId(
2116 1,
2117 ),
2118 full_range: 41..54,
2119 name: "B",
2120 kind: STRUCT_DEF,
2121 focus_range: Some(
2122 48..49,
2123 ),
2124 container_name: None,
2125 description: Some(
2126 "struct B",
2127 ),
2128 docs: None,
2129 },
2130 },
2131 HoverGotoTypeData {
2132 mod_path: "Foo",
2133 nav: NavigationTarget {
2134 file_id: FileId(
2135 1,
2136 ),
2137 full_range: 0..12,
2138 name: "Foo",
2139 kind: TRAIT_DEF,
2140 focus_range: Some(
2141 6..9,
2142 ),
2143 container_name: None,
2144 description: Some(
2145 "trait Foo",
2146 ),
2147 docs: None,
2148 },
2149 },
2150 ],
2151 ),
2152 ]
2153 "###);
2154 }
2155
2156 #[test]
2157 fn test_hover_dyn_arg_has_goto_type_action() {
2158 let (_, actions) = check_hover_result(
2159 "
2160 //- /lib.rs
2161 trait Foo {}
2162 fn foo(ar<|>g: &dyn Foo) {}
2163 ",
2164 &["&dyn Foo"],
2165 );
2166 assert_debug_snapshot!(actions,
2167 @r###"
2168 [
2169 GoToType(
2170 [
2171 HoverGotoTypeData {
2172 mod_path: "Foo",
2173 nav: NavigationTarget {
2174 file_id: FileId(
2175 1,
2176 ),
2177 full_range: 0..12,
2178 name: "Foo",
2179 kind: TRAIT_DEF,
2180 focus_range: Some(
2181 6..9,
2182 ),
2183 container_name: None,
2184 description: Some(
2185 "trait Foo",
2186 ),
2187 docs: None,
2188 },
2189 },
2190 ],
2191 ),
2192 ]
2193 "###);
2194 }
2195
2196 #[test]
2197 fn test_hover_generic_dyn_arg_has_goto_type_action() {
2198 let (_, actions) = check_hover_result(
2199 "
2200 //- /lib.rs
2201 trait Foo<T> {}
2202 struct S {}
2203 fn foo(ar<|>g: &dyn Foo<S>) {}
2204 ",
2205 &["&dyn Foo<S>"],
2206 );
2207 assert_debug_snapshot!(actions,
2208 @r###"
2209 [
2210 GoToType(
2211 [
2212 HoverGotoTypeData {
2213 mod_path: "Foo",
2214 nav: NavigationTarget {
2215 file_id: FileId(
2216 1,
2217 ),
2218 full_range: 0..15,
2219 name: "Foo",
2220 kind: TRAIT_DEF,
2221 focus_range: Some(
2222 6..9,
2223 ),
2224 container_name: None,
2225 description: Some(
2226 "trait Foo",
2227 ),
2228 docs: None,
2229 },
2230 },
2231 HoverGotoTypeData {
2232 mod_path: "S",
2233 nav: NavigationTarget {
2234 file_id: FileId(
2235 1,
2236 ),
2237 full_range: 16..27,
2238 name: "S",
2239 kind: STRUCT_DEF,
2240 focus_range: Some(
2241 23..24,
2242 ),
2243 container_name: None,
2244 description: Some(
2245 "struct S",
2246 ),
2247 docs: None,
2248 },
2249 },
2250 ],
2251 ),
2252 ]
2253 "###);
2254 }
2255
2256 #[test]
2257 fn test_hover_goto_type_action_links_order() {
2258 let (_, actions) = check_hover_result(
2259 "
2260 //- /lib.rs
2261 trait ImplTrait<T> {}
2262 trait DynTrait<T> {}
2263 struct B<T> {}
2264 struct S {}
2265
2266 fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
2267 ",
2268 &["&impl ImplTrait<B<dyn DynTrait<B<S>>>>"],
2269 );
2270 assert_debug_snapshot!(actions,
2271 @r###"
2272 [
2273 GoToType(
2274 [
2275 HoverGotoTypeData {
2276 mod_path: "ImplTrait",
2277 nav: NavigationTarget {
2278 file_id: FileId(
2279 1,
2280 ),
2281 full_range: 0..21,
2282 name: "ImplTrait",
2283 kind: TRAIT_DEF,
2284 focus_range: Some(
2285 6..15,
2286 ),
2287 container_name: None,
2288 description: Some(
2289 "trait ImplTrait",
2290 ),
2291 docs: None,
2292 },
2293 },
2294 HoverGotoTypeData {
2295 mod_path: "B",
2296 nav: NavigationTarget {
2297 file_id: FileId(
2298 1,
2299 ),
2300 full_range: 43..57,
2301 name: "B",
2302 kind: STRUCT_DEF,
2303 focus_range: Some(
2304 50..51,
2305 ),
2306 container_name: None,
2307 description: Some(
2308 "struct B",
2309 ),
2310 docs: None,
2311 },
2312 },
2313 HoverGotoTypeData {
2314 mod_path: "DynTrait",
2315 nav: NavigationTarget {
2316 file_id: FileId(
2317 1,
2318 ),
2319 full_range: 22..42,
2320 name: "DynTrait",
2321 kind: TRAIT_DEF,
2322 focus_range: Some(
2323 28..36,
2324 ),
2325 container_name: None,
2326 description: Some(
2327 "trait DynTrait",
2328 ),
2329 docs: None,
2330 },
2331 },
2332 HoverGotoTypeData {
2333 mod_path: "S",
2334 nav: NavigationTarget {
2335 file_id: FileId(
2336 1,
2337 ),
2338 full_range: 58..69,
2339 name: "S",
2340 kind: STRUCT_DEF,
2341 focus_range: Some(
2342 65..66,
2343 ),
2344 container_name: None,
2345 description: Some(
2346 "struct S",
2347 ),
2348 docs: None,
2349 },
2350 },
2351 ],
2352 ),
2353 ]
2354 "###);
2355 }
2356
2357 #[test]
2358 fn test_hover_associated_type_has_goto_type_action() {
2359 let (_, actions) = check_hover_result(
2360 "
2361 //- /main.rs
2362 trait Foo {
2363 type Item;
2364 fn get(self) -> Self::Item {}
2365 }
2366
2367 struct Bar{}
2368 struct S{}
2369
2370 impl Foo for S{
2371 type Item = Bar;
2372 }
2373
2374 fn test() -> impl Foo {
2375 S{}
2376 }
2377
2378 fn main() {
2379 let s<|>t = test().get();
2380 }
2381 ",
2382 &["Foo::Item<impl Foo>"],
2383 );
2384 assert_debug_snapshot!(actions,
2385 @r###"
2386 [
2387 GoToType(
2388 [
2389 HoverGotoTypeData {
2390 mod_path: "Foo",
2391 nav: NavigationTarget {
2392 file_id: FileId(
2393 1,
2394 ),
2395 full_range: 0..62,
2396 name: "Foo",
2397 kind: TRAIT_DEF,
2398 focus_range: Some(
2399 6..9,
2400 ),
2401 container_name: None,
2402 description: Some(
2403 "trait Foo",
2404 ),
2405 docs: None,
2406 },
2407 },
2408 ],
2409 ),
2410 ]
2411 "###);
2412 }
1313} 2413}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 51dc1f041..be9ab62c0 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -66,7 +66,7 @@ pub use crate::{
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 expand_macro::ExpandedMacro, 67 expand_macro::ExpandedMacro,
68 folding_ranges::{Fold, FoldKind}, 68 folding_ranges::{Fold, FoldKind},
69 hover::{HoverAction, HoverConfig, HoverResult}, 69 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 72 runnables::{Runnable, RunnableKind, TestId},
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
index f92a0aba5..f61c0daa5 100644
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -39,48 +39,48 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
39<span class="keyword">impl</span> <span class="struct">Foo</span> { 39<span class="keyword">impl</span> <span class="struct">Foo</span> {
40 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span>: <span class="builtin_type">bool</span> = <span class="bool_literal">true</span>; 40 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span>: <span class="builtin_type">bool</span> = <span class="bool_literal">true</span>;
41 41
42 <span class="comment">/// Constructs a new `Foo`.</span> 42 <span class="comment documentation">/// Constructs a new `Foo`.</span>
43 <span class="comment">///</span> 43 <span class="comment documentation">///</span>
44 <span class="comment">/// # Examples</span> 44 <span class="comment documentation">/// # Examples</span>
45 <span class="comment">///</span> 45 <span class="comment documentation">///</span>
46 <span class="comment">/// ```</span> 46 <span class="comment documentation">/// ```</span>
47 <span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span> 47 <span class="comment documentation">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span>
48 <span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="struct">Foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>(); 48 <span class="comment documentation">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="struct">Foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
49 <span class="comment">/// ```</span> 49 <span class="comment documentation">/// ```</span>
50 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="struct">Foo</span> { 50 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="struct">Foo</span> {
51 <span class="struct">Foo</span> { <span class="field">bar</span>: <span class="bool_literal">true</span> } 51 <span class="struct">Foo</span> { <span class="field">bar</span>: <span class="bool_literal">true</span> }
52 } 52 }
53 53
54 <span class="comment">/// `bar` method on `Foo`.</span> 54 <span class="comment documentation">/// `bar` method on `Foo`.</span>
55 <span class="comment">///</span> 55 <span class="comment documentation">///</span>
56 <span class="comment">/// # Examples</span> 56 <span class="comment documentation">/// # Examples</span>
57 <span class="comment">///</span> 57 <span class="comment documentation">///</span>
58 <span class="comment">/// ```</span> 58 <span class="comment documentation">/// ```</span>
59 <span class="comment">/// </span><span class="keyword">use</span> <span class="module">x</span>::<span class="module">y</span>; 59 <span class="comment documentation">/// </span><span class="keyword">use</span> <span class="module">x</span>::<span class="module">y</span>;
60 <span class="comment">///</span> 60 <span class="comment documentation">///</span>
61 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>(); 61 <span class="comment documentation">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
62 <span class="comment">///</span> 62 <span class="comment documentation">///</span>
63 <span class="comment">/// </span><span class="comment">// calls bar on foo</span> 63 <span class="comment documentation">/// </span><span class="comment">// calls bar on foo</span>
64 <span class="comment">/// </span><span class="macro">assert!</span>(foo.bar()); 64 <span class="comment documentation">/// </span><span class="macro">assert!</span>(foo.bar());
65 <span class="comment">///</span> 65 <span class="comment documentation">///</span>
66 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">bar</span> = <span class="variable">foo</span>.<span class="field">bar</span> || <span class="struct">Foo</span>::<span class="constant">bar</span>; 66 <span class="comment documentation">/// </span><span class="keyword">let</span> <span class="variable declaration">bar</span> = <span class="variable">foo</span>.<span class="field">bar</span> || <span class="struct">Foo</span>::<span class="constant">bar</span>;
67 <span class="comment">///</span> 67 <span class="comment documentation">///</span>
68 <span class="comment">/// </span><span class="comment">/* multi-line 68 <span class="comment documentation">/// </span><span class="comment">/* multi-line
69 </span><span class="comment">/// </span><span class="comment"> comment */</span> 69 </span><span class="comment documentation">/// </span><span class="comment"> comment */</span>
70 <span class="comment">///</span> 70 <span class="comment documentation">///</span>
71 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">multi_line_string</span> = <span class="string_literal">"Foo 71 <span class="comment documentation">/// </span><span class="keyword">let</span> <span class="variable declaration">multi_line_string</span> = <span class="string_literal">"Foo
72 </span><span class="comment">/// </span><span class="string_literal"> bar 72 </span><span class="comment documentation">/// </span><span class="string_literal"> bar
73 </span><span class="comment">/// </span><span class="string_literal"> "</span>; 73 </span><span class="comment documentation">/// </span><span class="string_literal"> "</span>;
74 <span class="comment">///</span> 74 <span class="comment documentation">///</span>
75 <span class="comment">/// ```</span> 75 <span class="comment documentation">/// ```</span>
76 <span class="comment">///</span> 76 <span class="comment documentation">///</span>
77 <span class="comment">/// ```rust,no_run</span> 77 <span class="comment documentation">/// ```rust,no_run</span>
78 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>(); 78 <span class="comment documentation">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>();
79 <span class="comment">/// ```</span> 79 <span class="comment documentation">/// ```</span>
80 <span class="comment">///</span> 80 <span class="comment documentation">///</span>
81 <span class="comment">/// ```sh</span> 81 <span class="comment documentation">/// ```sh</span>
82 <span class="comment">/// echo 1</span> 82 <span class="comment documentation">/// echo 1</span>
83 <span class="comment">/// ```</span> 83 <span class="comment documentation">/// ```</span>
84 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> { 84 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
85 <span class="bool_literal">true</span> 85 <span class="bool_literal">true</span>
86 } 86 }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 854b6cc6d..f8f790e59 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -489,7 +489,14 @@ fn highlight_element(
489 } 489 }
490 490
491 // Simple token-based highlighting 491 // Simple token-based highlighting
492 COMMENT => HighlightTag::Comment.into(), 492 COMMENT => {
493 let comment = element.into_token().and_then(ast::Comment::cast)?;
494 let h = HighlightTag::Comment;
495 match comment.kind().doc {
496 Some(_) => h | HighlightModifier::Documentation,
497 None => h.into(),
498 }
499 }
493 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), 500 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(),
494 ATTR => HighlightTag::Attribute.into(), 501 ATTR => HighlightTag::Attribute.into(),
495 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), 502 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index 929a5cc5c..415f24a6d 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -7,7 +7,10 @@ use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; 7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy; 8use stdx::SepBy;
9 9
10use crate::{call_info::ActiveParameter, Analysis, HighlightTag, HighlightedRange, RootDatabase}; 10use crate::{
11 call_info::ActiveParameter, Analysis, HighlightModifier, HighlightTag, HighlightedRange,
12 RootDatabase,
13};
11 14
12use super::HighlightedRangeStack; 15use super::HighlightedRangeStack;
13 16
@@ -118,7 +121,7 @@ pub(super) fn extract_doc_comments(
118 range.start(), 121 range.start(),
119 range.start() + TextSize::try_from(pos).unwrap(), 122 range.start() + TextSize::try_from(pos).unwrap(),
120 ), 123 ),
121 highlight: HighlightTag::Comment.into(), 124 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
122 binding_hash: None, 125 binding_hash: None,
123 }); 126 });
124 line_start += range.len() - TextSize::try_from(pos).unwrap(); 127 line_start += range.len() - TextSize::try_from(pos).unwrap();
@@ -164,6 +167,7 @@ pub(super) fn highlight_doc_comment(
164 h.range.start() + start_offset, 167 h.range.start() + start_offset,
165 h.range.end() + end_offset.unwrap_or(start_offset), 168 h.range.end() + end_offset.unwrap_or(start_offset),
166 ); 169 );
170
167 stack.add(h); 171 stack.add(h);
168 } 172 }
169 } 173 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 400d22fb6..93bbb4b4d 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -56,6 +56,7 @@ pub enum HighlightModifier {
56 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 56 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
57 /// not. 57 /// not.
58 Definition, 58 Definition,
59 Documentation,
59 Mutable, 60 Mutable,
60 Unsafe, 61 Unsafe,
61} 62}
@@ -108,6 +109,7 @@ impl HighlightModifier {
108 HighlightModifier::Attribute, 109 HighlightModifier::Attribute,
109 HighlightModifier::ControlFlow, 110 HighlightModifier::ControlFlow,
110 HighlightModifier::Definition, 111 HighlightModifier::Definition,
112 HighlightModifier::Documentation,
111 HighlightModifier::Mutable, 113 HighlightModifier::Mutable,
112 HighlightModifier::Unsafe, 114 HighlightModifier::Unsafe,
113 ]; 115 ];
@@ -117,6 +119,7 @@ impl HighlightModifier {
117 HighlightModifier::Attribute => "attribute", 119 HighlightModifier::Attribute => "attribute",
118 HighlightModifier::ControlFlow => "control", 120 HighlightModifier::ControlFlow => "control",
119 HighlightModifier::Definition => "declaration", 121 HighlightModifier::Definition => "declaration",
122 HighlightModifier::Documentation => "documentation",
120 HighlightModifier::Mutable => "mutable", 123 HighlightModifier::Mutable => "mutable",
121 HighlightModifier::Unsafe => "unsafe", 124 HighlightModifier::Unsafe => "unsafe",
122 } 125 }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0df7427cb..aa2c4ae15 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -296,6 +296,7 @@ impl Config {
296 set(value, "/hoverActions/implementations", &mut self.hover.implementations); 296 set(value, "/hoverActions/implementations", &mut self.hover.implementations);
297 set(value, "/hoverActions/run", &mut self.hover.run); 297 set(value, "/hoverActions/run", &mut self.hover.run);
298 set(value, "/hoverActions/debug", &mut self.hover.debug); 298 set(value, "/hoverActions/debug", &mut self.hover.debug);
299 set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
299 } else { 300 } else {
300 self.hover = HoverConfig::NO_ACTIONS; 301 self.hover = HoverConfig::NO_ACTIONS;
301 } 302 }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index b34b529b5..2d7e649d2 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -18,8 +18,8 @@ use lsp_types::{
18 TextDocumentIdentifier, Url, WorkspaceEdit, 18 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_ide::{ 20use ra_ide::{
21 FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, 21 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
22 SearchScope, TextEdit, 22 RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
23}; 23};
24use ra_prof::profile; 24use ra_prof::profile;
25use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
@@ -1150,6 +1150,23 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1150 } 1150 }
1151} 1151}
1152 1152
1153fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1154 let value = if snap.config.client_caps.location_link {
1155 let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1156 to_value(link).ok()?
1157 } else {
1158 let range = FileRange { file_id: nav.file_id(), range: nav.range() };
1159 let location = to_proto::location(snap, range).ok()?;
1160 to_value(location).ok()?
1161 };
1162
1163 Some(Command {
1164 title: nav.name().to_string(),
1165 command: "rust-analyzer.gotoLocation".into(),
1166 arguments: Some(vec![value]),
1167 })
1168}
1169
1153fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { 1170fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1154 lsp_ext::CommandLink { tooltip: Some(tooltip), command } 1171 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1155} 1172}
@@ -1180,13 +1197,13 @@ fn show_impl_command_link(
1180 None 1197 None
1181} 1198}
1182 1199
1183fn to_runnable_action( 1200fn runnable_action_links(
1184 snap: &GlobalStateSnapshot, 1201 snap: &GlobalStateSnapshot,
1185 file_id: FileId, 1202 file_id: FileId,
1186 runnable: Runnable, 1203 runnable: Runnable,
1187) -> Option<lsp_ext::CommandLinkGroup> { 1204) -> Option<lsp_ext::CommandLinkGroup> {
1188 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; 1205 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1189 if should_skip_target(&runnable, cargo_spec.as_ref()) { 1206 if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1190 return None; 1207 return None;
1191 } 1208 }
1192 1209
@@ -1208,6 +1225,26 @@ fn to_runnable_action(
1208 }) 1225 })
1209} 1226}
1210 1227
1228fn goto_type_action_links(
1229 snap: &GlobalStateSnapshot,
1230 nav_targets: &[HoverGotoTypeData],
1231) -> Option<lsp_ext::CommandLinkGroup> {
1232 if !snap.config.hover.goto_type_def || nav_targets.is_empty() {
1233 return None;
1234 }
1235
1236 Some(lsp_ext::CommandLinkGroup {
1237 title: Some("Go to ".into()),
1238 commands: nav_targets
1239 .iter()
1240 .filter_map(|it| {
1241 goto_location_command(snap, &it.nav)
1242 .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1243 })
1244 .collect(),
1245 })
1246}
1247
1211fn prepare_hover_actions( 1248fn prepare_hover_actions(
1212 snap: &GlobalStateSnapshot, 1249 snap: &GlobalStateSnapshot,
1213 file_id: FileId, 1250 file_id: FileId,
@@ -1221,7 +1258,8 @@ fn prepare_hover_actions(
1221 .iter() 1258 .iter()
1222 .filter_map(|it| match it { 1259 .filter_map(|it| match it {
1223 HoverAction::Implementaion(position) => show_impl_command_link(snap, position), 1260 HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
1224 HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()), 1261 HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1262 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1225 }) 1263 })
1226 .collect() 1264 .collect()
1227} 1265}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 055c97455..ec153097e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -331,6 +331,7 @@ fn semantic_token_type_and_modifiers(
331 let modifier = match modifier { 331 let modifier = match modifier {
332 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, 332 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
333 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 333 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
334 HighlightModifier::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
334 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 335 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW,
335 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 336 HighlightModifier::Mutable => semantic_tokens::MUTABLE,
336 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 337 HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index c0356344c..f2ff0e435 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,4 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2
3use std::{cell::Cell, fmt, time::Instant}; 2use std::{cell::Cell, fmt, time::Instant};
4 3
5#[inline(always)] 4#[inline(always)]
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 7dc721f7e..724606a3d 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -2,7 +2,7 @@
2//! 2//!
3//! Files which do not belong to any explicitly configured `FileSet` belong to 3//! Files which do not belong to any explicitly configured `FileSet` belong to
4//! the default `FileSet`. 4//! the default `FileSet`.
5use std::{cmp, fmt, iter}; 5use std::{fmt, iter};
6 6
7use paths::AbsPathBuf; 7use paths::AbsPathBuf;
8use rustc_hash::FxHashMap; 8use rustc_hash::FxHashMap;
@@ -44,6 +44,12 @@ pub struct FileSetConfig {
44 roots: Vec<(AbsPathBuf, usize)>, 44 roots: Vec<(AbsPathBuf, usize)>,
45} 45}
46 46
47impl Default for FileSetConfig {
48 fn default() -> Self {
49 FileSetConfig::builder().build()
50 }
51}
52
47impl FileSetConfig { 53impl FileSetConfig {
48 pub fn builder() -> FileSetConfigBuilder { 54 pub fn builder() -> FileSetConfigBuilder {
49 FileSetConfigBuilder::default() 55 FileSetConfigBuilder::default()
@@ -60,14 +66,19 @@ impl FileSetConfig {
60 self.n_file_sets 66 self.n_file_sets
61 } 67 }
62 fn classify(&self, path: &VfsPath) -> usize { 68 fn classify(&self, path: &VfsPath) -> usize {
63 for (root, idx) in self.roots.iter() { 69 let path = match path.as_path() {
64 if let Some(path) = path.as_path() { 70 Some(it) => it,
65 if path.starts_with(root) { 71 None => return self.len() - 1,
66 return *idx; 72 };
67 } 73 let idx = match self.roots.binary_search_by(|(p, _)| p.as_path().cmp(path)) {
68 } 74 Ok(it) => it,
75 Err(it) => it.saturating_sub(1),
76 };
77 if path.starts_with(&self.roots[idx].0) {
78 self.roots[idx].1
79 } else {
80 self.len() - 1
69 } 81 }
70 self.len() - 1
71 } 82 }
72} 83}
73 84
@@ -82,6 +93,9 @@ impl Default for FileSetConfigBuilder {
82} 93}
83 94
84impl FileSetConfigBuilder { 95impl FileSetConfigBuilder {
96 pub fn len(&self) -> usize {
97 self.roots.len()
98 }
85 pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) { 99 pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) {
86 self.roots.push(roots) 100 self.roots.push(roots)
87 } 101 }
@@ -93,7 +107,7 @@ impl FileSetConfigBuilder {
93 .enumerate() 107 .enumerate()
94 .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) 108 .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i)))
95 .collect(); 109 .collect();
96 roots.sort_by_key(|(path, _)| cmp::Reverse(path.to_string_lossy().len())); 110 roots.sort();
97 FileSetConfig { n_file_sets, roots } 111 FileSetConfig { n_file_sets, roots }
98 } 112 }
99} 113}
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 75ce61cf9..055219b0c 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -79,6 +79,9 @@ pub enum ChangeKind {
79} 79}
80 80
81impl Vfs { 81impl Vfs {
82 pub fn len(&self) -> usize {
83 self.data.len()
84 }
82 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { 85 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
83 self.interner.get(path).filter(|&it| self.get(it).is_some()) 86 self.interner.get(path).filter(|&it| self.get(it).is_some())
84 } 87 }
diff --git a/editors/code/package.json b/editors/code/package.json
index 3acc375f6..e6ceb235f 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -510,6 +510,11 @@
510 "type": "boolean", 510 "type": "boolean",
511 "default": true 511 "default": true
512 }, 512 },
513 "rust-analyzer.hoverActions.gotoTypeDef": {
514 "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
515 "type": "boolean",
516 "default": true
517 },
513 "rust-analyzer.linkedProjects": { 518 "rust-analyzer.linkedProjects": {
514 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format", 519 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format",
515 "type": "array", 520 "type": "array",
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 3e9c3aa0e..48a25495f 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -353,6 +353,20 @@ export function applyActionGroup(_ctx: Ctx): Cmd {
353 }; 353 };
354} 354}
355 355
356export function gotoLocation(ctx: Ctx): Cmd {
357 return async (locationLink: lc.LocationLink) => {
358 const client = ctx.client;
359 if (client) {
360 const uri = client.protocol2CodeConverter.asUri(locationLink.targetUri);
361 let range = client.protocol2CodeConverter.asRange(locationLink.targetSelectionRange);
362 // collapse the range to a cursor position
363 range = range.with({ end: range.start });
364
365 await vscode.window.showTextDocument(uri, { selection: range });
366 }
367 };
368}
369
356export function resolveCodeAction(ctx: Ctx): Cmd { 370export function resolveCodeAction(ctx: Ctx): Cmd {
357 const client = ctx.client; 371 const client = ctx.client;
358 return async (params: ra.ResolveCodeActionParams) => { 372 return async (params: ra.ResolveCodeActionParams) => {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index d8f0037d4..9591d4fe3 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -117,7 +117,7 @@ export class Config {
117 return { 117 return {
118 engine: this.get<string>("debug.engine"), 118 engine: this.get<string>("debug.engine"),
119 engineSettings: this.get<object>("debug.engineSettings"), 119 engineSettings: this.get<object>("debug.engineSettings"),
120 openUpDebugPane: this.get<boolean>("debug.openUpDebugPane"), 120 openDebugPane: this.get<boolean>("debug.openDebugPane"),
121 sourceFileMap: sourceFileMap 121 sourceFileMap: sourceFileMap
122 }; 122 };
123 } 123 }
@@ -135,6 +135,9 @@ export class Config {
135 return { 135 return {
136 enable: this.get<boolean>("hoverActions.enable"), 136 enable: this.get<boolean>("hoverActions.enable"),
137 implementations: this.get<boolean>("hoverActions.implementations"), 137 implementations: this.get<boolean>("hoverActions.implementations"),
138 run: this.get<boolean>("hoverActions.run"),
139 debug: this.get<boolean>("hoverActions.debug"),
140 gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"),
138 }; 141 };
139 } 142 }
140} 143}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index a0c9b3ab2..61c12dbe0 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -82,7 +82,7 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
82 } 82 }
83 83
84 debugOutput.clear(); 84 debugOutput.clear();
85 if (ctx.config.debug.openUpDebugPane) { 85 if (ctx.config.debug.openDebugPane) {
86 debugOutput.show(true); 86 debugOutput.show(true);
87 } 87 }
88 88
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index a92c676fa..270fbcb64 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -100,6 +100,7 @@ export async function activate(context: vscode.ExtensionContext) {
100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
101 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); 101 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
102 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 102 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
103 ctx.registerCommand('gotoLocation', commands.gotoLocation);
103 104
104 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 105 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
105 106