aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/hover.rs')
-rw-r--r--crates/ide/src/hover.rs268
1 files changed, 209 insertions, 59 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2737c900f..f2ad95cb6 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -70,7 +70,7 @@ impl HoverConfig {
70#[derive(Debug, Clone)] 70#[derive(Debug, Clone)]
71pub enum HoverAction { 71pub enum HoverAction {
72 Runnable(Runnable), 72 Runnable(Runnable),
73 Implementaion(FilePosition), 73 Implementation(FilePosition),
74 GoToType(Vec<HoverGotoTypeData>), 74 GoToType(Vec<HoverGotoTypeData>),
75} 75}
76 76
@@ -116,12 +116,13 @@ pub(crate) fn hover(
116 }; 116 };
117 if let Some(definition) = definition { 117 if let Some(definition) = definition {
118 if let Some(markup) = hover_for_definition(db, definition) { 118 if let Some(markup) = hover_for_definition(db, definition) {
119 let markup = markup.as_str();
119 let markup = if !markdown { 120 let markup = if !markdown {
120 remove_markdown(&markup.as_str()) 121 remove_markdown(markup)
121 } else if links_in_hover { 122 } else if links_in_hover {
122 rewrite_links(db, &markup.as_str(), &definition) 123 rewrite_links(db, markup, &definition)
123 } else { 124 } else {
124 remove_links(&markup.as_str()) 125 remove_links(markup)
125 }; 126 };
126 res.markup = Markup::from(markup); 127 res.markup = Markup::from(markup);
127 if let Some(action) = show_implementations_action(db, definition) { 128 if let Some(action) = show_implementations_action(db, definition) {
@@ -175,22 +176,24 @@ pub(crate) fn hover(
175 176
176fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 177fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
177 fn to_action(nav_target: NavigationTarget) -> HoverAction { 178 fn to_action(nav_target: NavigationTarget) -> HoverAction {
178 HoverAction::Implementaion(FilePosition { 179 HoverAction::Implementation(FilePosition {
179 file_id: nav_target.file_id, 180 file_id: nav_target.file_id,
180 offset: nav_target.focus_or_full_range().start(), 181 offset: nav_target.focus_or_full_range().start(),
181 }) 182 })
182 } 183 }
183 184
184 match def { 185 let adt = match def {
185 Definition::ModuleDef(it) => match it { 186 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
186 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)), 187 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
187 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)), 188 Definition::SelfType(it) => it.target_ty(db).as_adt(),
188 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
189 ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
190 _ => None,
191 },
192 _ => None, 189 _ => None,
190 }?;
191 match adt {
192 Adt::Struct(it) => it.try_to_nav(db),
193 Adt::Union(it) => it.try_to_nav(db),
194 Adt::Enum(it) => it.try_to_nav(db),
193 } 195 }
196 .map(to_action)
194} 197}
195 198
196fn runnable_action( 199fn runnable_action(
@@ -225,45 +228,46 @@ fn runnable_action(
225} 228}
226 229
227fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 230fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
228 match def { 231 let mut targets: Vec<ModuleDef> = Vec::new();
229 Definition::Local(it) => { 232 let mut push_new_def = |item: ModuleDef| {
230 let mut targets: Vec<ModuleDef> = Vec::new(); 233 if !targets.contains(&item) {
231 let mut push_new_def = |item: ModuleDef| { 234 targets.push(item);
232 if !targets.contains(&item) {
233 targets.push(item);
234 }
235 };
236
237 it.ty(db).walk(db, |t| {
238 if let Some(adt) = t.as_adt() {
239 push_new_def(adt.into());
240 } else if let Some(trait_) = t.as_dyn_trait() {
241 push_new_def(trait_.into());
242 } else if let Some(traits) = t.as_impl_traits(db) {
243 traits.into_iter().for_each(|it| push_new_def(it.into()));
244 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
245 push_new_def(trait_.into());
246 }
247 });
248
249 let targets = targets
250 .into_iter()
251 .filter_map(|it| {
252 Some(HoverGotoTypeData {
253 mod_path: render_path(
254 db,
255 it.module(db)?,
256 it.name(db).map(|name| name.to_string()),
257 ),
258 nav: it.try_to_nav(db)?,
259 })
260 })
261 .collect();
262
263 Some(HoverAction::GoToType(targets))
264 } 235 }
265 _ => None, 236 };
237
238 if let Definition::TypeParam(it) = def {
239 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
240 } else {
241 let ty = match def {
242 Definition::Local(it) => it.ty(db),
243 Definition::ConstParam(it) => it.ty(db),
244 _ => return None,
245 };
246
247 ty.walk(db, |t| {
248 if let Some(adt) = t.as_adt() {
249 push_new_def(adt.into());
250 } else if let Some(trait_) = t.as_dyn_trait() {
251 push_new_def(trait_.into());
252 } else if let Some(traits) = t.as_impl_traits(db) {
253 traits.into_iter().for_each(|it| push_new_def(it.into()));
254 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
255 push_new_def(trait_.into());
256 }
257 });
266 } 258 }
259
260 let targets = targets
261 .into_iter()
262 .filter_map(|it| {
263 Some(HoverGotoTypeData {
264 mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())),
265 nav: it.try_to_nav(db)?,
266 })
267 })
268 .collect();
269
270 Some(HoverAction::GoToType(targets))
267} 271}
268 272
269fn hover_markup( 273fn hover_markup(
@@ -370,10 +374,8 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 } 374 }
371 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 375 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
372 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), 376 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
373 Definition::TypeParam(_) | Definition::ConstParam(_) => { 377 Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))),
374 // FIXME: Hover for generic param 378 Definition::ConstParam(it) => from_def_source(db, it, None),
375 None
376 }
377 }; 379 };
378 380
379 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 381 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
@@ -1393,7 +1395,7 @@ fn bar() { fo<|>o(); }
1393 r"unsafe trait foo<|>() {}", 1395 r"unsafe trait foo<|>() {}",
1394 expect![[r#" 1396 expect![[r#"
1395 [ 1397 [
1396 Implementaion( 1398 Implementation(
1397 FilePosition { 1399 FilePosition {
1398 file_id: FileId( 1400 file_id: FileId(
1399 0, 1401 0,
@@ -2105,7 +2107,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2105 r#"trait foo<|>() {}"#, 2107 r#"trait foo<|>() {}"#,
2106 expect![[r#" 2108 expect![[r#"
2107 [ 2109 [
2108 Implementaion( 2110 Implementation(
2109 FilePosition { 2111 FilePosition {
2110 file_id: FileId( 2112 file_id: FileId(
2111 0, 2113 0,
@@ -2124,7 +2126,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2124 r"struct foo<|>() {}", 2126 r"struct foo<|>() {}",
2125 expect![[r#" 2127 expect![[r#"
2126 [ 2128 [
2127 Implementaion( 2129 Implementation(
2128 FilePosition { 2130 FilePosition {
2129 file_id: FileId( 2131 file_id: FileId(
2130 0, 2132 0,
@@ -2143,7 +2145,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2143 r#"union foo<|>() {}"#, 2145 r#"union foo<|>() {}"#,
2144 expect![[r#" 2146 expect![[r#"
2145 [ 2147 [
2146 Implementaion( 2148 Implementation(
2147 FilePosition { 2149 FilePosition {
2148 file_id: FileId( 2150 file_id: FileId(
2149 0, 2151 0,
@@ -2162,7 +2164,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2162 r"enum foo<|>() { A, B }", 2164 r"enum foo<|>() { A, B }",
2163 expect![[r#" 2165 expect![[r#"
2164 [ 2166 [
2165 Implementaion( 2167 Implementation(
2166 FilePosition { 2168 FilePosition {
2167 file_id: FileId( 2169 file_id: FileId(
2168 0, 2170 0,
@@ -2176,6 +2178,25 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2176 } 2178 }
2177 2179
2178 #[test] 2180 #[test]
2181 fn test_hover_self_has_impl_action() {
2182 check_actions(
2183 r#"struct foo where Self<|>:;"#,
2184 expect![[r#"
2185 [
2186 Implementation(
2187 FilePosition {
2188 file_id: FileId(
2189 0,
2190 ),
2191 offset: 7,
2192 },
2193 ),
2194 ]
2195 "#]],
2196 );
2197 }
2198
2199 #[test]
2179 fn test_hover_test_has_action() { 2200 fn test_hover_test_has_action() {
2180 check_actions( 2201 check_actions(
2181 r#" 2202 r#"
@@ -3064,6 +3085,71 @@ fn main() { let s<|>t = test().get(); }
3064 } 3085 }
3065 3086
3066 #[test] 3087 #[test]
3088 fn test_hover_const_param_has_goto_type_action() {
3089 check_actions(
3090 r#"
3091struct Bar;
3092struct Foo<const BAR: Bar>;
3093
3094impl<const BAR: Bar> Foo<BAR<|>> {}
3095"#,
3096 expect![[r#"
3097 [
3098 GoToType(
3099 [
3100 HoverGotoTypeData {
3101 mod_path: "test::Bar",
3102 nav: NavigationTarget {
3103 file_id: FileId(
3104 0,
3105 ),
3106 full_range: 0..11,
3107 focus_range: 7..10,
3108 name: "Bar",
3109 kind: Struct,
3110 description: "struct Bar",
3111 },
3112 },
3113 ],
3114 ),
3115 ]
3116 "#]],
3117 );
3118 }
3119
3120 #[test]
3121 fn test_hover_type_param_has_goto_type_action() {
3122 check_actions(
3123 r#"
3124trait Foo {}
3125
3126fn foo<T: Foo>(t: T<|>){}
3127"#,
3128 expect![[r#"
3129 [
3130 GoToType(
3131 [
3132 HoverGotoTypeData {
3133 mod_path: "test::Foo",
3134 nav: NavigationTarget {
3135 file_id: FileId(
3136 0,
3137 ),
3138 full_range: 0..12,
3139 focus_range: 6..9,
3140 name: "Foo",
3141 kind: Trait,
3142 description: "trait Foo",
3143 },
3144 },
3145 ],
3146 ),
3147 ]
3148 "#]],
3149 );
3150 }
3151
3152 #[test]
3067 fn hover_displays_normalized_crate_names() { 3153 fn hover_displays_normalized_crate_names() {
3068 check( 3154 check(
3069 r#" 3155 r#"
@@ -3257,4 +3343,68 @@ fn foo() {
3257 "#]], 3343 "#]],
3258 ); 3344 );
3259 } 3345 }
3346
3347 #[test]
3348 fn hover_type_param() {
3349 check(
3350 r#"
3351struct Foo<T>(T);
3352trait Copy {}
3353trait Clone {}
3354trait Sized {}
3355impl<T: Copy + Clone> Foo<T<|>> where T: Sized {}
3356"#,
3357 expect![[r#"
3358 *T*
3359
3360 ```rust
3361 T: Copy + Clone + Sized
3362 ```
3363 "#]],
3364 );
3365 check(
3366 r#"
3367struct Foo<T>(T);
3368impl<T> Foo<T<|>> {}
3369"#,
3370 expect![[r#"
3371 *T*
3372
3373 ```rust
3374 T
3375 ```
3376 "#]],
3377 );
3378 // lifetimes aren't being substituted yet
3379 check(
3380 r#"
3381struct Foo<T>(T);
3382impl<T: 'static> Foo<T<|>> {}
3383"#,
3384 expect![[r#"
3385 *T*
3386
3387 ```rust
3388 T: {error}
3389 ```
3390 "#]],
3391 );
3392 }
3393
3394 #[test]
3395 fn hover_const_param() {
3396 check(
3397 r#"
3398struct Foo<const LEN: usize>;
3399impl<const LEN: usize> Foo<LEN<|>> {}
3400"#,
3401 expect![[r#"
3402 *LEN*
3403
3404 ```rust
3405 const LEN: usize
3406 ```
3407 "#]],
3408 );
3409 }
3260} 3410}