1
1
use either:: Either ;
2
2
use hir:: { AsAssocItem , HasAttrs , HasSource , HirDisplay , Semantics } ;
3
3
use ide_db:: {
4
- base_db:: SourceDatabase ,
4
+ base_db:: { FileRange , SourceDatabase } ,
5
5
defs:: { Definition , NameClass , NameRefClass } ,
6
6
helpers:: {
7
7
generated_lints:: { CLIPPY_LINTS , DEFAULT_LINTS , FEATURES } ,
@@ -12,8 +12,12 @@ use ide_db::{
12
12
use itertools:: Itertools ;
13
13
use stdx:: format_to;
14
14
use 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 ,
17
21
} ;
18
22
19
23
use crate :: {
@@ -69,17 +73,39 @@ pub struct HoverResult {
69
73
70
74
// Feature: Hover
71
75
//
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.
73
77
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
74
78
//
75
79
// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[]
76
80
pub ( crate ) fn hover (
77
81
db : & RootDatabase ,
78
- position : FilePosition ,
82
+ range : FileRange ,
79
83
config : & HoverConfig ,
80
84
) -> Option < RangeInfo < HoverResult > > {
81
85
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 ( ) } ;
83
109
let token = pick_best_token ( file. token_at_offset ( position. offset ) , |kind| match kind {
84
110
IDENT | INT_NUMBER | LIFETIME_IDENT | T ! [ self ] | T ! [ super ] | T ! [ crate ] => 3 ,
85
111
T ! [ '(' ] | T ! [ ')' ] => 2 ,
@@ -94,8 +120,8 @@ pub(crate) fn hover(
94
120
let mut range = None ;
95
121
let definition = match_ast ! {
96
122
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.
99
125
ast:: Name ( name) => NameClass :: classify( & sema, & name) . map( |class| match class {
100
126
NameClass :: Definition ( it) | NameClass :: ConstReference ( it) => it,
101
127
NameClass :: PatFieldShorthand { local_def, field_ref: _ } => Definition :: Local ( local_def) ,
@@ -193,6 +219,7 @@ pub(crate) fn hover(
193
219
} else {
194
220
ty. display ( db) . to_string ( ) . into ( )
195
221
} ;
222
+
196
223
let range = sema. original_range ( & node) . range ;
197
224
Some ( RangeInfo :: new ( range, res) )
198
225
}
@@ -530,7 +557,8 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
530
557
#[ cfg( test) ]
531
558
mod tests {
532
559
use expect_test:: { expect, Expect } ;
533
- use ide_db:: base_db:: FileLoader ;
560
+ use ide_db:: base_db:: { FileLoader , FileRange } ;
561
+ use syntax:: TextRange ;
534
562
535
563
use crate :: { fixture, hover:: HoverDocFormat , HoverConfig } ;
536
564
@@ -542,7 +570,7 @@ mod tests {
542
570
links_in_hover : true ,
543
571
documentation : Some ( HoverDocFormat :: Markdown ) ,
544
572
} ,
545
- position,
573
+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
546
574
)
547
575
. unwrap ( ) ;
548
576
assert ! ( hover. is_none( ) ) ;
@@ -556,7 +584,7 @@ mod tests {
556
584
links_in_hover : true ,
557
585
documentation : Some ( HoverDocFormat :: Markdown ) ,
558
586
} ,
559
- position,
587
+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
560
588
)
561
589
. unwrap ( )
562
590
. unwrap ( ) ;
@@ -576,7 +604,7 @@ mod tests {
576
604
links_in_hover : false ,
577
605
documentation : Some ( HoverDocFormat :: Markdown ) ,
578
606
} ,
579
- position,
607
+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
580
608
)
581
609
. unwrap ( )
582
610
. unwrap ( ) ;
@@ -596,7 +624,7 @@ mod tests {
596
624
links_in_hover : true ,
597
625
documentation : Some ( HoverDocFormat :: PlainText ) ,
598
626
} ,
599
- position,
627
+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
600
628
)
601
629
. unwrap ( )
602
630
. unwrap ( ) ;
@@ -616,13 +644,42 @@ mod tests {
616
644
links_in_hover : true ,
617
645
documentation : Some ( HoverDocFormat :: Markdown ) ,
618
646
} ,
619
- position,
647
+ FileRange { file_id : position. file_id , range : TextRange :: empty ( position . offset ) } ,
620
648
)
621
649
. unwrap ( )
622
650
. unwrap ( ) ;
623
651
expect. assert_debug_eq ( & hover. info . actions )
624
652
}
625
653
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
+
626
683
#[ test]
627
684
fn hover_shows_type_of_an_expression ( ) {
628
685
check (
@@ -3882,4 +3939,142 @@ struct Foo;
3882
3939
"# ] ] ,
3883
3940
) ;
3884
3941
}
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
+ }
3885
4080
}
0 commit comments