diff options
Diffstat (limited to 'crates/ide/src/hover.rs')
-rw-r--r-- | crates/ide/src/hover.rs | 235 |
1 files changed, 198 insertions, 37 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 9a605b09d..ea45086ce 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use either::Either; | ||
1 | use hir::{ | 2 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, | 3 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, | 4 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
@@ -5,12 +6,12 @@ use hir::{ | |||
5 | use ide_db::{ | 6 | use ide_db::{ |
6 | base_db::SourceDatabase, | 7 | base_db::SourceDatabase, |
7 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
9 | helpers::FamousDefs, | ||
8 | RootDatabase, | 10 | RootDatabase, |
9 | }; | 11 | }; |
10 | use itertools::Itertools; | 12 | use itertools::Itertools; |
11 | use stdx::format_to; | 13 | use stdx::format_to; |
12 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 14 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
13 | use test_utils::mark; | ||
14 | 15 | ||
15 | use crate::{ | 16 | use crate::{ |
16 | display::{macro_label, ShortLabel, TryToNav}, | 17 | display::{macro_label, ShortLabel, TryToNav}, |
@@ -107,16 +108,14 @@ pub(crate) fn hover( | |||
107 | } | 108 | } |
108 | }; | 109 | }; |
109 | if let Some(definition) = definition { | 110 | if let Some(definition) = definition { |
110 | if let Some(markup) = hover_for_definition(db, definition) { | 111 | let famous_defs = match &definition { |
111 | let markup = markup.as_str(); | 112 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => { |
112 | let markup = if !markdown { | 113 | Some(FamousDefs(&sema, sema.scope(&node).krate())) |
113 | remove_markdown(markup) | 114 | } |
114 | } else if links_in_hover { | 115 | _ => None, |
115 | rewrite_links(db, markup, &definition) | 116 | }; |
116 | } else { | 117 | if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) { |
117 | remove_links(markup) | 118 | res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown); |
118 | }; | ||
119 | res.markup = Markup::from(markup); | ||
120 | if let Some(action) = show_implementations_action(db, definition) { | 119 | if let Some(action) = show_implementations_action(db, definition) { |
121 | res.actions.push(action); | 120 | res.actions.push(action); |
122 | } | 121 | } |
@@ -138,6 +137,9 @@ pub(crate) fn hover( | |||
138 | // don't highlight the entire parent node on comment hover | 137 | // don't highlight the entire parent node on comment hover |
139 | return None; | 138 | return None; |
140 | } | 139 | } |
140 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { | ||
141 | return res; | ||
142 | } | ||
141 | 143 | ||
142 | let node = token | 144 | let node = token |
143 | .ancestors() | 145 | .ancestors() |
@@ -191,8 +193,8 @@ fn runnable_action( | |||
191 | ModuleDef::Function(func) => { | 193 | ModuleDef::Function(func) => { |
192 | let src = func.source(sema.db)?; | 194 | let src = func.source(sema.db)?; |
193 | if src.file_id != file_id.into() { | 195 | if src.file_id != file_id.into() { |
194 | mark::hit!(hover_macro_generated_struct_fn_doc_comment); | 196 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); |
195 | mark::hit!(hover_macro_generated_struct_fn_doc_attr); | 197 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr); |
196 | return None; | 198 | return None; |
197 | } | 199 | } |
198 | 200 | ||
@@ -272,6 +274,24 @@ fn hover_markup( | |||
272 | } | 274 | } |
273 | } | 275 | } |
274 | 276 | ||
277 | fn process_markup( | ||
278 | db: &RootDatabase, | ||
279 | def: Definition, | ||
280 | markup: &Markup, | ||
281 | links_in_hover: bool, | ||
282 | markdown: bool, | ||
283 | ) -> Markup { | ||
284 | let markup = markup.as_str(); | ||
285 | let markup = if !markdown { | ||
286 | remove_markdown(markup) | ||
287 | } else if links_in_hover { | ||
288 | rewrite_links(db, markup, &def) | ||
289 | } else { | ||
290 | remove_links(markup) | ||
291 | }; | ||
292 | Markup::from(markup) | ||
293 | } | ||
294 | |||
275 | fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { | 295 | fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { |
276 | match def { | 296 | match def { |
277 | Definition::Field(f) => Some(f.parent_def(db).name(db)), | 297 | Definition::Field(f) => Some(f.parent_def(db).name(db)), |
@@ -304,7 +324,11 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | |||
304 | def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) | 324 | def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def))) |
305 | } | 325 | } |
306 | 326 | ||
307 | fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | 327 | fn hover_for_definition( |
328 | db: &RootDatabase, | ||
329 | def: Definition, | ||
330 | famous_defs: Option<&FamousDefs>, | ||
331 | ) -> Option<Markup> { | ||
308 | let mod_path = definition_mod_path(db, &def); | 332 | let mod_path = definition_mod_path(db, &def); |
309 | return match def { | 333 | return match def { |
310 | Definition::Macro(it) => { | 334 | Definition::Macro(it) => { |
@@ -339,9 +363,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
339 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), | 363 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), |
340 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), | 364 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), |
341 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), | 365 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), |
342 | ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())), | 366 | ModuleDef::BuiltinType(it) => famous_defs |
367 | .and_then(|fd| hover_for_builtin(fd, it)) | ||
368 | .or_else(|| Some(Markup::fenced_block(&it.name()))), | ||
343 | }, | 369 | }, |
344 | Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), | 370 | Definition::Local(it) => hover_for_local(it, db), |
345 | Definition::SelfType(impl_def) => { | 371 | Definition::SelfType(impl_def) => { |
346 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { | 372 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { |
347 | Adt::Struct(it) => from_def_source(db, it, mod_path), | 373 | Adt::Struct(it) => from_def_source(db, it, mod_path), |
@@ -380,11 +406,75 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
380 | } | 406 | } |
381 | } | 407 | } |
382 | 408 | ||
409 | fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> { | ||
410 | let ty = it.ty(db); | ||
411 | let ty = ty.display(db); | ||
412 | let is_mut = if it.is_mut(db) { "mut " } else { "" }; | ||
413 | let desc = match it.source(db).value { | ||
414 | Either::Left(ident) => { | ||
415 | let name = it.name(db).unwrap(); | ||
416 | let let_kw = if ident | ||
417 | .syntax() | ||
418 | .parent() | ||
419 | .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION) | ||
420 | { | ||
421 | "let " | ||
422 | } else { | ||
423 | "" | ||
424 | }; | ||
425 | format!("{}{}{}: {}", let_kw, is_mut, name, ty) | ||
426 | } | ||
427 | Either::Right(_) => format!("{}self: {}", is_mut, ty), | ||
428 | }; | ||
429 | hover_markup(None, Some(desc), None) | ||
430 | } | ||
431 | |||
432 | fn hover_for_keyword( | ||
433 | sema: &Semantics<RootDatabase>, | ||
434 | links_in_hover: bool, | ||
435 | markdown: bool, | ||
436 | token: &SyntaxToken, | ||
437 | ) -> Option<RangeInfo<HoverResult>> { | ||
438 | if !token.kind().is_keyword() { | ||
439 | return None; | ||
440 | } | ||
441 | let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate()); | ||
442 | // std exposes {}_keyword modules with docstrings on the root to document keywords | ||
443 | let keyword_mod = format!("{}_keyword", token.text()); | ||
444 | let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; | ||
445 | let docs = doc_owner.attrs(sema.db).docs()?; | ||
446 | let markup = process_markup( | ||
447 | sema.db, | ||
448 | Definition::ModuleDef(doc_owner.into()), | ||
449 | &hover_markup(Some(docs.into()), Some(token.text().into()), None)?, | ||
450 | links_in_hover, | ||
451 | markdown, | ||
452 | ); | ||
453 | Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() })) | ||
454 | } | ||
455 | |||
456 | fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> { | ||
457 | // std exposes prim_{} modules with docstrings on the root to document the builtins | ||
458 | let primitive_mod = format!("prim_{}", builtin.name()); | ||
459 | let doc_owner = find_std_module(famous_defs, &primitive_mod)?; | ||
460 | let docs = doc_owner.attrs(famous_defs.0.db).docs()?; | ||
461 | hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None) | ||
462 | } | ||
463 | |||
464 | fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> { | ||
465 | let db = famous_defs.0.db; | ||
466 | let std_crate = famous_defs.std()?; | ||
467 | let std_root_module = std_crate.root_module(db); | ||
468 | std_root_module | ||
469 | .children(db) | ||
470 | .find(|module| module.name(db).map_or(false, |module| module.to_string() == name)) | ||
471 | } | ||
472 | |||
383 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 473 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
384 | return tokens.max_by_key(priority); | 474 | return tokens.max_by_key(priority); |
385 | fn priority(n: &SyntaxToken) -> usize { | 475 | fn priority(n: &SyntaxToken) -> usize { |
386 | match n.kind() { | 476 | match n.kind() { |
387 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3, | 477 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3, |
388 | T!['('] | T![')'] => 2, | 478 | T!['('] | T![')'] => 2, |
389 | kind if kind.is_trivia() => 0, | 479 | kind if kind.is_trivia() => 0, |
390 | _ => 1, | 480 | _ => 1, |
@@ -508,7 +598,7 @@ fn main() { | |||
508 | *iter* | 598 | *iter* |
509 | 599 | ||
510 | ```rust | 600 | ```rust |
511 | Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> | 601 | let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> |
512 | ``` | 602 | ``` |
513 | "#]], | 603 | "#]], |
514 | ); | 604 | ); |
@@ -732,7 +822,7 @@ fn main() { | |||
732 | ``` | 822 | ``` |
733 | 823 | ||
734 | ```rust | 824 | ```rust |
735 | const foo: u32 = 123 | 825 | const foo: u32 |
736 | ``` | 826 | ``` |
737 | "#]], | 827 | "#]], |
738 | ); | 828 | ); |
@@ -765,7 +855,7 @@ fn main() { | |||
765 | *zz* | 855 | *zz* |
766 | 856 | ||
767 | ```rust | 857 | ```rust |
768 | Test<i32, u8> | 858 | let zz: Test<i32, u8> |
769 | ``` | 859 | ``` |
770 | "#]], | 860 | "#]], |
771 | ); | 861 | ); |
@@ -804,7 +894,7 @@ fn main() { let b$0ar = Some(12); } | |||
804 | *bar* | 894 | *bar* |
805 | 895 | ||
806 | ```rust | 896 | ```rust |
807 | Option<i32> | 897 | let bar: Option<i32> |
808 | ``` | 898 | ``` |
809 | "#]], | 899 | "#]], |
810 | ); | 900 | ); |
@@ -872,7 +962,7 @@ fn main() { | |||
872 | *foo* | 962 | *foo* |
873 | 963 | ||
874 | ```rust | 964 | ```rust |
875 | i32 | 965 | foo: i32 |
876 | ``` | 966 | ``` |
877 | "#]], | 967 | "#]], |
878 | ) | 968 | ) |
@@ -886,7 +976,7 @@ fn main() { | |||
886 | *foo* | 976 | *foo* |
887 | 977 | ||
888 | ```rust | 978 | ```rust |
889 | i32 | 979 | foo: i32 |
890 | ``` | 980 | ``` |
891 | "#]], | 981 | "#]], |
892 | ) | 982 | ) |
@@ -900,7 +990,7 @@ fn main() { | |||
900 | *foo* | 990 | *foo* |
901 | 991 | ||
902 | ```rust | 992 | ```rust |
903 | i32 | 993 | foo: i32 |
904 | ``` | 994 | ``` |
905 | "#]], | 995 | "#]], |
906 | ) | 996 | ) |
@@ -914,7 +1004,7 @@ fn main() { | |||
914 | *foo* | 1004 | *foo* |
915 | 1005 | ||
916 | ```rust | 1006 | ```rust |
917 | i32 | 1007 | foo: i32 |
918 | ``` | 1008 | ``` |
919 | "#]], | 1009 | "#]], |
920 | ) | 1010 | ) |
@@ -934,7 +1024,7 @@ fn main() { | |||
934 | *_x* | 1024 | *_x* |
935 | 1025 | ||
936 | ```rust | 1026 | ```rust |
937 | impl Deref<Target = u8> + DerefMut<Target = u8> | 1027 | _x: impl Deref<Target = u8> + DerefMut<Target = u8> |
938 | ``` | 1028 | ``` |
939 | "#]], | 1029 | "#]], |
940 | ) | 1030 | ) |
@@ -956,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); } | |||
956 | *foo_test* | 1046 | *foo_test* |
957 | 1047 | ||
958 | ```rust | 1048 | ```rust |
959 | Thing | 1049 | let foo_test: Thing |
960 | ``` | 1050 | ``` |
961 | "#]], | 1051 | "#]], |
962 | ) | 1052 | ) |
@@ -1015,7 +1105,7 @@ fn main() { | |||
1015 | ``` | 1105 | ``` |
1016 | 1106 | ||
1017 | ```rust | 1107 | ```rust |
1018 | const C: u32 = 1 | 1108 | const C: u32 |
1019 | ``` | 1109 | ``` |
1020 | "#]], | 1110 | "#]], |
1021 | ) | 1111 | ) |
@@ -1116,7 +1206,7 @@ fn y() { | |||
1116 | *x* | 1206 | *x* |
1117 | 1207 | ||
1118 | ```rust | 1208 | ```rust |
1119 | i32 | 1209 | let x: i32 |
1120 | ``` | 1210 | ``` |
1121 | "#]], | 1211 | "#]], |
1122 | ) | 1212 | ) |
@@ -1193,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1193 | *bar* | 1283 | *bar* |
1194 | 1284 | ||
1195 | ```rust | 1285 | ```rust |
1196 | u32 | 1286 | bar: u32 |
1197 | ``` | 1287 | ``` |
1198 | "#]], | 1288 | "#]], |
1199 | ); | 1289 | ); |
@@ -1211,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1211 | *bar* | 1301 | *bar* |
1212 | 1302 | ||
1213 | ```rust | 1303 | ```rust |
1214 | u32 | 1304 | bar: u32 |
1215 | ``` | 1305 | ``` |
1216 | "#]], | 1306 | "#]], |
1217 | ); | 1307 | ); |
@@ -2034,7 +2124,7 @@ pub fn fo$0o() {} | |||
2034 | 2124 | ||
2035 | #[test] | 2125 | #[test] |
2036 | fn test_hover_macro_generated_struct_fn_doc_comment() { | 2126 | fn test_hover_macro_generated_struct_fn_doc_comment() { |
2037 | mark::check!(hover_macro_generated_struct_fn_doc_comment); | 2127 | cov_mark::check!(hover_macro_generated_struct_fn_doc_comment); |
2038 | 2128 | ||
2039 | check( | 2129 | check( |
2040 | r#" | 2130 | r#" |
@@ -2072,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); } | |||
2072 | 2162 | ||
2073 | #[test] | 2163 | #[test] |
2074 | fn test_hover_macro_generated_struct_fn_doc_attr() { | 2164 | fn test_hover_macro_generated_struct_fn_doc_attr() { |
2075 | mark::check!(hover_macro_generated_struct_fn_doc_attr); | 2165 | cov_mark::check!(hover_macro_generated_struct_fn_doc_attr); |
2076 | 2166 | ||
2077 | check( | 2167 | check( |
2078 | r#" | 2168 | r#" |
@@ -3236,7 +3326,7 @@ fn main() { | |||
3236 | *f* | 3326 | *f* |
3237 | 3327 | ||
3238 | ```rust | 3328 | ```rust |
3239 | &i32 | 3329 | f: &i32 |
3240 | ``` | 3330 | ``` |
3241 | "#]], | 3331 | "#]], |
3242 | ); | 3332 | ); |
@@ -3255,7 +3345,7 @@ impl Foo { | |||
3255 | *self* | 3345 | *self* |
3256 | 3346 | ||
3257 | ```rust | 3347 | ```rust |
3258 | &Foo | 3348 | self: &Foo |
3259 | ``` | 3349 | ``` |
3260 | "#]], | 3350 | "#]], |
3261 | ); | 3351 | ); |
@@ -3275,7 +3365,7 @@ impl Foo { | |||
3275 | *self* | 3365 | *self* |
3276 | 3366 | ||
3277 | ```rust | 3367 | ```rust |
3278 | Arc<Foo> | 3368 | self: Arc<Foo> |
3279 | ``` | 3369 | ``` |
3280 | "#]], | 3370 | "#]], |
3281 | ); | 3371 | ); |
@@ -3471,7 +3561,7 @@ fn foo() { | |||
3471 | ``` | 3561 | ``` |
3472 | 3562 | ||
3473 | ```rust | 3563 | ```rust |
3474 | const FOO: usize = 3 | 3564 | const FOO: usize |
3475 | ``` | 3565 | ``` |
3476 | 3566 | ||
3477 | --- | 3567 | --- |
@@ -3496,4 +3586,75 @@ mod foo$0; | |||
3496 | "#]], | 3586 | "#]], |
3497 | ); | 3587 | ); |
3498 | } | 3588 | } |
3589 | |||
3590 | #[test] | ||
3591 | fn hover_self_in_use() { | ||
3592 | check( | ||
3593 | r#" | ||
3594 | //! This should not appear | ||
3595 | mod foo { | ||
3596 | /// But this should appear | ||
3597 | pub mod bar {} | ||
3598 | } | ||
3599 | use foo::bar::{self$0}; | ||
3600 | "#, | ||
3601 | expect![[r#" | ||
3602 | *self* | ||
3603 | |||
3604 | ```rust | ||
3605 | test::foo | ||
3606 | ``` | ||
3607 | |||
3608 | ```rust | ||
3609 | pub mod bar | ||
3610 | ``` | ||
3611 | |||
3612 | --- | ||
3613 | |||
3614 | But this should appear | ||
3615 | "#]], | ||
3616 | ) | ||
3617 | } | ||
3618 | |||
3619 | #[test] | ||
3620 | fn hover_keyword() { | ||
3621 | let ra_fixture = r#"//- /main.rs crate:main deps:std | ||
3622 | fn f() { retur$0n; }"#; | ||
3623 | let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
3624 | check( | ||
3625 | &fixture, | ||
3626 | expect![[r#" | ||
3627 | *return* | ||
3628 | |||
3629 | ```rust | ||
3630 | return | ||
3631 | ``` | ||
3632 | |||
3633 | --- | ||
3634 | |||
3635 | Docs for return_keyword | ||
3636 | "#]], | ||
3637 | ); | ||
3638 | } | ||
3639 | |||
3640 | #[test] | ||
3641 | fn hover_builtin() { | ||
3642 | let ra_fixture = r#"//- /main.rs crate:main deps:std | ||
3643 | cosnt _: &str$0 = ""; }"#; | ||
3644 | let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
3645 | check( | ||
3646 | &fixture, | ||
3647 | expect![[r#" | ||
3648 | *str* | ||
3649 | |||
3650 | ```rust | ||
3651 | str | ||
3652 | ``` | ||
3653 | |||
3654 | --- | ||
3655 | |||
3656 | Docs for prim_str | ||
3657 | "#]], | ||
3658 | ); | ||
3659 | } | ||
3499 | } | 3660 | } |