Skip to content

Commit 74230e2

Browse files
committed
internal: Improve inlay hint tooltips
1 parent 018975b commit 74230e2

File tree

4 files changed

+71
-57
lines changed

4 files changed

+71
-57
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,19 @@ pub enum InlayKind {
5757
TypeHint,
5858
}
5959

60-
// FIXME: This should live somewhere more general
61-
#[derive(Debug)]
62-
pub enum RangeOrOffset {
63-
Range(TextRange),
64-
Offset(TextSize),
65-
}
66-
6760
#[derive(Debug)]
6861
pub struct InlayHint {
6962
pub range: TextRange,
7063
pub kind: InlayKind,
7164
pub label: String,
72-
pub hover_trigger: Option<RangeOrOffset>,
65+
pub tooltip: Option<InlayTooltip>,
66+
}
67+
68+
#[derive(Debug)]
69+
pub enum InlayTooltip {
70+
String(String),
71+
HoverRanged(FileId, TextRange),
72+
HoverOffset(FileId, TextSize),
7373
}
7474

7575
// Feature: Inlay Hints
@@ -109,7 +109,7 @@ pub(crate) fn inlay_hints(
109109

110110
let mut acc = Vec::new();
111111

112-
let hints = |node| hints(&mut acc, &sema, config, node);
112+
let hints = |node| hints(&mut acc, &sema, config, file_id, node);
113113
match range_limit {
114114
Some(FileRange { range, .. }) => match file.covering_element(range) {
115115
NodeOrToken::Token(_) => return acc,
@@ -128,24 +128,25 @@ fn hints(
128128
hints: &mut Vec<InlayHint>,
129129
sema: &Semantics<RootDatabase>,
130130
config: &InlayHintsConfig,
131+
file_id: FileId,
131132
node: SyntaxNode,
132133
) {
133134
let famous_defs = match sema.scope(&node) {
134135
Some(it) => FamousDefs(sema, it.krate()),
135136
None => return,
136137
};
137138

138-
closing_brace_hints(hints, sema, config, node.clone());
139+
closing_brace_hints(hints, sema, config, file_id, node.clone());
139140
match_ast! {
140141
match node {
141142
ast::Expr(expr) => {
142-
chaining_hints(hints, sema, &famous_defs, config, &expr);
143+
chaining_hints(hints, sema, &famous_defs, config, file_id, &expr);
143144
match expr {
144145
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
145146
ast::Expr::MethodCallExpr(it) => {
146147
param_name_hints(hints, sema, config, ast::Expr::from(it))
147148
}
148-
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it),
149+
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it),
149150
// We could show reborrows for all expressions, but usually that is just noise to the user
150151
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
151152
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
@@ -155,7 +156,7 @@ fn hints(
155156
ast::Pat(it) => {
156157
binding_mode_hints(hints, sema, config, &it);
157158
if let ast::Pat::IdentPat(it) = it {
158-
bind_pat_hints(hints, sema, config, &it);
159+
bind_pat_hints(hints, sema, config, file_id, &it);
159160
}
160161
Some(())
161162
},
@@ -169,6 +170,7 @@ fn closing_brace_hints(
169170
acc: &mut Vec<InlayHint>,
170171
sema: &Semantics<RootDatabase>,
171172
config: &InlayHintsConfig,
173+
file_id: FileId,
172174
node: SyntaxNode,
173175
) -> Option<()> {
174176
let min_lines = config.closing_brace_hints_min_lines?;
@@ -263,7 +265,7 @@ fn closing_brace_hints(
263265
range: closing_token.text_range(),
264266
kind: InlayKind::ClosingBraceHint,
265267
label,
266-
hover_trigger: name_offset.map(RangeOrOffset::Offset),
268+
tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)),
267269
});
268270

269271
None
@@ -282,7 +284,7 @@ fn lifetime_fn_hints(
282284
range: t.text_range(),
283285
kind: InlayKind::LifetimeHint,
284286
label,
285-
hover_trigger: None,
287+
tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
286288
};
287289

288290
let param_list = func.param_list()?;
@@ -428,20 +430,22 @@ fn lifetime_fn_hints(
428430
(Some(gpl), allocated_lifetimes) => {
429431
let angle_tok = gpl.l_angle_token()?;
430432
let is_empty = gpl.generic_params().next().is_none();
431-
acc.push(mk_lt_hint(
432-
angle_tok,
433-
format!(
433+
acc.push(InlayHint {
434+
range: angle_tok.text_range(),
435+
kind: InlayKind::LifetimeHint,
436+
label: format!(
434437
"{}{}",
435438
allocated_lifetimes.iter().format(", "),
436439
if is_empty { "" } else { ", " }
437440
),
438-
));
441+
tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
442+
});
439443
}
440444
(None, allocated_lifetimes) => acc.push(InlayHint {
441445
range: func.name()?.syntax().text_range(),
442446
kind: InlayKind::GenericParamListHint,
443447
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
444-
hover_trigger: None,
448+
tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
445449
}),
446450
}
447451
Some(())
@@ -452,6 +456,7 @@ fn closure_ret_hints(
452456
sema: &Semantics<RootDatabase>,
453457
famous_defs: &FamousDefs,
454458
config: &InlayHintsConfig,
459+
file_id: FileId,
455460
closure: ast::ClosureExpr,
456461
) -> Option<()> {
457462
if !config.closure_return_type_hints {
@@ -475,7 +480,7 @@ fn closure_ret_hints(
475480
kind: InlayKind::ClosureReturnTypeHint,
476481
label: hint_iterator(sema, &famous_defs, config, &ty)
477482
.unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()),
478-
hover_trigger: None,
483+
tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
479484
});
480485
Some(())
481486
}
@@ -502,7 +507,7 @@ fn reborrow_hints(
502507
range: expr.syntax().text_range(),
503508
kind: InlayKind::ImplicitReborrowHint,
504509
label: label.to_string(),
505-
hover_trigger: None,
510+
tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
506511
});
507512
Some(())
508513
}
@@ -512,6 +517,7 @@ fn chaining_hints(
512517
sema: &Semantics<RootDatabase>,
513518
famous_defs: &FamousDefs,
514519
config: &InlayHintsConfig,
520+
file_id: FileId,
515521
expr: &ast::Expr,
516522
) -> Option<()> {
517523
if !config.chaining_hints {
@@ -561,7 +567,7 @@ fn chaining_hints(
561567
label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| {
562568
ty.display_truncated(sema.db, config.max_length).to_string()
563569
}),
564-
hover_trigger: Some(RangeOrOffset::Range(expr.syntax().text_range())),
570+
tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
565571
});
566572
}
567573
}
@@ -586,24 +592,23 @@ fn param_name_hints(
586592
.filter_map(|((param, _ty), arg)| {
587593
// Only annotate hints for expressions that exist in the original file
588594
let range = sema.original_range_opt(arg.syntax())?;
589-
let param_name = match param? {
590-
Either::Left(_) => "self".to_string(),
595+
let (param_name, param_syntax) = match param.as_ref()? {
596+
Either::Left(pat) => ("self".to_string(), pat.syntax()),
591597
Either::Right(pat) => match pat {
592-
ast::Pat::IdentPat(it) => it.name()?.to_string(),
598+
ast::Pat::IdentPat(it) => (it.name()?.to_string(), pat.syntax()),
593599
_ => return None,
594600
},
595601
};
596-
Some((param_name, arg, range))
602+
Some((sema.original_range_opt(param_syntax), param_name, arg, range))
597603
})
598-
.filter(|(param_name, arg, _)| {
604+
.filter(|(_, param_name, arg, _)| {
599605
!should_hide_param_name_hint(sema, &callable, param_name, arg)
600606
})
601-
.map(|(param_name, _, FileRange { range, .. })| InlayHint {
607+
.map(|(param_range, param_name, _, FileRange { range, .. })| InlayHint {
602608
range,
603609
kind: InlayKind::ParameterHint,
604-
label: param_name.into(),
605-
// FIXME: Show hover for parameter
606-
hover_trigger: None,
610+
label: param_name,
611+
tooltip: param_range.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
607612
});
608613

609614
acc.extend(hints);
@@ -633,7 +638,7 @@ fn binding_mode_hints(
633638
range,
634639
kind: InlayKind::BindingModeHint,
635640
label: r.to_string(),
636-
hover_trigger: None,
641+
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
637642
});
638643
});
639644
match pat {
@@ -648,7 +653,7 @@ fn binding_mode_hints(
648653
range,
649654
kind: InlayKind::BindingModeHint,
650655
label: bm.to_string(),
651-
hover_trigger: None,
656+
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
652657
});
653658
}
654659
_ => (),
@@ -661,6 +666,7 @@ fn bind_pat_hints(
661666
acc: &mut Vec<InlayHint>,
662667
sema: &Semantics<RootDatabase>,
663668
config: &InlayHintsConfig,
669+
file_id: FileId,
664670
pat: &ast::IdentPat,
665671
) -> Option<()> {
666672
if !config.type_hints {
@@ -699,7 +705,10 @@ fn bind_pat_hints(
699705
},
700706
kind: InlayKind::TypeHint,
701707
label,
702-
hover_trigger: pat.name().map(|it| it.syntax().text_range()).map(RangeOrOffset::Range),
708+
tooltip: pat
709+
.name()
710+
.map(|it| it.syntax().text_range())
711+
.map(|it| InlayTooltip::HoverRanged(file_id, it)),
703712
});
704713

705714
Some(())

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub use crate::{
8181
highlight_related::{HighlightRelatedConfig, HighlightedRange},
8282
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
8383
inlay_hints::{
84-
InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints, RangeOrOffset, ReborrowHints,
84+
InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, ReborrowHints,
8585
},
8686
join_lines::JoinLinesConfig,
8787
markup::Markup,

crates/rust-analyzer/src/handlers.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,12 +1344,7 @@ pub(crate) fn handle_inlay_hints(
13441344
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
13451345
.into_iter()
13461346
.map(|it| {
1347-
to_proto::inlay_hint(
1348-
&line_index,
1349-
&params.text_document,
1350-
inlay_hints_config.render_colons,
1351-
it,
1352-
)
1347+
to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
13531348
})
13541349
.collect(),
13551350
))

crates/rust-analyzer/src/to_proto.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ pub(crate) fn signature_help(
415415
}
416416

417417
pub(crate) fn inlay_hint(
418+
snap: &GlobalStateSnapshot,
418419
line_index: &LineIndex,
419-
text_document: &lsp_types::TextDocumentIdentifier,
420420
render_colons: bool,
421421
inlay_hint: InlayHint,
422422
) -> lsp_types::InlayHint {
@@ -472,20 +472,30 @@ pub(crate) fn inlay_hint(
472472
| InlayKind::ClosingBraceHint => None,
473473
},
474474
text_edits: None,
475-
tooltip: Some(lsp_types::InlayHintTooltip::String(inlay_hint.label)),
476-
data: inlay_hint.hover_trigger.map(|range_or_offset| {
477-
to_value(lsp_ext::InlayHintResolveData {
478-
text_document: text_document.clone(),
479-
position: match range_or_offset {
480-
ide::RangeOrOffset::Offset(offset) => {
481-
lsp_ext::PositionOrRange::Position(position(line_index, offset))
482-
}
483-
ide::RangeOrOffset::Range(text_range) => {
484-
lsp_ext::PositionOrRange::Range(range(line_index, text_range))
485-
}
486-
},
487-
})
488-
.unwrap()
475+
data: match inlay_hint.tooltip {
476+
Some(ide::InlayTooltip::HoverOffset(file_id, offset)) => {
477+
let uri = url(snap, file_id);
478+
let text_document = lsp_types::TextDocumentIdentifier { uri };
479+
to_value(lsp_ext::InlayHintResolveData {
480+
text_document,
481+
position: lsp_ext::PositionOrRange::Position(position(line_index, offset)),
482+
})
483+
.ok()
484+
}
485+
Some(ide::InlayTooltip::HoverRanged(file_id, text_range)) => {
486+
let uri = url(snap, file_id);
487+
let text_document = lsp_types::TextDocumentIdentifier { uri };
488+
to_value(lsp_ext::InlayHintResolveData {
489+
text_document,
490+
position: lsp_ext::PositionOrRange::Range(range(line_index, text_range)),
491+
})
492+
.ok()
493+
}
494+
_ => None,
495+
},
496+
tooltip: Some(match inlay_hint.tooltip {
497+
Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
498+
_ => lsp_types::InlayHintTooltip::String(inlay_hint.label),
489499
}),
490500
}
491501
}

0 commit comments

Comments
 (0)