From c50157f33025b6ff01809b975a3d12c0e43a0072 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 21:24:36 +0300 Subject: Add `Go to Type Definition` hover action. --- crates/ra_hir/src/code_model.rs | 108 +++- crates/ra_hir/src/lib.rs | 8 +- crates/ra_hir_ty/src/lib.rs | 6 +- crates/ra_ide/src/display/navigation_target.rs | 9 + crates/ra_ide/src/hover.rs | 811 ++++++++++++++++++++++++- crates/ra_ide/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 1 + crates/rust-analyzer/src/main_loop/handlers.rs | 46 +- 8 files changed, 956 insertions(+), 35 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 1a9f6cc76..c22eb451b 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -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 { + fn push_new_item(item: AdtOrTrait, acc: &mut Vec) { + if !acc.contains(&item) { + acc.push(item); + } + } + + fn push_bounds( + db: &dyn HirDatabase, + predicates: &[GenericPredicate], + acc: &mut Vec, + ) { + 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(db: &dyn HirDatabase, tw: &T, acc: &mut Vec) { + tw.walk(&mut |ty| walk_type(db, ty, acc)); + } + + fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { + 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 = 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), + } + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3364a822f..eded039e4 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -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}, diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2b9372b4b..9d4d6aaa4 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -1052,10 +1052,10 @@ pub enum OpaqueTyId { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Vec, + pub impl_traits: Vec, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct ReturnTypeImplTrait { - pub(crate) bounds: Binders>, +pub struct ReturnTypeImplTrait { + pub bounds: Binders>, } diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index c7bb1e69f..325b247bb 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -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 { diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index ad78b7671..c434e5c8b 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -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), +} + +#[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 Option { + 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, desc: Option, @@ -248,25 +279,30 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option .map(|name| name.to_string()) } -fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option { - 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 { + 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 { + 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 { - 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{ f1: T } + + fn main() { + let s<|>t = S{ f1:Arg(0) }; + } + ", + &["S"], + ); + 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{ f1: T } + + fn main() { + let s<|>t = S{ f1: S{ f1: Arg(0) } }; + } + ", + &["S>"], + ); + 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 {} + struct S; + + 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..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 {} + struct S {} + 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..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{} + + fn foo() -> B {} + + fn main() { + let s<|>t = foo(); + } + ", + &["B"], + ); + 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 {} + struct S {} + 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..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 {} + trait DynTrait {} + struct B {} + struct S {} + + fn foo(a<|>rg: &impl ImplTrait>>) {} + ", + &["&impl ImplTrait>>"], + ); + 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, + }, + }, + ], + ), + ] + "###); + } } 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::{ 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}, 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 { 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; } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f3868bf17..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::{ 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 { + 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 { + 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() } -- cgit v1.2.3