@@ -5,11 +5,12 @@ use hir::{
5
5
use ide_db:: {
6
6
base_db:: SourceDatabase ,
7
7
defs:: { Definition , NameClass , NameRefClass } ,
8
+ helpers:: FamousDefs ,
8
9
RootDatabase ,
9
10
} ;
10
11
use itertools:: Itertools ;
11
12
use stdx:: format_to;
12
- use syntax:: { ast, match_ast, AstNode , SyntaxKind :: * , SyntaxToken , TokenAtOffset , T } ;
13
+ use syntax:: { ast, match_ast, AstNode , SyntaxKind :: * , SyntaxNode , SyntaxToken , TokenAtOffset , T } ;
13
14
use test_utils:: mark;
14
15
15
16
use crate :: {
@@ -107,16 +108,8 @@ pub(crate) fn hover(
107
108
}
108
109
} ;
109
110
if let Some ( definition) = definition {
110
- if let Some ( markup) = hover_for_definition ( db, definition) {
111
- let markup = markup. as_str ( ) ;
112
- let markup = if !markdown {
113
- remove_markdown ( markup)
114
- } else if links_in_hover {
115
- rewrite_links ( db, markup, & definition)
116
- } else {
117
- remove_links ( markup)
118
- } ;
119
- res. markup = Markup :: from ( markup) ;
111
+ if let Some ( markup) = hover_for_definition ( & sema, definition, & node) {
112
+ res. markup = process_markup ( sema. db , definition, & markup, links_in_hover, markdown) ;
120
113
if let Some ( action) = show_implementations_action ( db, definition) {
121
114
res. actions . push ( action) ;
122
115
}
@@ -138,6 +131,9 @@ pub(crate) fn hover(
138
131
// don't highlight the entire parent node on comment hover
139
132
return None ;
140
133
}
134
+ if let res @ Some ( _) = hover_for_keyword ( & sema, links_in_hover, markdown, & token) {
135
+ return res;
136
+ }
141
137
142
138
let node = token
143
139
. ancestors ( )
@@ -272,6 +268,24 @@ fn hover_markup(
272
268
}
273
269
}
274
270
271
+ fn process_markup (
272
+ db : & RootDatabase ,
273
+ def : Definition ,
274
+ markup : & Markup ,
275
+ links_in_hover : bool ,
276
+ markdown : bool ,
277
+ ) -> Markup {
278
+ let markup = markup. as_str ( ) ;
279
+ let markup = if !markdown {
280
+ remove_markdown ( markup)
281
+ } else if links_in_hover {
282
+ rewrite_links ( db, markup, & def)
283
+ } else {
284
+ remove_links ( markup)
285
+ } ;
286
+ Markup :: from ( markup)
287
+ }
288
+
275
289
fn definition_owner_name ( db : & RootDatabase , def : & Definition ) -> Option < String > {
276
290
match def {
277
291
Definition :: Field ( f) => Some ( f. parent_def ( db) . name ( db) ) ,
@@ -304,7 +318,12 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
304
318
def. module ( db) . map ( |module| render_path ( db, module, definition_owner_name ( db, def) ) )
305
319
}
306
320
307
- fn hover_for_definition ( db : & RootDatabase , def : Definition ) -> Option < Markup > {
321
+ fn hover_for_definition (
322
+ sema : & Semantics < RootDatabase > ,
323
+ def : Definition ,
324
+ node : & SyntaxNode ,
325
+ ) -> Option < Markup > {
326
+ let db = sema. db ;
308
327
let mod_path = definition_mod_path ( db, & def) ;
309
328
return match def {
310
329
Definition :: Macro ( it) => {
@@ -339,7 +358,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
339
358
ModuleDef :: Static ( it) => from_def_source ( db, it, mod_path) ,
340
359
ModuleDef :: Trait ( it) => from_def_source ( db, it, mod_path) ,
341
360
ModuleDef :: TypeAlias ( it) => from_def_source ( db, it, mod_path) ,
342
- ModuleDef :: BuiltinType ( it) => Some ( Markup :: fenced_block ( & it. name ( ) ) ) ,
361
+ ModuleDef :: BuiltinType ( it) => {
362
+ hover_for_builtin ( sema, it, node) . or_else ( || Some ( Markup :: fenced_block ( & it. name ( ) ) ) )
363
+ }
343
364
} ,
344
365
Definition :: Local ( it) => Some ( Markup :: fenced_block ( & it. ty ( db) . display ( db) ) ) ,
345
366
Definition :: SelfType ( impl_def) => {
@@ -380,11 +401,58 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
380
401
}
381
402
}
382
403
404
+ fn hover_for_keyword (
405
+ sema : & Semantics < RootDatabase > ,
406
+ links_in_hover : bool ,
407
+ markdown : bool ,
408
+ token : & SyntaxToken ,
409
+ ) -> Option < RangeInfo < HoverResult > > {
410
+ if !token. kind ( ) . is_keyword ( ) {
411
+ return None ;
412
+ }
413
+ // std exposes {}_keyword modules with docstrings on the root to document keywords
414
+ let keyword_mod = format ! ( "{}_keyword" , token. text( ) ) ;
415
+ let doc_owner = find_std_module ( sema, & token. parent ( ) , & keyword_mod) ?;
416
+ let docs = doc_owner. attrs ( sema. db ) . docs ( ) ?;
417
+ let markup = process_markup (
418
+ sema. db ,
419
+ Definition :: ModuleDef ( doc_owner. into ( ) ) ,
420
+ & hover_markup ( Some ( docs. into ( ) ) , Some ( token. text ( ) . into ( ) ) , None ) ?,
421
+ links_in_hover,
422
+ markdown,
423
+ ) ;
424
+ Some ( RangeInfo :: new ( token. text_range ( ) , HoverResult { markup, actions : Default :: default ( ) } ) )
425
+ }
426
+
427
+ fn hover_for_builtin (
428
+ sema : & Semantics < RootDatabase > ,
429
+ builtin : hir:: BuiltinType ,
430
+ node : & SyntaxNode ,
431
+ ) -> Option < Markup > {
432
+ // std exposes prim_{} modules with docstrings on the root to document the builtins
433
+ let primitive_mod = format ! ( "prim_{}" , builtin. name( ) ) ;
434
+ let doc_owner = find_std_module ( sema, node, & primitive_mod) ?;
435
+ let docs = doc_owner. attrs ( sema. db ) . docs ( ) ?;
436
+ hover_markup ( Some ( docs. into ( ) ) , Some ( builtin. name ( ) . to_string ( ) ) , None )
437
+ }
438
+
439
+ fn find_std_module (
440
+ sema : & Semantics < RootDatabase > ,
441
+ node : & SyntaxNode ,
442
+ name : & str ,
443
+ ) -> Option < hir:: Module > {
444
+ let std_crate = FamousDefs ( sema, sema. scope ( node) . krate ( ) ) . std ( ) ?;
445
+ let std_root_module = std_crate. root_module ( sema. db ) ;
446
+ std_root_module
447
+ . children ( sema. db )
448
+ . find ( |module| module. name ( sema. db ) . map_or ( false , |module| module. to_string ( ) == name) )
449
+ }
450
+
383
451
fn pick_best ( tokens : TokenAtOffset < SyntaxToken > ) -> Option < SyntaxToken > {
384
452
return tokens. max_by_key ( priority) ;
385
453
fn priority ( n : & SyntaxToken ) -> usize {
386
454
match n. kind ( ) {
387
- IDENT | INT_NUMBER | LIFETIME_IDENT | T ! [ self ] => 3 ,
455
+ IDENT | INT_NUMBER | LIFETIME_IDENT | T ! [ self ] | T ! [ super ] | T ! [ crate ] => 3 ,
388
456
T ! [ '(' ] | T ! [ ')' ] => 2 ,
389
457
kind if kind. is_trivia ( ) => 0 ,
390
458
_ => 1 ,
@@ -3496,4 +3564,46 @@ mod foo$0;
3496
3564
"# ] ] ,
3497
3565
) ;
3498
3566
}
3567
+
3568
+ #[ test]
3569
+ fn hover_keyword ( ) {
3570
+ let ra_fixture = r#"//- /main.rs crate:main deps:std
3571
+ fn f() { retur$0n; }"# ;
3572
+ let fixture = format ! ( "{}\n {}" , ra_fixture, FamousDefs :: FIXTURE ) ;
3573
+ check (
3574
+ & fixture,
3575
+ expect ! [ [ r#"
3576
+ *return*
3577
+
3578
+ ```rust
3579
+ return
3580
+ ```
3581
+
3582
+ ---
3583
+
3584
+ Docs for return_keyword
3585
+ "# ] ] ,
3586
+ ) ;
3587
+ }
3588
+
3589
+ #[ test]
3590
+ fn hover_builtin ( ) {
3591
+ let ra_fixture = r#"//- /main.rs crate:main deps:std
3592
+ cosnt _: &str$0 = ""; }"# ;
3593
+ let fixture = format ! ( "{}\n {}" , ra_fixture, FamousDefs :: FIXTURE ) ;
3594
+ check (
3595
+ & fixture,
3596
+ expect ! [ [ r#"
3597
+ *str*
3598
+
3599
+ ```rust
3600
+ str
3601
+ ```
3602
+
3603
+ ---
3604
+
3605
+ Docs for prim_str
3606
+ "# ] ] ,
3607
+ ) ;
3608
+ }
3499
3609
}
0 commit comments