11use either:: Either ;
22use hir:: { AsAssocItem , HasAttrs , HasSource , HirDisplay , Semantics } ;
33use ide_db:: {
4- base_db:: SourceDatabase ,
4+ base_db:: { FileRange , SourceDatabase } ,
55 defs:: { Definition , NameClass , NameRefClass } ,
66 helpers:: {
77 generated_lints:: { CLIPPY_LINTS , DEFAULT_LINTS , FEATURES } ,
@@ -12,8 +12,12 @@ use ide_db::{
1212use itertools:: Itertools ;
1313use stdx:: format_to;
1414use syntax:: {
15- algo, ast, display:: fn_as_proc_macro_label, match_ast, AstNode , AstToken , Direction ,
16- SyntaxKind :: * , SyntaxToken , T ,
15+ algo:: { self , find_node_at_range} ,
16+ ast,
17+ display:: fn_as_proc_macro_label,
18+ match_ast, AstNode , AstToken , Direction ,
19+ SyntaxKind :: * ,
20+ SyntaxToken , T ,
1721} ;
1822
1923use crate :: {
@@ -69,17 +73,39 @@ pub struct HoverResult {
6973
7074// Feature: Hover
7175//
72- // Shows additional information, like type of an expression or documentation for definition when "focusing" code.
76+ // Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code.
7377// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
7478//
7579// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
7680pub ( crate ) fn hover (
7781 db : & RootDatabase ,
78- position : FilePosition ,
82+ range : FileRange ,
7983 config : & HoverConfig ,
8084) -> Option < RangeInfo < HoverResult > > {
8185 let sema = hir:: Semantics :: new ( db) ;
82- let file = sema. parse ( position. file_id ) . syntax ( ) . clone ( ) ;
86+ let file = sema. parse ( range. file_id ) . syntax ( ) . clone ( ) ;
87+
88+ // This means we're hovering over a range.
89+ if !range. range . is_empty ( ) {
90+ let expr = find_node_at_range :: < ast:: Expr > ( & file, range. range ) ?;
91+ let ty = sema. type_of_expr ( & expr) ?;
92+
93+ if ty. is_unknown ( ) {
94+ return None ;
95+ }
96+
97+ let mut res = HoverResult :: default ( ) ;
98+
99+ res. markup = if config. markdown ( ) {
100+ Markup :: fenced_block ( & ty. display ( db) )
101+ } else {
102+ ty. display ( db) . to_string ( ) . into ( )
103+ } ;
104+
105+ return Some ( RangeInfo :: new ( range. range , res) ) ;
106+ }
107+
108+ let position = FilePosition { file_id : range. file_id , offset : range. range . start ( ) } ;
83109 let token = pick_best_token ( file. token_at_offset ( position. offset ) , |kind| match kind {
84110 IDENT | INT_NUMBER | LIFETIME_IDENT | T ! [ self ] | T ! [ super ] | T ! [ crate ] => 3 ,
85111 T ! [ '(' ] | T ! [ ')' ] => 2 ,
@@ -94,8 +120,8 @@ pub(crate) fn hover(
94120 let mut range = None ;
95121 let definition = match_ast ! {
96122 match node {
97- // we don't use NameClass::referenced_or_defined here as we do not want to resolve
98- // field pattern shorthands to their definition
123+ // We don't use NameClass::referenced_or_defined here as we do not want to resolve
124+ // field pattern shorthands to their definition.
99125 ast:: Name ( name) => NameClass :: classify( & sema, & name) . map( |class| match class {
100126 NameClass :: Definition ( it) | NameClass :: ConstReference ( it) => it,
101127 NameClass :: PatFieldShorthand { local_def, field_ref: _ } => Definition :: Local ( local_def) ,
@@ -193,6 +219,7 @@ pub(crate) fn hover(
193219 } else {
194220 ty. display ( db) . to_string ( ) . into ( )
195221 } ;
222+
196223 let range = sema. original_range ( & node) . range ;
197224 Some ( RangeInfo :: new ( range, res) )
198225}
@@ -530,7 +557,8 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
530557#[ cfg( test) ]
531558mod tests {
532559 use expect_test:: { expect, Expect } ;
533- use ide_db:: base_db:: FileLoader ;
560+ use ide_db:: base_db:: { FileLoader , FileRange } ;
561+ use syntax:: TextRange ;
534562
535563 use crate :: { fixture, hover:: HoverDocFormat , HoverConfig } ;
536564
@@ -542,7 +570,7 @@ mod tests {
542570 links_in_hover : true ,
543571 documentation : Some ( HoverDocFormat :: Markdown ) ,
544572 } ,
545- position,
573+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
546574 )
547575 . unwrap ( ) ;
548576 assert ! ( hover. is_none( ) ) ;
@@ -556,7 +584,7 @@ mod tests {
556584 links_in_hover : true ,
557585 documentation : Some ( HoverDocFormat :: Markdown ) ,
558586 } ,
559- position,
587+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
560588 )
561589 . unwrap ( )
562590 . unwrap ( ) ;
@@ -576,7 +604,7 @@ mod tests {
576604 links_in_hover : false ,
577605 documentation : Some ( HoverDocFormat :: Markdown ) ,
578606 } ,
579- position,
607+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
580608 )
581609 . unwrap ( )
582610 . unwrap ( ) ;
@@ -596,7 +624,7 @@ mod tests {
596624 links_in_hover : true ,
597625 documentation : Some ( HoverDocFormat :: PlainText ) ,
598626 } ,
599- position,
627+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
600628 )
601629 . unwrap ( )
602630 . unwrap ( ) ;
@@ -616,13 +644,42 @@ mod tests {
616644 links_in_hover : true ,
617645 documentation : Some ( HoverDocFormat :: Markdown ) ,
618646 } ,
619- position,
647+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
620648 )
621649 . unwrap ( )
622650 . unwrap ( ) ;
623651 expect. assert_debug_eq ( & hover. info . actions )
624652 }
625653
654+ fn check_hover_range ( ra_fixture : & str , expect : Expect ) {
655+ let ( analysis, range) = fixture:: range ( ra_fixture) ;
656+ let hover = analysis
657+ . hover (
658+ & HoverConfig {
659+ links_in_hover : false ,
660+ documentation : Some ( HoverDocFormat :: Markdown ) ,
661+ } ,
662+ range,
663+ )
664+ . unwrap ( )
665+ . unwrap ( ) ;
666+ expect. assert_eq ( hover. info . markup . as_str ( ) )
667+ }
668+
669+ fn check_hover_range_no_results ( ra_fixture : & str ) {
670+ let ( analysis, range) = fixture:: range ( ra_fixture) ;
671+ let hover = analysis
672+ . hover (
673+ & HoverConfig {
674+ links_in_hover : false ,
675+ documentation : Some ( HoverDocFormat :: Markdown ) ,
676+ } ,
677+ range,
678+ )
679+ . unwrap ( ) ;
680+ assert ! ( hover. is_none( ) ) ;
681+ }
682+
626683 #[ test]
627684 fn hover_shows_type_of_an_expression ( ) {
628685 check (
@@ -3882,4 +3939,142 @@ struct Foo;
38823939 "# ] ] ,
38833940 ) ;
38843941 }
3942+
3943+ #[ test]
3944+ fn hover_range_math ( ) {
3945+ check_hover_range (
3946+ r#"
3947+ fn f() { let expr = $01 + 2 * 3$0 }
3948+ "# ,
3949+ expect ! [ [ r#"
3950+ ```rust
3951+ i32
3952+ ```"# ] ] ,
3953+ ) ;
3954+
3955+ check_hover_range (
3956+ r#"
3957+ fn f() { let expr = 1 $0+ 2 * $03 }
3958+ "# ,
3959+ expect ! [ [ r#"
3960+ ```rust
3961+ i32
3962+ ```"# ] ] ,
3963+ ) ;
3964+
3965+ check_hover_range (
3966+ r#"
3967+ fn f() { let expr = 1 + $02 * 3$0 }
3968+ "# ,
3969+ expect ! [ [ r#"
3970+ ```rust
3971+ i32
3972+ ```"# ] ] ,
3973+ ) ;
3974+ }
3975+
3976+ #[ test]
3977+ fn hover_range_arrays ( ) {
3978+ check_hover_range (
3979+ r#"
3980+ fn f() { let expr = $0[1, 2, 3, 4]$0 }
3981+ "# ,
3982+ expect ! [ [ r#"
3983+ ```rust
3984+ [i32; 4]
3985+ ```"# ] ] ,
3986+ ) ;
3987+
3988+ check_hover_range (
3989+ r#"
3990+ fn f() { let expr = [1, 2, $03, 4]$0 }
3991+ "# ,
3992+ expect ! [ [ r#"
3993+ ```rust
3994+ [i32; 4]
3995+ ```"# ] ] ,
3996+ ) ;
3997+
3998+ check_hover_range (
3999+ r#"
4000+ fn f() { let expr = [1, 2, $03$0, 4] }
4001+ "# ,
4002+ expect ! [ [ r#"
4003+ ```rust
4004+ i32
4005+ ```"# ] ] ,
4006+ ) ;
4007+ }
4008+
4009+ #[ test]
4010+ fn hover_range_functions ( ) {
4011+ check_hover_range (
4012+ r#"
4013+ fn f<T>(a: &[T]) { }
4014+ fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
4015+ "# ,
4016+ expect ! [ [ r#"
4017+ ```rust
4018+ fn f<i32>(&[i32])
4019+ ```"# ] ] ,
4020+ ) ;
4021+
4022+ check_hover_range (
4023+ r#"
4024+ fn f<T>(a: &[T]) { }
4025+ fn b() { f($0&[1, 2, 3, 4, 5]$0); }
4026+ "# ,
4027+ expect ! [ [ r#"
4028+ ```rust
4029+ &[i32; 5]
4030+ ```"# ] ] ,
4031+ ) ;
4032+ }
4033+
4034+ #[ test]
4035+ fn hover_range_shows_nothing_when_invalid ( ) {
4036+ check_hover_range_no_results (
4037+ r#"
4038+ fn f<T>(a: &[T]) { }
4039+ fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
4040+ "# ,
4041+ ) ;
4042+
4043+ check_hover_range_no_results (
4044+ r#"
4045+ fn f<T>$0(a: &[T]) { }
4046+ fn b() { f(&[1, 2, 3,$0 4, 5]); }
4047+ "# ,
4048+ ) ;
4049+
4050+ check_hover_range_no_results (
4051+ r#"
4052+ fn $0f() { let expr = [1, 2, 3, 4]$0 }
4053+ "# ,
4054+ ) ;
4055+ }
4056+
4057+ #[ test]
4058+ fn hover_range_shows_unit_for_statements ( ) {
4059+ check_hover_range (
4060+ r#"
4061+ fn f<T>(a: &[T]) { }
4062+ fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
4063+ "# ,
4064+ expect ! [ [ r#"
4065+ ```rust
4066+ ()
4067+ ```"# ] ] ,
4068+ ) ;
4069+
4070+ check_hover_range (
4071+ r#"
4072+ fn f() { let expr$0 = $0[1, 2, 3, 4] }
4073+ "# ,
4074+ expect ! [ [ r#"
4075+ ```rust
4076+ ()
4077+ ```"# ] ] ,
4078+ ) ;
4079+ }
38854080}
0 commit comments