Add Go to Type Definition hover action.

This commit is contained in:
vsrs 2020-06-10 21:24:36 +03:00
parent 4d6c6a6b1e
commit c50157f330
8 changed files with 956 additions and 35 deletions

View file

@ -26,8 +26,8 @@ use hir_ty::{
autoderef, autoderef,
display::{HirDisplayError, HirFormatter}, display::{HirDisplayError, HirFormatter},
expr::ExprValidator, expr::ExprValidator,
method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, OpaqueTyId,
TyDefId, TypeCtor, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk,
}; };
use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_db::{CrateId, CrateName, Edition, FileId};
use ra_prof::profile; use ra_prof::profile;
@ -1380,6 +1380,87 @@ impl Type {
ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
} }
} }
/// Returns a flattened list of all the ADTs and Traits mentioned in the type
pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec<AdtOrTrait> {
fn push_new_item(item: AdtOrTrait, acc: &mut Vec<AdtOrTrait>) {
if !acc.contains(&item) {
acc.push(item);
}
}
fn push_bounds(
db: &dyn HirDatabase,
predicates: &[GenericPredicate],
acc: &mut Vec<AdtOrTrait>,
) {
for p in predicates.iter() {
match p {
GenericPredicate::Implemented(trait_ref) => {
push_new_item(Trait::from(trait_ref.trait_).into(), acc);
walk_types(db, &trait_ref.substs, acc);
}
GenericPredicate::Projection(_) => {}
GenericPredicate::Error => (),
}
}
}
fn walk_types<T: TypeWalk>(db: &dyn HirDatabase, tw: &T, acc: &mut Vec<AdtOrTrait>) {
tw.walk(&mut |ty| walk_type(db, ty, acc));
}
fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec<AdtOrTrait>) {
match ty.strip_references() {
Ty::Apply(ApplicationTy { ctor, parameters, .. }) => {
match ctor {
TypeCtor::Adt(adt_id) => push_new_item(Adt::from(*adt_id).into(), acc),
_ => (),
}
// adt params, tuples, etc...
walk_types(db, parameters, acc);
}
Ty::Dyn(predicates) => {
push_bounds(db, predicates, acc);
}
Ty::Placeholder(id) => {
let generic_params = db.generic_params(id.parent);
let param_data = &generic_params.types[id.local_id];
match param_data.provenance {
hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
let predicates: Vec<_> = db
.generic_predicates_for_param(*id)
.into_iter()
.map(|pred| pred.value.clone())
.collect();
push_bounds(db, &predicates, acc);
}
_ => (),
}
}
Ty::Opaque(opaque_ty) => {
let bounds = match opaque_ty.opaque_ty_id {
OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
let datas = db
.return_type_impl_traits(func)
.expect("impl trait id without data");
let data = (*datas)
.as_ref()
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
data.clone().subst(&opaque_ty.parameters)
}
};
push_bounds(db, &bounds.value, acc);
walk_types(db, &opaque_ty.parameters, acc);
}
_ => (),
}
}
let mut res: Vec<AdtOrTrait> = Vec::new(); // not a Set to preserve the order
walk_type(db, &self.ty.value, &mut res);
res
}
} }
impl HirDisplay for Type { impl HirDisplay for Type {
@ -1488,3 +1569,26 @@ pub trait HasVisibility {
vis.is_visible_from(db.upcast(), module.id) vis.is_visible_from(db.upcast(), module.id)
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AdtOrTrait {
Adt(Adt),
Trait(Trait),
}
impl_froms!(AdtOrTrait: Adt, Trait);
impl AdtOrTrait {
pub fn module(self, db: &dyn HirDatabase) -> Module {
match self {
AdtOrTrait::Adt(adt) => adt.module(db),
AdtOrTrait::Trait(trait_) => trait_.module(db),
}
}
pub fn name(self, db: &dyn HirDatabase) -> Name {
match self {
AdtOrTrait::Adt(adt) => adt.name(db),
AdtOrTrait::Trait(trait_) => trait_.name(db),
}
}
}

View file

@ -51,10 +51,10 @@ mod has_source;
pub use crate::{ pub use crate::{
code_model::{ code_model::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, Adt, AdtOrTrait, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate,
DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{original_range, PathResolution, Semantics, SemanticsScope}, semantics::{original_range, PathResolution, Semantics, SemanticsScope},

View file

@ -1052,10 +1052,10 @@ pub enum OpaqueTyId {
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ReturnTypeImplTraits { pub struct ReturnTypeImplTraits {
pub(crate) impl_traits: Vec<ReturnTypeImplTrait>, pub impl_traits: Vec<ReturnTypeImplTrait>,
} }
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct ReturnTypeImplTrait { pub struct ReturnTypeImplTrait {
pub(crate) bounds: Binders<Vec<GenericPredicate>>, pub bounds: Binders<Vec<GenericPredicate>>,
} }

View file

@ -321,6 +321,15 @@ impl ToNav for hir::Adt {
} }
} }
impl ToNav for hir::AdtOrTrait {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
match self {
hir::AdtOrTrait::Adt(adt) => adt.to_nav(db),
hir::AdtOrTrait::Trait(trait_) => trait_.to_nav(db),
}
}
}
impl ToNav for hir::AssocItem { impl ToNav for hir::AssocItem {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
match self { match self {

View file

@ -1,8 +1,8 @@
use std::iter::once; use std::iter::once;
use hir::{ use hir::{
Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, Adt, AdtOrTrait, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource,
ModuleDef, ModuleSource, Semantics, HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
}; };
use itertools::Itertools; use itertools::Itertools;
use ra_db::SourceDatabase; use ra_db::SourceDatabase;
@ -24,19 +24,21 @@ pub struct HoverConfig {
pub implementations: bool, pub implementations: bool,
pub run: bool, pub run: bool,
pub debug: bool, pub debug: bool,
pub goto_type_def: bool,
} }
impl Default for HoverConfig { impl Default for HoverConfig {
fn default() -> Self { fn default() -> Self {
Self { implementations: true, run: true, debug: true } Self { implementations: true, run: true, debug: true, goto_type_def: true }
} }
} }
impl HoverConfig { impl HoverConfig {
pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false }; pub const NO_ACTIONS: Self =
Self { implementations: false, run: false, debug: false, goto_type_def: false };
pub fn any(&self) -> bool { pub fn any(&self) -> bool {
self.implementations || self.runnable() self.implementations || self.runnable() || self.goto_type_def
} }
pub fn none(&self) -> bool { pub fn none(&self) -> bool {
@ -52,6 +54,13 @@ impl HoverConfig {
pub enum HoverAction { pub enum HoverAction {
Runnable(Runnable), Runnable(Runnable),
Implementaion(FilePosition), Implementaion(FilePosition),
GoToType(Vec<HoverGotoTypeData>),
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct HoverGotoTypeData {
pub mod_path: String,
pub nav: NavigationTarget,
} }
/// Contains the results when hovering over an item /// Contains the results when hovering over an item
@ -138,6 +147,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
res.push_action(action); res.push_action(action);
} }
if let Some(action) = goto_type_action(db, name_kind) {
res.push_action(action);
}
return Some(RangeInfo::new(range, res)); return Some(RangeInfo::new(range, res));
} }
} }
@ -218,6 +231,24 @@ fn runnable_action(
} }
} }
fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
match def {
Definition::Local(it) => {
let ty = it.ty(db);
let v = ty.flattened_type_items(db);
let targets = v.into_iter()
.map(|it| HoverGotoTypeData {
mod_path: adt_or_trait_mod_path(db, &it),
nav: it.to_nav(db),
})
.collect_vec();
Some(HoverAction::GoToType(targets))
}
_ => None,
}
}
fn hover_text( fn hover_text(
docs: Option<String>, docs: Option<String>,
desc: Option<String>, desc: Option<String>,
@ -248,25 +279,30 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
.map(|name| name.to_string()) .map(|name| name.to_string())
} }
fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String {
let mod_path = def.module(db).map(|module| { once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string))
once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) .chain(
.chain( module
module .path_to_root(db)
.path_to_root(db) .into_iter()
.into_iter() .rev()
.rev() .map(|it| it.name(db).map(|name| name.to_string())),
.map(|it| it.name(db).map(|name| name.to_string())), )
) .chain(once(name))
.chain(once(definition_owner_name(db, def))) .flatten()
.flatten() .join("::")
.join("::") }
});
mod_path fn adt_or_trait_mod_path(db: &RootDatabase, item: &AdtOrTrait) -> String {
determine_mod_path(db, item.module(db), Some(item.name(db).to_string()))
}
fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def)))
} }
fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
let mod_path = determine_mod_path(db, &def); let mod_path = definition_mod_path(db, &def);
return match def { return match def {
Definition::Macro(it) => { Definition::Macro(it) => {
let src = it.source(db); let src = it.source(db);
@ -1310,4 +1346,737 @@ fn func(foo: i32) { if true { <|>foo; }; }
] ]
"###); "###);
} }
#[test]
fn test_hover_struct_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /main.rs
struct S{ f1: u32 }
fn main() {
let s<|>t = S{ f1:0 };
}
",
&["S"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..19,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
7..8,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_generic_struct_has_goto_type_actions() {
let (_, actions) = check_hover_result(
"
//- /main.rs
struct Arg(u32);
struct S<T>{ f1: T }
fn main() {
let s<|>t = S{ f1:Arg(0) };
}
",
&["S<Arg>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 17..37,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
24..25,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "Arg",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..16,
name: "Arg",
kind: STRUCT_DEF,
focus_range: Some(
7..10,
),
container_name: None,
description: Some(
"struct Arg",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_generic_struct_has_flattened_goto_type_actions() {
let (_, actions) = check_hover_result(
"
//- /main.rs
struct Arg(u32);
struct S<T>{ f1: T }
fn main() {
let s<|>t = S{ f1: S{ f1: Arg(0) } };
}
",
&["S<S<Arg>>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 17..37,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
24..25,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "Arg",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..16,
name: "Arg",
kind: STRUCT_DEF,
focus_range: Some(
7..10,
),
container_name: None,
description: Some(
"struct Arg",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_tuple_has_goto_type_actions() {
let (_, actions) = check_hover_result(
"
//- /main.rs
struct A(u32);
struct B(u32);
mod M {
pub struct C(u32);
}
fn main() {
let s<|>t = (A(1), B(2), M::C(3) );
}
",
&["(A, B, C)"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "A",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..14,
name: "A",
kind: STRUCT_DEF,
focus_range: Some(
7..8,
),
container_name: None,
description: Some(
"struct A",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "B",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 15..29,
name: "B",
kind: STRUCT_DEF,
focus_range: Some(
22..23,
),
container_name: None,
description: Some(
"struct B",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "M::C",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 42..60,
name: "C",
kind: STRUCT_DEF,
focus_range: Some(
53..54,
),
container_name: None,
description: Some(
"pub struct C",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_return_impl_trait_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /main.rs
trait Foo {}
fn foo() -> impl Foo {}
fn main() {
let s<|>t = foo();
}
",
&["impl Foo"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..12,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_generic_return_impl_trait_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /main.rs
trait Foo<T> {}
struct S;
fn foo() -> impl Foo<S> {}
fn main() {
let s<|>t = foo();
}
",
&["impl Foo<S>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..15,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 16..25,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
23..24,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_arg_impl_trait_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /lib.rs
trait Foo {}
fn foo(ar<|>g: &impl Foo) {}
",
&["&impl Foo"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..12,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /lib.rs
trait Foo<T> {}
struct S {}
fn foo(ar<|>g: &impl Foo<S>) {}
",
&["&impl Foo<S>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..15,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 16..27,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
23..24,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_dyn_return_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /main.rs
trait Foo {}
struct S;
impl Foo for S {}
struct B<T>{}
fn foo() -> B<dyn Foo> {}
fn main() {
let s<|>t = foo();
}
",
&["B<dyn Foo>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "B",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 41..54,
name: "B",
kind: STRUCT_DEF,
focus_range: Some(
48..49,
),
container_name: None,
description: Some(
"struct B",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..12,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_dyn_arg_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /lib.rs
trait Foo {}
fn foo(ar<|>g: &dyn Foo) {}
",
&["&dyn Foo"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..12,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_generic_dyn_arg_has_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /lib.rs
trait Foo<T> {}
struct S {}
fn foo(ar<|>g: &dyn Foo<S>) {}
",
&["&dyn Foo<S>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "Foo",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..15,
name: "Foo",
kind: TRAIT_DEF,
focus_range: Some(
6..9,
),
container_name: None,
description: Some(
"trait Foo",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 16..27,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
23..24,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
],
),
]
"###);
}
#[test]
fn test_hover_arg_goto_type_action() {
let (_, actions) = check_hover_result(
"
//- /lib.rs
trait ImplTrait<T> {}
trait DynTrait<T> {}
struct B<T> {}
struct S {}
fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<S>>>) {}
",
&["&impl ImplTrait<B<dyn DynTrait<S>>>"],
);
assert_debug_snapshot!(actions,
@r###"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "ImplTrait",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 0..21,
name: "ImplTrait",
kind: TRAIT_DEF,
focus_range: Some(
6..15,
),
container_name: None,
description: Some(
"trait ImplTrait",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "S",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 58..69,
name: "S",
kind: STRUCT_DEF,
focus_range: Some(
65..66,
),
container_name: None,
description: Some(
"struct S",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "DynTrait",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 22..42,
name: "DynTrait",
kind: TRAIT_DEF,
focus_range: Some(
28..36,
),
container_name: None,
description: Some(
"trait DynTrait",
),
docs: None,
},
},
HoverGotoTypeData {
mod_path: "B",
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 43..57,
name: "B",
kind: STRUCT_DEF,
focus_range: Some(
50..51,
),
container_name: None,
description: Some(
"struct B",
),
docs: None,
},
},
],
),
]
"###);
}
} }

View file

@ -66,7 +66,7 @@ pub use crate::{
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
expand_macro::ExpandedMacro, expand_macro::ExpandedMacro,
folding_ranges::{Fold, FoldKind}, folding_ranges::{Fold, FoldKind},
hover::{HoverAction, HoverConfig, HoverResult}, hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
runnables::{Runnable, RunnableKind, TestId}, runnables::{Runnable, RunnableKind, TestId},

View file

@ -296,6 +296,7 @@ impl Config {
set(value, "/hoverActions/implementations", &mut self.hover.implementations); set(value, "/hoverActions/implementations", &mut self.hover.implementations);
set(value, "/hoverActions/run", &mut self.hover.run); set(value, "/hoverActions/run", &mut self.hover.run);
set(value, "/hoverActions/debug", &mut self.hover.debug); set(value, "/hoverActions/debug", &mut self.hover.debug);
set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
} else { } else {
self.hover = HoverConfig::NO_ACTIONS; self.hover = HoverConfig::NO_ACTIONS;
} }

View file

@ -18,8 +18,8 @@ use lsp_types::{
TextDocumentIdentifier, Url, WorkspaceEdit, TextDocumentIdentifier, Url, WorkspaceEdit,
}; };
use ra_ide::{ use ra_ide::{
FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
SearchScope, TextEdit, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
}; };
use ra_prof::profile; use ra_prof::profile;
use ra_project_model::TargetKind; use ra_project_model::TargetKind;
@ -1150,6 +1150,23 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
} }
} }
fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
let value = if snap.config.client_caps.location_link {
let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
to_value(link).ok()?
} else {
let range = FileRange { file_id: nav.file_id(), range: nav.range() };
let location = to_proto::location(snap, range).ok()?;
to_value(location).ok()?
};
Some(Command {
title: nav.name().to_string(),
command: "rust-analyzer.gotoLocation".into(),
arguments: Some(vec![value]),
})
}
fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
lsp_ext::CommandLink { tooltip: Some(tooltip), command } lsp_ext::CommandLink { tooltip: Some(tooltip), command }
} }
@ -1180,7 +1197,7 @@ fn show_impl_command_link(
None None
} }
fn to_runnable_action( fn runnable_action_links(
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
file_id: FileId, file_id: FileId,
runnable: Runnable, runnable: Runnable,
@ -1208,6 +1225,26 @@ fn to_runnable_action(
}) })
} }
fn goto_type_action_links(
snap: &GlobalStateSnapshot,
nav_targets: &[HoverGotoTypeData],
) -> Option<lsp_ext::CommandLinkGroup> {
if !snap.config.hover.goto_type_def || nav_targets.is_empty() {
return None;
}
Some(lsp_ext::CommandLinkGroup {
title: Some("Go to ".into()),
commands: nav_targets
.iter()
.filter_map(|it| {
goto_location_command(snap, &it.nav)
.map(|cmd| to_command_link(cmd, it.mod_path.clone()))
})
.collect(),
})
}
fn prepare_hover_actions( fn prepare_hover_actions(
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
file_id: FileId, file_id: FileId,
@ -1221,7 +1258,8 @@ fn prepare_hover_actions(
.iter() .iter()
.filter_map(|it| match it { .filter_map(|it| match it {
HoverAction::Implementaion(position) => show_impl_command_link(snap, position), HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()), HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
}) })
.collect() .collect()
} }