Add Go to Type Definition
hover action.
This commit is contained in:
parent
4d6c6a6b1e
commit
c50157f330
@ -26,8 +26,8 @@ use hir_ty::{
|
||||
autoderef,
|
||||
display::{HirDisplayError, HirFormatter},
|
||||
expr::ExprValidator,
|
||||
method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty,
|
||||
TyDefId, TypeCtor,
|
||||
method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, OpaqueTyId,
|
||||
Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk,
|
||||
};
|
||||
use ra_db::{CrateId, CrateName, Edition, FileId};
|
||||
use ra_prof::profile;
|
||||
@ -1380,6 +1380,87 @@ impl Type {
|
||||
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 {
|
||||
@ -1488,3 +1569,26 @@ pub trait HasVisibility {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,10 +51,10 @@ mod has_source;
|
||||
|
||||
pub use crate::{
|
||||
code_model::{
|
||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency,
|
||||
DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs,
|
||||
HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct,
|
||||
Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
|
||||
Adt, AdtOrTrait, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate,
|
||||
CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
|
||||
GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
|
||||
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{original_range, PathResolution, Semantics, SemanticsScope},
|
||||
|
@ -1052,10 +1052,10 @@ pub enum OpaqueTyId {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ReturnTypeImplTraits {
|
||||
pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
|
||||
pub impl_traits: Vec<ReturnTypeImplTrait>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) struct ReturnTypeImplTrait {
|
||||
pub(crate) bounds: Binders<Vec<GenericPredicate>>,
|
||||
pub struct ReturnTypeImplTrait {
|
||||
pub bounds: Binders<Vec<GenericPredicate>>,
|
||||
}
|
||||
|
@ -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 {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
match self {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::iter::once;
|
||||
|
||||
use hir::{
|
||||
Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
|
||||
ModuleDef, ModuleSource, Semantics,
|
||||
Adt, AdtOrTrait, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource,
|
||||
HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use ra_db::SourceDatabase;
|
||||
@ -24,19 +24,21 @@ pub struct HoverConfig {
|
||||
pub implementations: bool,
|
||||
pub run: bool,
|
||||
pub debug: bool,
|
||||
pub goto_type_def: bool,
|
||||
}
|
||||
|
||||
impl Default for HoverConfig {
|
||||
fn default() -> Self {
|
||||
Self { implementations: true, run: true, debug: true }
|
||||
Self { implementations: true, run: true, debug: true, goto_type_def: true }
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
self.implementations || self.runnable()
|
||||
self.implementations || self.runnable() || self.goto_type_def
|
||||
}
|
||||
|
||||
pub fn none(&self) -> bool {
|
||||
@ -52,6 +54,13 @@ impl HoverConfig {
|
||||
pub enum HoverAction {
|
||||
Runnable(Runnable),
|
||||
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
|
||||
@ -138,6 +147,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
||||
res.push_action(action);
|
||||
}
|
||||
|
||||
if let Some(action) = goto_type_action(db, name_kind) {
|
||||
res.push_action(action);
|
||||
}
|
||||
|
||||
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(
|
||||
docs: Option<String>,
|
||||
desc: Option<String>,
|
||||
@ -248,25 +279,30 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
|
||||
.map(|name| name.to_string())
|
||||
}
|
||||
|
||||
fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
|
||||
let mod_path = def.module(db).map(|module| {
|
||||
once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string))
|
||||
.chain(
|
||||
module
|
||||
.path_to_root(db)
|
||||
.into_iter()
|
||||
.rev()
|
||||
.map(|it| it.name(db).map(|name| name.to_string())),
|
||||
)
|
||||
.chain(once(definition_owner_name(db, def)))
|
||||
.flatten()
|
||||
.join("::")
|
||||
});
|
||||
mod_path
|
||||
fn determine_mod_path(db: &RootDatabase, module: Module, name: Option<String>) -> String {
|
||||
once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string))
|
||||
.chain(
|
||||
module
|
||||
.path_to_root(db)
|
||||
.into_iter()
|
||||
.rev()
|
||||
.map(|it| it.name(db).map(|name| name.to_string())),
|
||||
)
|
||||
.chain(once(name))
|
||||
.flatten()
|
||||
.join("::")
|
||||
}
|
||||
|
||||
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> {
|
||||
let mod_path = determine_mod_path(db, &def);
|
||||
let mod_path = definition_mod_path(db, &def);
|
||||
return match def {
|
||||
Definition::Macro(it) => {
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ pub use crate::{
|
||||
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
|
||||
expand_macro::ExpandedMacro,
|
||||
folding_ranges::{Fold, FoldKind},
|
||||
hover::{HoverAction, HoverConfig, HoverResult},
|
||||
hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
|
||||
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
|
||||
references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
|
||||
runnables::{Runnable, RunnableKind, TestId},
|
||||
|
@ -296,6 +296,7 @@ impl Config {
|
||||
set(value, "/hoverActions/implementations", &mut self.hover.implementations);
|
||||
set(value, "/hoverActions/run", &mut self.hover.run);
|
||||
set(value, "/hoverActions/debug", &mut self.hover.debug);
|
||||
set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def);
|
||||
} else {
|
||||
self.hover = HoverConfig::NO_ACTIONS;
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ use lsp_types::{
|
||||
TextDocumentIdentifier, Url, WorkspaceEdit,
|
||||
};
|
||||
use ra_ide::{
|
||||
FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind,
|
||||
SearchScope, TextEdit,
|
||||
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
|
||||
RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
|
||||
};
|
||||
use ra_prof::profile;
|
||||
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 {
|
||||
lsp_ext::CommandLink { tooltip: Some(tooltip), command }
|
||||
}
|
||||
@ -1180,7 +1197,7 @@ fn show_impl_command_link(
|
||||
None
|
||||
}
|
||||
|
||||
fn to_runnable_action(
|
||||
fn runnable_action_links(
|
||||
snap: &GlobalStateSnapshot,
|
||||
file_id: FileId,
|
||||
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(
|
||||
snap: &GlobalStateSnapshot,
|
||||
file_id: FileId,
|
||||
@ -1221,7 +1258,8 @@ fn prepare_hover_actions(
|
||||
.iter()
|
||||
.filter_map(|it| match it {
|
||||
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()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user