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_ide/src/display/navigation_target.rs | 9 + crates/ra_ide/src/hover.rs | 811 ++++++++++++++++++++++++- crates/ra_ide/src/lib.rs | 2 +- 3 files changed, 800 insertions(+), 22 deletions(-) (limited to 'crates/ra_ide') 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}, -- cgit v1.2.3