@@ -13,7 +13,7 @@ use itertools::Itertools;
13
13
use stdx:: format_to;
14
14
use syntax:: {
15
15
algo, ast, display:: fn_as_proc_macro_label, match_ast, AstNode , AstToken , Direction ,
16
- SyntaxKind :: * , SyntaxToken , T ,
16
+ SyntaxKind :: * , SyntaxNode , SyntaxToken , T ,
17
17
} ;
18
18
19
19
use crate :: {
@@ -54,6 +54,25 @@ pub enum HoverAction {
54
54
GoToType ( Vec < HoverGotoTypeData > ) ,
55
55
}
56
56
57
+ impl HoverAction {
58
+ fn goto_type_from_targets ( db : & RootDatabase , targets : Vec < hir:: ModuleDef > ) -> Self {
59
+ let targets = targets
60
+ . into_iter ( )
61
+ . filter_map ( |it| {
62
+ Some ( HoverGotoTypeData {
63
+ mod_path : render_path (
64
+ db,
65
+ it. module ( db) ?,
66
+ it. name ( db) . map ( |name| name. to_string ( ) ) ,
67
+ ) ,
68
+ nav : it. try_to_nav ( db) ?,
69
+ } )
70
+ } )
71
+ . collect ( ) ;
72
+ HoverAction :: GoToType ( targets)
73
+ }
74
+ }
75
+
57
76
#[ derive( Debug , Clone , Eq , PartialEq ) ]
58
77
pub struct HoverGotoTypeData {
59
78
pub mod_path : String ,
@@ -81,28 +100,10 @@ pub(crate) fn hover(
81
100
let sema = hir:: Semantics :: new ( db) ;
82
101
let file = sema. parse ( file_id) . syntax ( ) . clone ( ) ;
83
102
84
- let offset = if range. is_empty ( ) {
85
- range. start ( )
86
- } else {
87
- let expr = file. covering_element ( range) . ancestors ( ) . find_map ( |it| {
88
- match_ast ! {
89
- match it {
90
- ast:: Expr ( expr) => Some ( Either :: Left ( expr) ) ,
91
- ast:: Pat ( pat) => Some ( Either :: Right ( pat) ) ,
92
- _ => None ,
93
- }
94
- }
95
- } ) ?;
96
- return hover_type_info ( & sema, config, & expr) . map ( |it| {
97
- RangeInfo :: new (
98
- match expr {
99
- Either :: Left ( it) => it. syntax ( ) . text_range ( ) ,
100
- Either :: Right ( it) => it. syntax ( ) . text_range ( ) ,
101
- } ,
102
- it,
103
- )
104
- } ) ;
105
- } ;
103
+ if !range. is_empty ( ) {
104
+ return hover_ranged ( & file, range, & sema, config) ;
105
+ }
106
+ let offset = range. start ( ) ;
106
107
107
108
let token = pick_best_token ( file. token_at_offset ( offset) , |kind| match kind {
108
109
IDENT | INT_NUMBER | LIFETIME_IDENT | T ! [ self ] | T ! [ super ] | T ! [ crate ] => 3 ,
@@ -112,8 +113,8 @@ pub(crate) fn hover(
112
113
} ) ?;
113
114
let token = sema. descend_into_macros ( token) ;
114
115
116
+ let mut range_override = None ;
115
117
let node = token. parent ( ) ?;
116
- let mut range = None ;
117
118
let definition = match_ast ! {
118
119
match node {
119
120
// We don't use NameClass::referenced_or_defined here as we do not want to resolve
@@ -129,11 +130,13 @@ pub(crate) fn hover(
129
130
}
130
131
} ) ,
131
132
ast:: Lifetime ( lifetime) => NameClass :: classify_lifetime( & sema, & lifetime) . map_or_else(
132
- || NameRefClass :: classify_lifetime( & sema, & lifetime) . and_then( |class| match class {
133
- NameRefClass :: Definition ( it) => Some ( it) ,
134
- _ => None ,
135
- } ) ,
136
- |d| d. defined( ) ,
133
+ || {
134
+ NameRefClass :: classify_lifetime( & sema, & lifetime) . and_then( |class| match class {
135
+ NameRefClass :: Definition ( it) => Some ( it) ,
136
+ _ => None ,
137
+ } )
138
+ } ,
139
+ NameClass :: defined,
137
140
) ,
138
141
_ => {
139
142
if ast:: Comment :: cast( token. clone( ) ) . is_some( ) {
@@ -145,7 +148,7 @@ pub(crate) fn hover(
145
148
let mapped = doc_mapping. map( range) ?;
146
149
( mapped. file_id == file_id. into( ) && mapped. value. contains( offset) ) . then( ||( mapped. value, link, ns) )
147
150
} ) ?;
148
- range = Some ( idl_range) ;
151
+ range_override = Some ( idl_range) ;
149
152
Some ( match resolve_doc_path_for_def( db, def, & link, ns) ? {
150
153
Either :: Left ( it) => Definition :: ModuleDef ( it) ,
151
154
Either :: Right ( it) => Definition :: Macro ( it) ,
@@ -154,7 +157,7 @@ pub(crate) fn hover(
154
157
if let res@Some ( _) = try_hover_for_lint( & attr, & token) {
155
158
return res;
156
159
} else {
157
- range = Some ( token. text_range( ) ) ;
160
+ range_override = Some ( token. text_range( ) ) ;
158
161
try_resolve_derive_input_at( & sema, & attr, & token) . map( Definition :: Macro )
159
162
}
160
163
} else {
@@ -186,11 +189,11 @@ pub(crate) fn hover(
186
189
res. actions . push ( action) ;
187
190
}
188
191
189
- if let Some ( action) = goto_type_action ( db, definition) {
192
+ if let Some ( action) = goto_type_action_for_def ( db, definition) {
190
193
res. actions . push ( action) ;
191
194
}
192
195
193
- let range = range . unwrap_or_else ( || sema. original_range ( & node) . range ) ;
196
+ let range = range_override . unwrap_or_else ( || sema. original_range ( & node) . range ) ;
194
197
return Some ( RangeInfo :: new ( range, res) ) ;
195
198
}
196
199
}
@@ -199,6 +202,8 @@ pub(crate) fn hover(
199
202
return res;
200
203
}
201
204
205
+ // No definition below cursor, fall back to showing type hovers.
206
+
202
207
let node = token
203
208
. ancestors ( )
204
209
. take_while ( |it| !ast:: Item :: can_cast ( it. kind ( ) ) )
@@ -220,6 +225,30 @@ pub(crate) fn hover(
220
225
Some ( RangeInfo :: new ( range, res) )
221
226
}
222
227
228
+ fn hover_ranged (
229
+ file : & SyntaxNode ,
230
+ range : syntax:: TextRange ,
231
+ sema : & Semantics < RootDatabase > ,
232
+ config : & HoverConfig ,
233
+ ) -> Option < RangeInfo < HoverResult > > {
234
+ let expr = file. covering_element ( range) . ancestors ( ) . find_map ( |it| {
235
+ match_ast ! {
236
+ match it {
237
+ ast:: Expr ( expr) => Some ( Either :: Left ( expr) ) ,
238
+ ast:: Pat ( pat) => Some ( Either :: Right ( pat) ) ,
239
+ _ => None ,
240
+ }
241
+ }
242
+ } ) ?;
243
+ hover_type_info ( sema, config, & expr) . map ( |it| {
244
+ let range = match expr {
245
+ Either :: Left ( it) => it. syntax ( ) . text_range ( ) ,
246
+ Either :: Right ( it) => it. syntax ( ) . text_range ( ) ,
247
+ } ;
248
+ RangeInfo :: new ( range, it)
249
+ } )
250
+ }
251
+
223
252
fn hover_type_info (
224
253
sema : & Semantics < RootDatabase > ,
225
254
config : & HoverConfig ,
@@ -231,7 +260,16 @@ fn hover_type_info(
231
260
} ;
232
261
233
262
let mut res = HoverResult :: default ( ) ;
263
+ let mut targets: Vec < hir:: ModuleDef > = Vec :: new ( ) ;
264
+ let mut push_new_def = |item : hir:: ModuleDef | {
265
+ if !targets. contains ( & item) {
266
+ targets. push ( item) ;
267
+ }
268
+ } ;
269
+ walk_and_push_ty ( sema. db , & original, & mut push_new_def) ;
270
+
234
271
res. markup = if let Some ( adjusted_ty) = adjusted {
272
+ walk_and_push_ty ( sema. db , & adjusted_ty, & mut push_new_def) ;
235
273
let original = original. display ( sema. db ) . to_string ( ) ;
236
274
let adjusted = adjusted_ty. display ( sema. db ) . to_string ( ) ;
237
275
format ! (
@@ -250,6 +288,7 @@ fn hover_type_info(
250
288
original. display ( sema. db ) . to_string ( ) . into ( )
251
289
}
252
290
} ;
291
+ res. actions . push ( HoverAction :: goto_type_from_targets ( sema. db , targets) ) ;
253
292
Some ( res)
254
293
}
255
294
@@ -354,7 +393,7 @@ fn runnable_action(
354
393
}
355
394
}
356
395
357
- fn goto_type_action ( db : & RootDatabase , def : Definition ) -> Option < HoverAction > {
396
+ fn goto_type_action_for_def ( db : & RootDatabase , def : Definition ) -> Option < HoverAction > {
358
397
let mut targets: Vec < hir:: ModuleDef > = Vec :: new ( ) ;
359
398
let mut push_new_def = |item : hir:: ModuleDef | {
360
399
if !targets. contains ( & item) {
@@ -372,30 +411,28 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
372
411
_ => return None ,
373
412
} ;
374
413
375
- ty. walk ( db, |t| {
376
- if let Some ( adt) = t. as_adt ( ) {
377
- push_new_def ( adt. into ( ) ) ;
378
- } else if let Some ( trait_) = t. as_dyn_trait ( ) {
379
- push_new_def ( trait_. into ( ) ) ;
380
- } else if let Some ( traits) = t. as_impl_traits ( db) {
381
- traits. into_iter ( ) . for_each ( |it| push_new_def ( it. into ( ) ) ) ;
382
- } else if let Some ( trait_) = t. as_associated_type_parent_trait ( db) {
383
- push_new_def ( trait_. into ( ) ) ;
384
- }
385
- } ) ;
414
+ walk_and_push_ty ( db, & ty, & mut push_new_def) ;
386
415
}
387
416
388
- let targets = targets
389
- . into_iter ( )
390
- . filter_map ( |it| {
391
- Some ( HoverGotoTypeData {
392
- mod_path : render_path ( db, it. module ( db) ?, it. name ( db) . map ( |name| name. to_string ( ) ) ) ,
393
- nav : it. try_to_nav ( db) ?,
394
- } )
395
- } )
396
- . collect ( ) ;
417
+ Some ( HoverAction :: goto_type_from_targets ( db, targets) )
418
+ }
397
419
398
- Some ( HoverAction :: GoToType ( targets) )
420
+ fn walk_and_push_ty (
421
+ db : & RootDatabase ,
422
+ ty : & hir:: Type ,
423
+ push_new_def : & mut dyn FnMut ( hir:: ModuleDef ) ,
424
+ ) {
425
+ ty. walk ( db, |t| {
426
+ if let Some ( adt) = t. as_adt ( ) {
427
+ push_new_def ( adt. into ( ) ) ;
428
+ } else if let Some ( trait_) = t. as_dyn_trait ( ) {
429
+ push_new_def ( trait_. into ( ) ) ;
430
+ } else if let Some ( traits) = t. as_impl_traits ( db) {
431
+ traits. into_iter ( ) . for_each ( |it| push_new_def ( it. into ( ) ) ) ;
432
+ } else if let Some ( trait_) = t. as_associated_type_parent_trait ( db) {
433
+ push_new_def ( trait_. into ( ) ) ;
434
+ }
435
+ } ) ;
399
436
}
400
437
401
438
fn hover_markup ( docs : Option < String > , desc : String , mod_path : Option < String > ) -> Option < Markup > {
@@ -666,14 +703,14 @@ mod tests {
666
703
}
667
704
668
705
fn check_actions ( ra_fixture : & str , expect : Expect ) {
669
- let ( analysis, position) = fixture:: position ( ra_fixture) ;
706
+ let ( analysis, file_id , position) = fixture:: range_or_position ( ra_fixture) ;
670
707
let hover = analysis
671
708
. hover (
672
709
& HoverConfig {
673
710
links_in_hover : true ,
674
711
documentation : Some ( HoverDocFormat :: Markdown ) ,
675
712
} ,
676
- FileRange { file_id : position . file_id , range : TextRange :: empty ( position. offset ) } ,
713
+ FileRange { file_id, range : position. range_or_empty ( ) } ,
677
714
)
678
715
. unwrap ( )
679
716
. unwrap ( ) ;
@@ -4163,4 +4200,37 @@ fn foo() {
4163
4200
"# ] ] ,
4164
4201
) ;
4165
4202
}
4203
+
4204
+ #[ test]
4205
+ fn hover_range_shows_type_actions ( ) {
4206
+ check_actions (
4207
+ r#"
4208
+ struct Foo;
4209
+ fn foo() {
4210
+ let x: &Foo = $0&&&&&Foo$0;
4211
+ }
4212
+ "# ,
4213
+ expect ! [ [ r#"
4214
+ [
4215
+ GoToType(
4216
+ [
4217
+ HoverGotoTypeData {
4218
+ mod_path: "test::Foo",
4219
+ nav: NavigationTarget {
4220
+ file_id: FileId(
4221
+ 0,
4222
+ ),
4223
+ full_range: 0..11,
4224
+ focus_range: 7..10,
4225
+ name: "Foo",
4226
+ kind: Struct,
4227
+ description: "struct Foo",
4228
+ },
4229
+ },
4230
+ ],
4231
+ ),
4232
+ ]
4233
+ "# ] ] ,
4234
+ ) ;
4235
+ }
4166
4236
}
0 commit comments