diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index bf82e83822c..e68631f9f71 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -57,19 +57,19 @@ pub enum InlayKind { TypeHint, } -// FIXME: This should live somewhere more general -#[derive(Debug)] -pub enum RangeOrOffset { - Range(TextRange), - Offset(TextSize), -} - #[derive(Debug)] pub struct InlayHint { pub range: TextRange, pub kind: InlayKind, pub label: String, - pub hover_trigger: Option, + pub tooltip: Option, +} + +#[derive(Debug)] +pub enum InlayTooltip { + String(String), + HoverRanged(FileId, TextRange), + HoverOffset(FileId, TextSize), } // Feature: Inlay Hints @@ -109,7 +109,7 @@ pub(crate) fn inlay_hints( let mut acc = Vec::new(); - let hints = |node| hints(&mut acc, &sema, config, node); + let hints = |node| hints(&mut acc, &sema, config, file_id, node); match range_limit { Some(FileRange { range, .. }) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, @@ -128,6 +128,7 @@ fn hints( hints: &mut Vec, sema: &Semantics, config: &InlayHintsConfig, + file_id: FileId, node: SyntaxNode, ) { let famous_defs = match sema.scope(&node) { @@ -135,17 +136,17 @@ fn hints( None => return, }; - closing_brace_hints(hints, sema, config, node.clone()); + closing_brace_hints(hints, sema, config, file_id, node.clone()); match_ast! { match node { ast::Expr(expr) => { - chaining_hints(hints, sema, &famous_defs, config, &expr); + chaining_hints(hints, sema, &famous_defs, config, file_id, &expr); match expr { ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { param_name_hints(hints, sema, config, ast::Expr::from(it)) } - ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it), + ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it), // We could show reborrows for all expressions, but usually that is just noise to the user // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), @@ -155,7 +156,7 @@ fn hints( ast::Pat(it) => { binding_mode_hints(hints, sema, config, &it); if let ast::Pat::IdentPat(it) = it { - bind_pat_hints(hints, sema, config, &it); + bind_pat_hints(hints, sema, config, file_id, &it); } Some(()) }, @@ -169,6 +170,7 @@ fn closing_brace_hints( acc: &mut Vec, sema: &Semantics, config: &InlayHintsConfig, + file_id: FileId, node: SyntaxNode, ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; @@ -263,7 +265,7 @@ fn closing_brace_hints( range: closing_token.text_range(), kind: InlayKind::ClosingBraceHint, label, - hover_trigger: name_offset.map(RangeOrOffset::Offset), + tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)), }); None @@ -282,7 +284,7 @@ fn lifetime_fn_hints( range: t.text_range(), kind: InlayKind::LifetimeHint, label, - hover_trigger: None, + tooltip: Some(InlayTooltip::String("Elided lifetime".into())), }; let param_list = func.param_list()?; @@ -428,20 +430,22 @@ fn lifetime_fn_hints( (Some(gpl), allocated_lifetimes) => { let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); - acc.push(mk_lt_hint( - angle_tok, - format!( + acc.push(InlayHint { + range: angle_tok.text_range(), + kind: InlayKind::LifetimeHint, + label: format!( "{}{}", allocated_lifetimes.iter().format(", "), if is_empty { "" } else { ", " } ), - )); + tooltip: Some(InlayTooltip::String("Elided lifetimes".into())), + }); } (None, allocated_lifetimes) => acc.push(InlayHint { range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamListHint, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), - hover_trigger: None, + tooltip: Some(InlayTooltip::String("Elided lifetimes".into())), }), } Some(()) @@ -452,6 +456,7 @@ fn closure_ret_hints( sema: &Semantics, famous_defs: &FamousDefs, config: &InlayHintsConfig, + file_id: FileId, closure: ast::ClosureExpr, ) -> Option<()> { if !config.closure_return_type_hints { @@ -475,7 +480,7 @@ fn closure_ret_hints( kind: InlayKind::ClosureReturnTypeHint, label: hint_iterator(sema, &famous_defs, config, &ty) .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()), - hover_trigger: None, + tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())), }); Some(()) } @@ -502,7 +507,7 @@ fn reborrow_hints( range: expr.syntax().text_range(), kind: InlayKind::ImplicitReborrowHint, label: label.to_string(), - hover_trigger: None, + tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())), }); Some(()) } @@ -512,6 +517,7 @@ fn chaining_hints( sema: &Semantics, famous_defs: &FamousDefs, config: &InlayHintsConfig, + file_id: FileId, expr: &ast::Expr, ) -> Option<()> { if !config.chaining_hints { @@ -561,7 +567,7 @@ fn chaining_hints( label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| { ty.display_truncated(sema.db, config.max_length).to_string() }), - hover_trigger: Some(RangeOrOffset::Range(expr.syntax().text_range())), + tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())), }); } } @@ -586,24 +592,23 @@ fn param_name_hints( .filter_map(|((param, _ty), arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let param_name = match param? { - Either::Left(_) => "self".to_string(), + let (param_name, param_syntax) = match param.as_ref()? { + Either::Left(pat) => ("self".to_string(), pat.syntax()), Either::Right(pat) => match pat { - ast::Pat::IdentPat(it) => it.name()?.to_string(), + ast::Pat::IdentPat(it) => (it.name()?.to_string(), pat.syntax()), _ => return None, }, }; - Some((param_name, arg, range)) + Some((sema.original_range_opt(param_syntax), param_name, arg, range)) }) - .filter(|(param_name, arg, _)| { + .filter(|(_, param_name, arg, _)| { !should_hide_param_name_hint(sema, &callable, param_name, arg) }) - .map(|(param_name, _, FileRange { range, .. })| InlayHint { + .map(|(param_range, param_name, _, FileRange { range, .. })| InlayHint { range, kind: InlayKind::ParameterHint, - label: param_name.into(), - // FIXME: Show hover for parameter - hover_trigger: None, + label: param_name, + tooltip: param_range.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())), }); acc.extend(hints); @@ -633,7 +638,7 @@ fn binding_mode_hints( range, kind: InlayKind::BindingModeHint, label: r.to_string(), - hover_trigger: None, + tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); }); match pat { @@ -648,7 +653,7 @@ fn binding_mode_hints( range, kind: InlayKind::BindingModeHint, label: bm.to_string(), - hover_trigger: None, + tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), }); } _ => (), @@ -661,6 +666,7 @@ fn bind_pat_hints( acc: &mut Vec, sema: &Semantics, config: &InlayHintsConfig, + file_id: FileId, pat: &ast::IdentPat, ) -> Option<()> { if !config.type_hints { @@ -699,7 +705,10 @@ fn bind_pat_hints( }, kind: InlayKind::TypeHint, label, - hover_trigger: pat.name().map(|it| it.syntax().text_range()).map(RangeOrOffset::Range), + tooltip: pat + .name() + .map(|it| it.syntax().text_range()) + .map(|it| InlayTooltip::HoverRanged(file_id, it)), }); Some(()) diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 071da8097c5..0d87f4fcfaf 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -81,7 +81,7 @@ macro_rules! eprintln { highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ - InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints, RangeOrOffset, ReborrowHints, + InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, ReborrowHints, }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 510b37bb6f6..261c02816d5 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1344,12 +1344,7 @@ pub(crate) fn handle_inlay_hints( .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() .map(|it| { - to_proto::inlay_hint( - &line_index, - ¶ms.text_document, - inlay_hints_config.render_colons, - it, - ) + to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) }) .collect(), )) diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 6817090a8bc..5493180c5ec 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -415,8 +415,8 @@ pub(crate) fn signature_help( } pub(crate) fn inlay_hint( + snap: &GlobalStateSnapshot, line_index: &LineIndex, - text_document: &lsp_types::TextDocumentIdentifier, render_colons: bool, inlay_hint: InlayHint, ) -> lsp_types::InlayHint { @@ -472,20 +472,30 @@ pub(crate) fn inlay_hint( | InlayKind::ClosingBraceHint => None, }, text_edits: None, - tooltip: Some(lsp_types::InlayHintTooltip::String(inlay_hint.label)), - data: inlay_hint.hover_trigger.map(|range_or_offset| { - to_value(lsp_ext::InlayHintResolveData { - text_document: text_document.clone(), - position: match range_or_offset { - ide::RangeOrOffset::Offset(offset) => { - lsp_ext::PositionOrRange::Position(position(line_index, offset)) - } - ide::RangeOrOffset::Range(text_range) => { - lsp_ext::PositionOrRange::Range(range(line_index, text_range)) - } - }, - }) - .unwrap() + data: match inlay_hint.tooltip { + Some(ide::InlayTooltip::HoverOffset(file_id, offset)) => { + let uri = url(snap, file_id); + let text_document = lsp_types::TextDocumentIdentifier { uri }; + to_value(lsp_ext::InlayHintResolveData { + text_document, + position: lsp_ext::PositionOrRange::Position(position(line_index, offset)), + }) + .ok() + } + Some(ide::InlayTooltip::HoverRanged(file_id, text_range)) => { + let uri = url(snap, file_id); + let text_document = lsp_types::TextDocumentIdentifier { uri }; + to_value(lsp_ext::InlayHintResolveData { + text_document, + position: lsp_ext::PositionOrRange::Range(range(line_index, text_range)), + }) + .ok() + } + _ => None, + }, + tooltip: Some(match inlay_hint.tooltip { + Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s), + _ => lsp_types::InlayHintTooltip::String(inlay_hint.label), }), } }