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.rs235
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 @@
1use either::Either;
1use hir::{ 2use 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::{
5use ide_db::{ 6use 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};
10use itertools::Itertools; 12use itertools::Itertools;
11use stdx::format_to; 13use stdx::format_to;
12use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
13use test_utils::mark;
14 15
15use crate::{ 16use 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
277fn 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
275fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { 295fn 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
307fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { 327fn 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
409fn 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
432fn 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
456fn 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
464fn 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
383fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 473fn 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
3595mod foo {
3596 /// But this should appear
3597 pub mod bar {}
3598}
3599use 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
3622fn 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
3643cosnt _: &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}