diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 8f9e62d398c3..5934160f3438 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -524,9 +524,17 @@ pub enum InlayTooltip { Markdown(String), } -#[derive(Default, Hash)] +#[derive(Default)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, + pub tooltip: Option>, +} + +impl std::hash::Hash for InlayHintLabel { + fn hash(&self, state: &mut H) { + self.parts.hash(state); + self.tooltip.is_some().hash(state); + } } impl InlayHintLabel { @@ -537,6 +545,7 @@ impl InlayHintLabel { ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], + tooltip: None, } } @@ -582,6 +591,7 @@ impl From for InlayHintLabel { fn from(s: String) -> Self { Self { parts: smallvec![InlayHintLabelPart { text: s, linked_location: None, tooltip: None }], + tooltip: None, } } } @@ -592,8 +602,9 @@ impl From<&str> for InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location: None, - tooltip: None + tooltip: None, }], + tooltip: None, } } } @@ -606,7 +617,10 @@ impl fmt::Display for InlayHintLabel { impl fmt::Debug for InlayHintLabel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(&self.parts).finish() + f.debug_struct("InlayHintLabel") + .field("parts", &self.parts) + .field("tooltip", &self.tooltip) + .finish() } } @@ -703,6 +717,13 @@ impl InlayHintLabelBuilder<'_> { } } + fn write_to_label_and_tooltip(&mut self, s: &str) -> fmt::Result { + if let Some(LazyProperty::Computed(ref mut tooltip)) = self.result.tooltip { + tooltip.push_str(s); + } + self.write_str(s) + } + fn finish(mut self) -> InlayHintLabel { self.make_new_part(); self.result @@ -744,31 +765,44 @@ fn label_of_ty( ) }); - label_builder.write_str(LABEL_START)?; + label_builder.write_to_label_and_tooltip(LABEL_START)?; label_builder.start_location_link(ModuleDef::from(iter_trait).into()); - label_builder.write_str(LABEL_ITERATOR)?; + label_builder.write_to_label_and_tooltip(LABEL_ITERATOR)?; label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE)?; + label_builder.write_to_label_and_tooltip(LABEL_MIDDLE)?; label_builder.start_location_link(ModuleDef::from(item).into()); - label_builder.write_str(LABEL_ITEM)?; + label_builder.write_to_label_and_tooltip(LABEL_ITEM)?; label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE2)?; + label_builder.write_to_label_and_tooltip(LABEL_MIDDLE2)?; rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?; - label_builder.write_str(LABEL_END)?; + label_builder.write_to_label_and_tooltip(LABEL_END)?; Ok(()) } - None => ty - .display_truncated(sema.db, max_length, display_target) - .with_closure_style(config.closure_style) - .write_to(label_builder), + None => { + if let Some(LazyProperty::Computed(ref mut tooltip)) = label_builder.result.tooltip + { + ty.display(sema.db, display_target) + .with_closure_style(config.closure_style) + .write_to(tooltip)?; + } + + ty.display_truncated(sema.db, max_length, display_target) + .with_closure_style(config.closure_style) + .write_to(label_builder) + } } } + let tooltip = if config.fields_to_resolve.resolve_label_tooltip { + Some(LazyProperty::Lazy) + } else { + Some(LazyProperty::Computed(String::new())) + }; let mut label_builder = InlayHintLabelBuilder { db: sema.db, last_part: String::new(), location: None, - result: InlayHintLabel::default(), + result: InlayHintLabel { tooltip, ..Default::default() }, resolve: config.fields_to_resolve.resolve_label_location, }; let _ = @@ -966,6 +1000,22 @@ mod tests { assert!(edits.is_empty(), "unexpected edits: {edits:?}"); } + #[track_caller] + pub(super) fn check_tooltip( + config: InlayHintsConfig, + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect: Expect, + ) { + let (analysis, file_id) = fixture::file(ra_fixture); + let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + + let tooltips = inlay_hints + .into_iter() + .filter_map(|hint| hint.label.tooltip?.computed()) + .collect::>(); + expect.assert_debug_eq(&tooltips); + } + #[test] fn hints_disabled() { check_with_config( diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 0718e5ac646b..90b444689708 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -184,7 +184,8 @@ mod tests { use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig}; use crate::inlay_hints::tests::{ - DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config, + DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_tooltip, + check_with_config, }; #[track_caller] @@ -192,6 +193,14 @@ mod tests { check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture); } + #[track_caller] + fn check_types_tooltip( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect: expect_test::Expect, + ) { + check_tooltip(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture, expect); + } + #[test] fn type_hints_only() { check_types( @@ -1256,4 +1265,42 @@ where "#, ); } + + #[test] + fn tooltip_for_bind_pat() { + check_types_tooltip( + r#" +struct Struct { + a: T, + b: T2, +} + +pub fn main() { + let x = Struct { a: Struct { a: Struct { a: 1, b: 2 }, b: 2 }, b: Struct { a: 1, b: 2 } }; +} + "#, + expect![[r#" + [ + "Struct, i32>, Struct>", + ] + "#]], + ); + } + + #[test] + fn tooltip_for_bind_pat_with_type_alias() { + check_types_tooltip( + r#" +//- minicore: option +pub fn main() { + let x = Some(1); +} + "#, + expect![[r#" + [ + "Option", + ] + "#]], + ); + } } diff --git a/crates/ide/src/inlay_hints/bounds.rs b/crates/ide/src/inlay_hints/bounds.rs index 8ddbfaeffe87..8daaa22954dd 100644 --- a/crates/ide/src/inlay_hints/bounds.rs +++ b/crates/ide/src/inlay_hints/bounds.rs @@ -135,23 +135,26 @@ fn foo() {} [ ( 7..8, - [ - ": ", - InlayHintLabelPart { - text: "Sized", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 135..140, - }, + InlayHintLabel { + parts: [ + ": ", + InlayHintLabelPart { + text: "Sized", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 135..140, + }, + ), ), - ), - tooltip: "", - }, - ], + tooltip: "", + }, + ], + tooltip: None, + }, ), ] "#]], diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index ff157fa171b5..6a77978bc7a9 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -132,41 +132,55 @@ fn main() { [ ( 147..172, - [ - InlayHintLabelPart { - text: "B", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 63..64, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "B", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "B", ), - tooltip: "", - }, - ], + ), + }, ), ( 147..154, - [ - InlayHintLabelPart { - text: "A", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "A", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "A", ), - tooltip: "", - }, - ], + ), + }, ), ] "#]], @@ -215,41 +229,55 @@ fn main() { [ ( 143..190, - [ - InlayHintLabelPart { - text: "C", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "C", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "C", ), - tooltip: "", - }, - ], + ), + }, ), ( 143..179, - [ - InlayHintLabelPart { - text: "B", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "B", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "B", ), - tooltip: "", - }, - ], + ), + }, ), ] "#]], @@ -282,41 +310,55 @@ fn main() { [ ( 143..190, - [ - InlayHintLabelPart { - text: "C", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "C", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "C", ), - tooltip: "", - }, - ], + ), + }, ), ( 143..179, - [ - InlayHintLabelPart { - text: "B", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "B", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "B", ), - tooltip: "", - }, - ], + ), + }, ), ] "#]], @@ -350,73 +392,87 @@ fn main() { [ ( 246..283, - [ - InlayHintLabelPart { - text: "B", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 23..24, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "B", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), ), - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "X", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "X", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), + tooltip: "", + }, + ">", + ], + tooltip: Some( + Computed( + "B>", ), - tooltip: "", - }, - ">", - ], + ), + }, ), ( 246..265, - [ - InlayHintLabelPart { - text: "A", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "A", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "X", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "X", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), + tooltip: "", + }, + ">", + ], + tooltip: Some( + Computed( + "A>", ), - tooltip: "", - }, - ">", - ], + ), + }, ), ] "#]], @@ -452,131 +508,159 @@ fn main() { [ ( 174..241, - [ - "impl ", - InlayHintLabelPart { - text: "Iterator", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + InlayHintLabel { + parts: [ + "impl ", + InlayHintLabelPart { + text: "Iterator", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Item", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "Item", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), + tooltip: "", + }, + " = ()>", + ], + tooltip: Some( + Computed( + "impl Iterator", ), - tooltip: "", - }, - " = ()>", - ], + ), + }, ), ( 174..224, - [ - "impl ", - InlayHintLabelPart { - text: "Iterator", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + InlayHintLabel { + parts: [ + "impl ", + InlayHintLabelPart { + text: "Iterator", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Item", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "Item", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), + tooltip: "", + }, + " = ()>", + ], + tooltip: Some( + Computed( + "impl Iterator", ), - tooltip: "", - }, - " = ()>", - ], + ), + }, ), ( 174..206, - [ - "impl ", - InlayHintLabelPart { - text: "Iterator", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + InlayHintLabel { + parts: [ + "impl ", + InlayHintLabelPart { + text: "Iterator", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Item", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "Item", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), + tooltip: "", + }, + " = ()>", + ], + tooltip: Some( + Computed( + "impl Iterator", ), - tooltip: "", - }, - " = ()>", - ], + ), + }, ), ( 174..189, - [ - "&mut ", - InlayHintLabelPart { - text: "MyIter", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 0..0, - }, + InlayHintLabel { + parts: [ + "&mut ", + InlayHintLabelPart { + text: "MyIter", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 0..0, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "&mut MyIter", ), - tooltip: "", - }, - ], + ), + }, ), ] "#]], @@ -609,79 +693,103 @@ fn main() { [ ( 124..130, - [ - InlayHintLabelPart { - text: "Struct", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "Struct", ), - tooltip: "", - }, - ], + ), + }, ), ( 145..185, - [ - InlayHintLabelPart { - text: "Struct", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "Struct", ), - tooltip: "", - }, - ], + ), + }, ), ( 145..168, - [ - InlayHintLabelPart { - text: "Struct", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), + tooltip: "", + }, + ], + tooltip: Some( + Computed( + "Struct", ), - tooltip: "", - }, - ], + ), + }, ), ( 222..228, - [ - InlayHintLabelPart { - text: "self", - linked_location: Some( - Computed( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 42..46, - }, + InlayHintLabel { + parts: [ + InlayHintLabelPart { + text: "self", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 42..46, + }, + ), ), - ), - tooltip: "", - }, - ], + tooltip: "", + }, + ], + tooltip: None, + }, ), ] "#]], diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 4efe330f16ac..8c7072ff5bbc 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -563,6 +563,7 @@ pub(crate) fn inlay_hint( part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy) || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) }) + || hint.label.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) }) }; @@ -641,8 +642,20 @@ fn inlay_hint_label( needs_resolve: bool, mut label: InlayHintLabel, ) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { + let tooltip = label.tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => { + Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: format!("```rust\n{it}\n```"), + })) + } + LazyProperty::Lazy => { + *something_to_resolve |= needs_resolve && fields_to_resolve.resolve_hint_tooltip; + None + } + }); let (label, tooltip) = match &*label.parts { - [InlayHintLabelPart { linked_location: None, .. }] => { + [InlayHintLabelPart { linked_location: None, .. }] if tooltip.is_none() => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let tooltip = tooltip.and_then(|it| match it { LazyProperty::Computed(it) => Some(it), @@ -709,7 +722,7 @@ fn inlay_hint_label( }) }) .collect::>()?; - (lsp_types::InlayHintLabel::LabelParts(parts), None) + (lsp_types::InlayHintLabel::LabelParts(parts), tooltip) } }; Ok((label, tooltip))