1- use std:: fmt;
1+ use std:: {
2+ fmt:: { self , Write } ,
3+ mem:: take,
4+ } ;
25
36use either:: Either ;
4- use hir:: { known, HasVisibility , HirDisplay , Semantics } ;
7+ use hir:: { known, HasVisibility , HirDisplay , HirWrite , ModuleDef , ModuleDefId , Semantics } ;
58use ide_db:: { base_db:: FileRange , famous_defs:: FamousDefs , RootDatabase } ;
69use itertools:: Itertools ;
10+ use stdx:: never;
711use syntax:: {
812 ast:: { self , AstNode } ,
913 match_ast, NodeOrToken , SyntaxNode , TextRange , TextSize ,
1014} ;
1115
12- use crate :: FileId ;
16+ use crate :: { navigation_target :: TryToNav , FileId } ;
1317
1418mod closing_brace;
1519mod implicit_static;
@@ -23,6 +27,7 @@ mod bind_pat;
2327
2428#[ derive( Clone , Debug , PartialEq , Eq ) ]
2529pub struct InlayHintsConfig {
30+ pub location_links : bool ,
2631 pub render_colons : bool ,
2732 pub type_hints : bool ,
2833 pub parameter_hints : bool ,
@@ -89,6 +94,7 @@ pub enum InlayTooltip {
8994 HoverOffset ( FileId , TextSize ) ,
9095}
9196
97+ #[ derive( Default ) ]
9298pub struct InlayHintLabel {
9399 pub parts : Vec < InlayHintLabelPart > ,
94100}
@@ -172,6 +178,104 @@ impl fmt::Debug for InlayHintLabelPart {
172178 }
173179}
174180
181+ #[ derive( Debug ) ]
182+ struct InlayHintLabelBuilder < ' a > {
183+ db : & ' a RootDatabase ,
184+ result : InlayHintLabel ,
185+ last_part : String ,
186+ location_link_enabled : bool ,
187+ location : Option < FileRange > ,
188+ }
189+
190+ impl fmt:: Write for InlayHintLabelBuilder < ' _ > {
191+ fn write_str ( & mut self , s : & str ) -> fmt:: Result {
192+ self . last_part . write_str ( s)
193+ }
194+ }
195+
196+ impl HirWrite for InlayHintLabelBuilder < ' _ > {
197+ fn start_location_link ( & mut self , def : ModuleDefId ) {
198+ if !self . location_link_enabled {
199+ return ;
200+ }
201+ if self . location . is_some ( ) {
202+ never ! ( "location link is already started" ) ;
203+ }
204+ self . make_new_part ( ) ;
205+ let Some ( location) = ModuleDef :: from ( def) . try_to_nav ( self . db ) else { return } ;
206+ let location =
207+ FileRange { file_id : location. file_id , range : location. focus_or_full_range ( ) } ;
208+ self . location = Some ( location) ;
209+ }
210+
211+ fn end_location_link ( & mut self ) {
212+ if !self . location_link_enabled {
213+ return ;
214+ }
215+ self . make_new_part ( ) ;
216+ }
217+ }
218+
219+ impl InlayHintLabelBuilder < ' _ > {
220+ fn make_new_part ( & mut self ) {
221+ self . result . parts . push ( InlayHintLabelPart {
222+ text : take ( & mut self . last_part ) ,
223+ linked_location : self . location . take ( ) ,
224+ } ) ;
225+ }
226+
227+ fn finish ( mut self ) -> InlayHintLabel {
228+ self . make_new_part ( ) ;
229+ self . result
230+ }
231+ }
232+
233+ fn label_of_ty (
234+ sema : & Semantics < ' _ , RootDatabase > ,
235+ desc_pat : & impl AstNode ,
236+ config : & InlayHintsConfig ,
237+ ty : hir:: Type ,
238+ ) -> Option < InlayHintLabel > {
239+ fn rec (
240+ sema : & Semantics < ' _ , RootDatabase > ,
241+ famous_defs : & FamousDefs < ' _ , ' _ > ,
242+ mut max_length : Option < usize > ,
243+ ty : hir:: Type ,
244+ label_builder : & mut InlayHintLabelBuilder < ' _ > ,
245+ ) {
246+ let iter_item_type = hint_iterator ( sema, & famous_defs, & ty) ;
247+ match iter_item_type {
248+ Some ( ty) => {
249+ const LABEL_START : & str = "impl Iterator<Item = " ;
250+ const LABEL_END : & str = ">" ;
251+
252+ max_length =
253+ max_length. map ( |len| len. saturating_sub ( LABEL_START . len ( ) + LABEL_END . len ( ) ) ) ;
254+
255+ label_builder. write_str ( LABEL_START ) . unwrap ( ) ;
256+ rec ( sema, famous_defs, max_length, ty, label_builder) ;
257+ label_builder. write_str ( LABEL_END ) . unwrap ( ) ;
258+ }
259+ None => {
260+ let _ = ty. display_truncated ( sema. db , max_length) . write_to ( label_builder) ;
261+ }
262+ } ;
263+ }
264+
265+ let krate = sema. scope ( desc_pat. syntax ( ) ) ?. krate ( ) ;
266+ let famous_defs = FamousDefs ( sema, krate) ;
267+ let mut label_builder = InlayHintLabelBuilder {
268+ db : sema. db ,
269+ last_part : String :: new ( ) ,
270+ location : None ,
271+ location_link_enabled : config. location_links ,
272+ result : InlayHintLabel :: default ( ) ,
273+ } ;
274+ rec ( sema, & famous_defs, config. max_length , ty, & mut label_builder) ;
275+ let r = label_builder. finish ( ) ;
276+ Some ( r)
277+ }
278+
175279// Feature: Inlay Hints
176280//
177281// rust-analyzer shows additional information inline with the source code.
@@ -224,7 +328,7 @@ pub(crate) fn inlay_hints(
224328
225329fn hints (
226330 hints : & mut Vec < InlayHint > ,
227- famous_defs @ FamousDefs ( sema, _) : & FamousDefs < ' _ , ' _ > ,
331+ FamousDefs ( sema, _) : & FamousDefs < ' _ , ' _ > ,
228332 config : & InlayHintsConfig ,
229333 file_id : FileId ,
230334 node : SyntaxNode ,
@@ -233,14 +337,14 @@ fn hints(
233337 match_ast ! {
234338 match node {
235339 ast:: Expr ( expr) => {
236- chaining:: hints( hints, sema, & famous_defs , config, file_id, & expr) ;
340+ chaining:: hints( hints, sema, config, file_id, & expr) ;
237341 adjustment:: hints( hints, sema, config, & expr) ;
238342 match expr {
239343 ast:: Expr :: CallExpr ( it) => param_name:: hints( hints, sema, config, ast:: Expr :: from( it) ) ,
240344 ast:: Expr :: MethodCallExpr ( it) => {
241345 param_name:: hints( hints, sema, config, ast:: Expr :: from( it) )
242346 }
243- ast:: Expr :: ClosureExpr ( it) => closure_ret:: hints( hints, sema, & famous_defs , config, file_id, it) ,
347+ ast:: Expr :: ClosureExpr ( it) => closure_ret:: hints( hints, sema, config, file_id, it) ,
244348 // We could show reborrows for all expressions, but usually that is just noise to the user
245349 // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
246350 // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
@@ -270,13 +374,12 @@ fn hints(
270374 } ;
271375}
272376
273- /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>` .
377+ /// Checks if the type is an Iterator from std::iter and returns its item type .
274378fn hint_iterator (
275379 sema : & Semantics < ' _ , RootDatabase > ,
276380 famous_defs : & FamousDefs < ' _ , ' _ > ,
277- config : & InlayHintsConfig ,
278381 ty : & hir:: Type ,
279- ) -> Option < String > {
382+ ) -> Option < hir :: Type > {
280383 let db = sema. db ;
281384 let strukt = ty. strip_references ( ) . as_adt ( ) ?;
282385 let krate = strukt. module ( db) . krate ( ) ;
@@ -299,21 +402,7 @@ fn hint_iterator(
299402 _ => None ,
300403 } ) ?;
301404 if let Some ( ty) = ty. normalize_trait_assoc_type ( db, & [ ] , assoc_type_item) {
302- const LABEL_START : & str = "impl Iterator<Item = " ;
303- const LABEL_END : & str = ">" ;
304-
305- let ty_display = hint_iterator ( sema, famous_defs, config, & ty)
306- . map ( |assoc_type_impl| assoc_type_impl. to_string ( ) )
307- . unwrap_or_else ( || {
308- ty. display_truncated (
309- db,
310- config
311- . max_length
312- . map ( |len| len. saturating_sub ( LABEL_START . len ( ) + LABEL_END . len ( ) ) ) ,
313- )
314- . to_string ( )
315- } ) ;
316- return Some ( format ! ( "{}{}{}" , LABEL_START , ty_display, LABEL_END ) ) ;
405+ return Some ( ty) ;
317406 }
318407 }
319408
@@ -336,6 +425,7 @@ mod tests {
336425 use super :: ClosureReturnTypeHints ;
337426
338427 pub ( super ) const DISABLED_CONFIG : InlayHintsConfig = InlayHintsConfig {
428+ location_links : false ,
339429 render_colons : false ,
340430 type_hints : false ,
341431 parameter_hints : false ,
@@ -350,14 +440,16 @@ mod tests {
350440 max_length : None ,
351441 closing_brace_hints_min_lines : None ,
352442 } ;
443+ pub ( super ) const DISABLED_CONFIG_WITH_LINKS : InlayHintsConfig =
444+ InlayHintsConfig { location_links : true , ..DISABLED_CONFIG } ;
353445 pub ( super ) const TEST_CONFIG : InlayHintsConfig = InlayHintsConfig {
354446 type_hints : true ,
355447 parameter_hints : true ,
356448 chaining_hints : true ,
357449 closure_return_type_hints : ClosureReturnTypeHints :: WithBlock ,
358450 binding_mode_hints : true ,
359451 lifetime_elision_hints : LifetimeElisionHints :: Always ,
360- ..DISABLED_CONFIG
452+ ..DISABLED_CONFIG_WITH_LINKS
361453 } ;
362454
363455 #[ track_caller]
0 commit comments