11use std:: {
22 fmt:: { self , Write } ,
3+ hash:: { BuildHasher , BuildHasherDefault } ,
34 mem:: take,
45} ;
56
@@ -8,7 +9,7 @@ use hir::{
89 known, ClosureStyle , HasVisibility , HirDisplay , HirDisplayError , HirWrite , ModuleDef ,
910 ModuleDefId , Semantics ,
1011} ;
11- use ide_db:: { base_db:: FileRange , famous_defs:: FamousDefs , RootDatabase } ;
12+ use ide_db:: { base_db:: FileRange , famous_defs:: FamousDefs , FxHasher , RootDatabase } ;
1213use itertools:: Itertools ;
1314use smallvec:: { smallvec, SmallVec } ;
1415use stdx:: never;
@@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode {
116117 PreferPostfix ,
117118}
118119
119- #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
120+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
120121pub enum InlayKind {
121122 Adjustment ,
122123 BindingMode ,
@@ -132,7 +133,7 @@ pub enum InlayKind {
132133 RangeExclusive ,
133134}
134135
135- #[ derive( Debug ) ]
136+ #[ derive( Debug , Hash ) ]
136137pub enum InlayHintPosition {
137138 Before ,
138139 After ,
@@ -151,13 +152,23 @@ pub struct InlayHint {
151152 pub label : InlayHintLabel ,
152153 /// Text edit to apply when "accepting" this inlay hint.
153154 pub text_edit : Option < TextEdit > ,
154- pub needs_resolve : bool ,
155+ }
156+
157+ impl std:: hash:: Hash for InlayHint {
158+ fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
159+ self . range . hash ( state) ;
160+ self . position . hash ( state) ;
161+ self . pad_left . hash ( state) ;
162+ self . pad_right . hash ( state) ;
163+ self . kind . hash ( state) ;
164+ self . label . hash ( state) ;
165+ self . text_edit . is_some ( ) . hash ( state) ;
166+ }
155167}
156168
157169impl InlayHint {
158170 fn closing_paren_after ( kind : InlayKind , range : TextRange ) -> InlayHint {
159171 InlayHint {
160- needs_resolve : false ,
161172 range,
162173 kind,
163174 label : InlayHintLabel :: from ( ")" ) ,
@@ -167,9 +178,9 @@ impl InlayHint {
167178 pad_right : false ,
168179 }
169180 }
181+
170182 fn opening_paren_before ( kind : InlayKind , range : TextRange ) -> InlayHint {
171183 InlayHint {
172- needs_resolve : false ,
173184 range,
174185 kind,
175186 label : InlayHintLabel :: from ( "(" ) ,
@@ -179,15 +190,19 @@ impl InlayHint {
179190 pad_right : false ,
180191 }
181192 }
193+
194+ pub fn needs_resolve ( & self ) -> bool {
195+ self . text_edit . is_some ( ) || self . label . needs_resolve ( )
196+ }
182197}
183198
184- #[ derive( Debug ) ]
199+ #[ derive( Debug , Hash ) ]
185200pub enum InlayTooltip {
186201 String ( String ) ,
187202 Markdown ( String ) ,
188203}
189204
190- #[ derive( Default ) ]
205+ #[ derive( Default , Hash ) ]
191206pub struct InlayHintLabel {
192207 pub parts : SmallVec < [ InlayHintLabelPart ; 1 ] > ,
193208}
@@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel {
265280 }
266281}
267282
283+ #[ derive( Hash ) ]
268284pub struct InlayHintLabelPart {
269285 pub text : String ,
270286 /// Source location represented by this label part. The client will use this to fetch the part's
@@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> {
313329
314330impl HirWrite for InlayHintLabelBuilder < ' _ > {
315331 fn start_location_link ( & mut self , def : ModuleDefId ) {
316- if self . location . is_some ( ) {
317- never ! ( "location link is already started" ) ;
318- }
332+ never ! ( self . location. is_some( ) , "location link is already started" ) ;
319333 self . make_new_part ( ) ;
320334 let Some ( location) = ModuleDef :: from ( def) . try_to_nav ( self . db ) else { return } ;
321335 let location = location. call_site ( ) ;
@@ -425,11 +439,6 @@ fn ty_to_text_edit(
425439 Some ( builder. finish ( ) )
426440}
427441
428- pub enum RangeLimit {
429- Fixed ( TextRange ) ,
430- NearestParent ( TextSize ) ,
431- }
432-
433442// Feature: Inlay Hints
434443//
435444// rust-analyzer shows additional information inline with the source code.
@@ -451,7 +460,7 @@ pub enum RangeLimit {
451460pub ( crate ) fn inlay_hints (
452461 db : & RootDatabase ,
453462 file_id : FileId ,
454- range_limit : Option < RangeLimit > ,
463+ range_limit : Option < TextRange > ,
455464 config : & InlayHintsConfig ,
456465) -> Vec < InlayHint > {
457466 let _p = tracing:: span!( tracing:: Level :: INFO , "inlay_hints" ) . entered ( ) ;
@@ -466,38 +475,53 @@ pub(crate) fn inlay_hints(
466475
467476 let hints = |node| hints ( & mut acc, & famous_defs, config, file_id, node) ;
468477 match range_limit {
469- Some ( RangeLimit :: Fixed ( range) ) => match file. covering_element ( range) {
478+ Some ( range) => match file. covering_element ( range) {
470479 NodeOrToken :: Token ( _) => return acc,
471480 NodeOrToken :: Node ( n) => n
472481 . descendants ( )
473482 . filter ( |descendant| range. intersect ( descendant. text_range ( ) ) . is_some ( ) )
474483 . for_each ( hints) ,
475484 } ,
476- Some ( RangeLimit :: NearestParent ( position) ) => {
477- match file. token_at_offset ( position) . left_biased ( ) {
478- Some ( token) => {
479- if let Some ( parent_block) =
480- token. parent_ancestors ( ) . find_map ( ast:: BlockExpr :: cast)
481- {
482- parent_block. syntax ( ) . descendants ( ) . for_each ( hints)
483- } else if let Some ( parent_item) =
484- token. parent_ancestors ( ) . find_map ( ast:: Item :: cast)
485- {
486- parent_item. syntax ( ) . descendants ( ) . for_each ( hints)
487- } else {
488- return acc;
489- }
490- }
491- None => return acc,
492- }
493- }
494485 None => file. descendants ( ) . for_each ( hints) ,
495486 } ;
496487 }
497488
498489 acc
499490}
500491
492+ pub ( crate ) fn inlay_hints_resolve (
493+ db : & RootDatabase ,
494+ file_id : FileId ,
495+ position : TextSize ,
496+ hash : u64 ,
497+ config : & InlayHintsConfig ,
498+ ) -> Option < InlayHint > {
499+ let _p = tracing:: span!( tracing:: Level :: INFO , "inlay_hints" ) . entered ( ) ;
500+ let sema = Semantics :: new ( db) ;
501+ let file = sema. parse ( file_id) ;
502+ let file = file. syntax ( ) ;
503+
504+ let scope = sema. scope ( file) ?;
505+ let famous_defs = FamousDefs ( & sema, scope. krate ( ) ) ;
506+ let mut acc = Vec :: new ( ) ;
507+
508+ let hints = |node| hints ( & mut acc, & famous_defs, config, file_id, node) ;
509+ match file. token_at_offset ( position) . left_biased ( ) {
510+ Some ( token) => {
511+ if let Some ( parent_block) = token. parent_ancestors ( ) . find_map ( ast:: BlockExpr :: cast) {
512+ parent_block. syntax ( ) . descendants ( ) . for_each ( hints)
513+ } else if let Some ( parent_item) = token. parent_ancestors ( ) . find_map ( ast:: Item :: cast) {
514+ parent_item. syntax ( ) . descendants ( ) . for_each ( hints)
515+ } else {
516+ return None ;
517+ }
518+ }
519+ None => return None ,
520+ }
521+
522+ acc. into_iter ( ) . find ( |hint| BuildHasherDefault :: < FxHasher > :: default ( ) . hash_one ( hint) == hash)
523+ }
524+
501525fn hints (
502526 hints : & mut Vec < InlayHint > ,
503527 famous_defs @ FamousDefs ( sema, _) : & FamousDefs < ' _ , ' _ > ,
0 commit comments