Merge pull request #18991 from Veykril/push-rmqmnrymwmoz

Keep already computed inlay hint properties instead of late resolving them
This commit is contained in:
Lukas Wirth 2025-01-24 14:27:43 +00:00 committed by GitHub
commit a62e2f513a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 433 additions and 322 deletions

View file

@ -45,7 +45,7 @@ use hir_def::{
body::BodyDiagnostic, body::BodyDiagnostic,
data::{adt::VariantData, TraitFlags}, data::{adt::VariantData, TraitFlags},
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
lang_item::LangItemTarget, lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout}, layout::{self, ReprOptions, TargetDataLayout},
@ -2470,20 +2470,31 @@ impl Param {
} }
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> { pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
let parent = match self.func { match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), Callee::Def(CallableDefId::FunctionId(it)) => {
Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, let parent = DefWithBodyId::FunctionId(it);
_ => return None, let body = db.body(parent);
}; if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
let body = db.body(parent); Some(Local { parent, binding_id: self_param })
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { } else if let Pat::Bind { id, .. } =
Some(Local { parent, binding_id: self_param }) &body[body.params[self.idx - body.self_param.is_some() as usize]]
} else if let Pat::Bind { id, .. } = {
&body[body.params[self.idx - body.self_param.is_some() as usize]] Some(Local { parent, binding_id: *id })
{ } else {
Some(Local { parent, binding_id: *id }) None
} else { }
None }
Callee::Closure(closure, _) => {
let c = db.lookup_intern_closure(closure.into());
let body = db.body(c.0);
if let Expr::Closure { args, .. } = &body[c.1] {
if let Pat::Bind { id, .. } = &body[args[self.idx]] {
return Some(Local { parent: c.0, binding_id: *id });
}
}
None
}
_ => None,
} }
} }

View file

@ -209,7 +209,7 @@ fn hints(
) { ) {
closing_brace::hints(hints, sema, config, file_id, node.clone()); closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args); generic_param::hints(hints, famous_defs, config, any_has_generic_args);
} }
match_ast! { match_ast! {
@ -300,22 +300,23 @@ pub struct InlayHintsConfig {
pub closing_brace_hints_min_lines: Option<usize>, pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve, pub fields_to_resolve: InlayFieldsToResolve,
} }
impl InlayHintsConfig { impl InlayHintsConfig {
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy<TextEdit> { fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
if self.fields_to_resolve.resolve_text_edits { if self.fields_to_resolve.resolve_text_edits {
Lazy::Lazy LazyProperty::Lazy
} else { } else {
let edit = finish(); let edit = finish();
never!(edit.is_empty(), "inlay hint produced an empty text edit"); never!(edit.is_empty(), "inlay hint produced an empty text edit");
Lazy::Computed(edit) LazyProperty::Computed(edit)
} }
} }
fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy<InlayTooltip> { fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty<InlayTooltip> {
if self.fields_to_resolve.resolve_hint_tooltip if self.fields_to_resolve.resolve_hint_tooltip
&& self.fields_to_resolve.resolve_label_tooltip && self.fields_to_resolve.resolve_label_tooltip
{ {
Lazy::Lazy LazyProperty::Lazy
} else { } else {
let tooltip = finish(); let tooltip = finish();
never!( never!(
@ -326,7 +327,20 @@ impl InlayHintsConfig {
.is_empty(), .is_empty(),
"inlay hint produced an empty tooltip" "inlay hint produced an empty tooltip"
); );
Lazy::Computed(tooltip) LazyProperty::Computed(tooltip)
}
}
/// This always reports a resolvable location, so only use this when it is very likely for a
/// location link to actually resolve but where computing `finish` would be costly.
fn lazy_location_opt(
&self,
finish: impl FnOnce() -> Option<FileRange>,
) -> Option<LazyProperty<FileRange>> {
if self.fields_to_resolve.resolve_label_location {
Some(LazyProperty::Lazy)
} else {
finish().map(LazyProperty::Computed)
} }
} }
} }
@ -441,7 +455,7 @@ pub struct InlayHint {
/// The actual label to show in the inlay hint. /// The actual label to show in the inlay hint.
pub label: InlayHintLabel, pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint. /// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<Lazy<TextEdit>>, pub text_edit: Option<LazyProperty<TextEdit>>,
/// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
/// hint does not support resolving. /// hint does not support resolving.
pub resolve_parent: Option<TextRange>, pub resolve_parent: Option<TextRange>,
@ -449,15 +463,15 @@ pub struct InlayHint {
/// A type signaling that a value is either computed, or is available for computation. /// A type signaling that a value is either computed, or is available for computation.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Lazy<T> { pub enum LazyProperty<T> {
Computed(T), Computed(T),
Lazy, Lazy,
} }
impl<T> Lazy<T> { impl<T> LazyProperty<T> {
pub fn computed(self) -> Option<T> { pub fn computed(self) -> Option<T> {
match self { match self {
Lazy::Computed(it) => Some(it), LazyProperty::Computed(it) => Some(it),
_ => None, _ => None,
} }
} }
@ -508,8 +522,8 @@ pub struct InlayHintLabel {
impl InlayHintLabel { impl InlayHintLabel {
pub fn simple( pub fn simple(
s: impl Into<String>, s: impl Into<String>,
tooltip: Option<Lazy<InlayTooltip>>, tooltip: Option<LazyProperty<InlayTooltip>>,
linked_location: Option<FileRange>, linked_location: Option<LazyProperty<FileRange>>,
) -> InlayHintLabel { ) -> InlayHintLabel {
InlayHintLabel { InlayHintLabel {
parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }],
@ -593,16 +607,16 @@ pub struct InlayHintLabelPart {
/// refers to (not necessarily the location itself). /// refers to (not necessarily the location itself).
/// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// When setting this, no tooltip must be set on the containing hint, or VS Code will display
/// them both. /// them both.
pub linked_location: Option<FileRange>, pub linked_location: Option<LazyProperty<FileRange>>,
/// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like
/// hover requests to show. /// hover requests to show.
pub tooltip: Option<Lazy<InlayTooltip>>, pub tooltip: Option<LazyProperty<InlayTooltip>>,
} }
impl std::hash::Hash for InlayHintLabelPart { impl std::hash::Hash for InlayHintLabelPart {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state); self.text.hash(state);
self.linked_location.hash(state); self.linked_location.is_some().hash(state);
self.tooltip.is_some().hash(state); self.tooltip.is_some().hash(state);
} }
} }
@ -610,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart {
impl fmt::Debug for InlayHintLabelPart { impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f), Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => {
text.fmt(f)
}
Self { text, linked_location, tooltip } => f Self { text, linked_location, tooltip } => f
.debug_struct("InlayHintLabelPart") .debug_struct("InlayHintLabelPart")
.field("text", text) .field("text", text)
@ -618,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart {
.field( .field(
"tooltip", "tooltip",
&tooltip.as_ref().map_or("", |it| match it { &tooltip.as_ref().map_or("", |it| match it {
Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it, LazyProperty::Computed(
Lazy::Lazy => "", InlayTooltip::String(it) | InlayTooltip::Markdown(it),
) => it,
LazyProperty::Lazy => "",
}), }),
) )
.finish(), .finish(),
@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> {
db: &'a RootDatabase, db: &'a RootDatabase,
result: InlayHintLabel, result: InlayHintLabel,
last_part: String, last_part: String,
location: Option<FileRange>, resolve: bool,
location: Option<LazyProperty<FileRange>>,
} }
impl fmt::Write for InlayHintLabelBuilder<'_> { impl fmt::Write for InlayHintLabelBuilder<'_> {
@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) { fn start_location_link(&mut self, def: ModuleDefId) {
never!(self.location.is_some(), "location link is already started"); never!(self.location.is_some(), "location link is already started");
self.make_new_part(); self.make_new_part();
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site(); self.location = Some(if self.resolve {
let location = LazyProperty::Lazy
FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; } else {
self.location = Some(location); LazyProperty::Computed({
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site();
FileRange { file_id: location.file_id, range: location.focus_or_full_range() }
})
});
} }
fn end_location_link(&mut self) { fn end_location_link(&mut self) {
@ -735,6 +759,7 @@ fn label_of_ty(
last_part: String::new(), last_part: String::new(),
location: None, location: None,
result: InlayHintLabel::default(), result: InlayHintLabel::default(),
resolve: config.fields_to_resolve.resolve_label_location,
}; };
let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition); let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
let r = label_builder.finish(); let r = label_builder.finish();
@ -783,7 +808,7 @@ fn ty_to_text_edit(
ty: &hir::Type, ty: &hir::Type,
offset_to_insert: TextSize, offset_to_insert: TextSize,
prefix: impl Into<String>, prefix: impl Into<String>,
) -> Option<Lazy<TextEdit>> { ) -> Option<LazyProperty<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow? // FIXME: Limit the length and bail out on excess somehow?
let rendered = sema let rendered = sema
.scope(node_for_hint) .scope(node_for_hint)

View file

@ -22,11 +22,7 @@ pub(super) fn hints(
return None; return None;
} }
let linked_location = let sized_trait = famous_defs.core_marker_Sized();
famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
let n = it.call_site();
FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
});
for param in params.type_or_const_params() { for param in params.type_or_const_params() {
match param { match param {
@ -48,7 +44,17 @@ pub(super) fn hints(
} }
hint.parts.push(InlayHintLabelPart { hint.parts.push(InlayHintLabelPart {
text: "Sized".to_owned(), text: "Sized".to_owned(),
linked_location, linked_location: sized_trait.and_then(|it| {
config.lazy_location_opt(|| {
it.try_to_nav(sema.db).map(|it| {
let n = it.call_site();
FileRange {
file_id: n.file_id,
range: n.focus_or_full_range(),
}
})
})
}),
tooltip: None, tooltip: None,
}); });
if has_bounds { if has_bounds {
@ -134,12 +140,14 @@ fn foo<T>() {}
InlayHintLabelPart { InlayHintLabelPart {
text: "Sized", text: "Sized",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 135..140, ),
}, range: 135..140,
},
),
), ),
tooltip: "", tooltip: "",
}, },

View file

@ -81,7 +81,10 @@ mod tests {
use crate::{ use crate::{
fixture, fixture,
inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, inlay_hints::{
tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
LazyProperty,
},
InlayHintsConfig, InlayHintsConfig,
}; };
@ -99,7 +102,7 @@ mod tests {
let (analysis, file_id) = fixture::file(ra_fixture); let (analysis, file_id) = fixture::file(ra_fixture);
let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| {
if let Some(loc) = &mut hint.linked_location { if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location {
loc.range = TextRange::empty(TextSize::from(0)); loc.range = TextRange::empty(TextSize::from(0));
} }
}); });
@ -134,12 +137,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "B", text: "B",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 63..64, ),
}, range: 63..64,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -151,12 +156,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "A", text: "A",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 7..8, ),
}, range: 7..8,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -213,12 +220,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "C", text: "C",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 51..52, ),
}, range: 51..52,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -230,12 +239,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "B", text: "B",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 29..30, ),
}, range: 29..30,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -276,12 +287,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "C", text: "C",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 51..52, ),
}, range: 51..52,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -293,12 +306,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "B", text: "B",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 29..30, ),
}, range: 29..30,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -340,12 +355,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "B", text: "B",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 23..24, ),
}, range: 23..24,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -353,12 +370,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "X", text: "X",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 55..56, ),
}, range: 55..56,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -371,12 +390,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "A", text: "A",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 7..8, ),
}, range: 7..8,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -384,12 +405,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "X", text: "X",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 55..56, ),
}, range: 55..56,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -435,12 +458,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Iterator", text: "Iterator",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -448,12 +473,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Item", text: "Item",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -467,12 +494,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Iterator", text: "Iterator",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -480,12 +509,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Item", text: "Item",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -499,12 +530,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Iterator", text: "Iterator",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -512,12 +545,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Item", text: "Item",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
1, file_id: FileId(
), 1,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -531,12 +566,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "MyIter", text: "MyIter",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 0..0, ),
}, range: 0..0,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -577,12 +614,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Struct", text: "Struct",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 7..13, ),
}, range: 7..13,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -594,12 +633,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Struct", text: "Struct",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 7..13, ),
}, range: 7..13,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -611,12 +652,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "Struct", text: "Struct",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 7..13, ),
}, range: 7..13,
},
),
), ),
tooltip: "", tooltip: "",
}, },
@ -628,12 +671,14 @@ fn main() {
InlayHintLabelPart { InlayHintLabelPart {
text: "self", text: "self",
linked_location: Some( linked_location: Some(
FileRangeWrapper { Computed(
file_id: FileId( FileRangeWrapper {
0, file_id: FileId(
), 0,
range: 42..46, ),
}, range: 42..46,
},
),
), ),
tooltip: "", tooltip: "",
}, },

View file

@ -11,7 +11,10 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T, match_ast, SyntaxKind, SyntaxNode, T,
}; };
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; use crate::{
inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig,
InlayKind,
};
pub(super) fn hints( pub(super) fn hints(
acc: &mut Vec<InlayHint>, acc: &mut Vec<InlayHint>,
@ -141,7 +144,7 @@ pub(super) fn hints(
acc.push(InlayHint { acc.push(InlayHint {
range: closing_token.text_range(), range: closing_token.text_range(),
kind: InlayKind::ClosingBrace, kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location), label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)),
text_edit: None, text_edit: None,
position: InlayHintPosition::After, position: InlayHintPosition::After,
pad_left: true, pad_left: true,

View file

@ -53,10 +53,6 @@ pub(super) fn hints(
let last = captures.len() - 1; let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() { for (idx, capture) in captures.into_iter().enumerate() {
let local = capture.local(); let local = capture.local();
let source = local.primary_source(sema.db);
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
let label = format!( let label = format!(
"{}{}", "{}{}",
@ -73,8 +69,17 @@ pub(super) fn hints(
} }
hint.label.append_part(InlayHintLabelPart { hint.label.append_part(InlayHintLabelPart {
text: label, text: label,
linked_location: source.name().and_then(|name| { linked_location: config.lazy_location_opt(|| {
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) let source = local.primary_source(sema.db);
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
source.name().and_then(|name| {
name.syntax()
.original_file_range_opt(sema.db)
.map(TupleExt::head)
.map(Into::into)
})
}), }),
tooltip: None, tooltip: None,
}); });

View file

@ -1,17 +1,19 @@
//! Implementation of inlay hints for generic parameters. //! Implementation of inlay hints for generic parameters.
use ide_db::{active_parameter::generic_def_for_node, RootDatabase}; use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs};
use syntax::{ use syntax::{
ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName},
AstNode, AstNode,
}; };
use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind}; use crate::{
inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
};
use super::param_name::{is_argument_similar_to_param_name, render_label}; use super::param_name::is_argument_similar_to_param_name;
pub(crate) fn hints( pub(crate) fn hints(
acc: &mut Vec<InlayHint>, acc: &mut Vec<InlayHint>,
sema: &hir::Semantics<'_, RootDatabase>, FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig, config: &InlayHintsConfig,
node: AnyHasGenericArgs, node: AnyHasGenericArgs,
) -> Option<()> { ) -> Option<()> {
@ -45,12 +47,23 @@ pub(crate) fn hints(
return None; return None;
} }
let name = param.name(sema.db); let allowed = match (param, &arg) {
let param_name = name.as_str(); (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints,
(hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints,
(hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => {
lifetime_hints
}
_ => false,
};
if !allowed {
return None;
}
let param_name = param.name(sema.db);
let should_hide = { let should_hide = {
let argument = get_string_representation(&arg)?; let argument = get_string_representation(&arg)?;
is_argument_similar_to_param_name(&argument, param_name) is_argument_similar_to_param_name(&argument, param_name.as_str())
}; };
if should_hide { if should_hide {
@ -59,30 +72,28 @@ pub(crate) fn hints(
let range = sema.original_range_opt(arg.syntax())?.range; let range = sema.original_range_opt(arg.syntax())?.range;
let source_syntax = match param { let colon = if config.render_colons { ":" } else { "" };
hir::GenericParam::TypeParam(it) => { let label = InlayHintLabel::simple(
if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
return None; None,
} config.lazy_location_opt(|| {
sema.source(it.merge())?.value.syntax().clone() let source_syntax = match param {
} hir::GenericParam::TypeParam(it) => {
hir::GenericParam::ConstParam(it) => { sema.source(it.merge()).map(|it| it.value.syntax().clone())
if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { }
return None; hir::GenericParam::ConstParam(it) => {
} let syntax = sema.source(it.merge())?.value.syntax().clone();
let syntax = sema.source(it.merge())?.value.syntax().clone(); let const_param = ast::ConstParam::cast(syntax)?;
let const_param = ast::ConstParam::cast(syntax)?; const_param.name().map(|it| it.syntax().clone())
const_param.name()?.syntax().clone() }
} hir::GenericParam::LifetimeParam(it) => {
hir::GenericParam::LifetimeParam(it) => { sema.source(it).map(|it| it.value.syntax().clone())
if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { }
return None; };
} let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it));
sema.source(it)?.value.syntax().clone() linked_location.map(Into::into)
} }),
}; );
let linked_location = sema.original_range_opt(&source_syntax);
let label = render_label(param_name, config, linked_location);
Some(InlayHint { Some(InlayHint {
range, range,

View file

@ -49,7 +49,7 @@ pub(super) fn hints(
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
continue; // Arguably only ADTs have significant drop impls continue; // Arguably only ADTs have significant drop impls
} }
let Some(binding) = local_to_binding.get(place.local) else { let Some(&binding_idx) = local_to_binding.get(place.local) else {
continue; // Ignore temporary values continue; // Ignore temporary values
}; };
let range = match terminator.span { let range = match terminator.span {
@ -91,25 +91,26 @@ pub(super) fn hints(
}, },
MirSpan::Unknown => continue, MirSpan::Unknown => continue,
}; };
let binding_source = source_map let binding = &hir.bindings[binding_idx];
.patterns_for_binding(*binding)
.first()
.and_then(|d| source_map.pat_syntax(*d).ok())
.and_then(|d| {
Some(FileRange {
file_id: d.file_id.file_id()?.into(),
range: d.value.text_range(),
})
});
let binding = &hir.bindings[*binding];
let name = binding.name.display_no_db(file_id.edition()).to_smolstr(); let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
if name.starts_with("<ra@") { if name.starts_with("<ra@") {
continue; // Ignore desugared variables continue; // Ignore desugared variables
} }
let mut label = InlayHintLabel::simple( let mut label = InlayHintLabel::simple(
name, name,
Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))), None,
binding_source, config.lazy_location_opt(|| {
source_map
.patterns_for_binding(binding_idx)
.first()
.and_then(|d| source_map.pat_syntax(*d).ok())
.and_then(|d| {
Some(FileRange {
file_id: d.file_id.file_id()?.into(),
range: d.value.text_range(),
})
})
}),
); );
label.prepend_str("drop("); label.prepend_str("drop(");
label.append_str(")"); label.append_str(")");

View file

@ -3,7 +3,6 @@
//! fn max(x: i32, y: i32) -> i32 { x + y } //! fn max(x: i32, y: i32) -> i32 { x + y }
//! _ = max(/*x*/4, /*y*/4); //! _ = max(/*x*/4, /*y*/4);
//! ``` //! ```
use std::fmt::Display;
use either::Either; use either::Either;
use hir::{Callable, Semantics}; use hir::{Callable, Semantics};
@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints( pub(super) fn hints(
acc: &mut Vec<InlayHint>, acc: &mut Vec<InlayHint>,
FamousDefs(sema, _): &FamousDefs<'_, '_>, FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig, config: &InlayHintsConfig,
_file_id: EditionedFileId, _file_id: EditionedFileId,
expr: ast::Expr, expr: ast::Expr,
@ -37,23 +36,29 @@ pub(super) fn hints(
.filter_map(|(p, arg)| { .filter_map(|(p, arg)| {
// Only annotate hints for expressions that exist in the original file // Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?; let range = sema.original_range_opt(arg.syntax())?;
let source = sema.source(p)?; let param_name = p.name(sema.db)?;
let (param_name, name_syntax) = match source.value.as_ref() { Some((p, param_name, arg, range))
Either::Left(pat) => (pat.name()?, pat.name()),
Either::Right(param) => match param.pat()? {
ast::Pat::IdentPat(it) => (it.name()?, it.name()),
_ => return None,
},
};
Some((name_syntax, param_name, arg, range))
}) })
.filter(|(_, param_name, arg, _)| { .filter(|(_, param_name, arg, _)| {
!should_hide_param_name_hint(sema, &callable, &param_name.text(), arg) !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg)
}) })
.map(|(param, param_name, _, hir::FileRange { range, .. })| { .map(|(param, param_name, _, hir::FileRange { range, .. })| {
let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); let colon = if config.render_colons { ":" } else { "" };
let label = InlayHintLabel::simple(
let label = render_label(&param_name, config, linked_location); format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
None,
config.lazy_location_opt(|| {
let source = sema.source(param)?;
let name_syntax = match source.value.as_ref() {
Either::Left(pat) => pat.name(),
Either::Right(param) => match param.pat()? {
ast::Pat::IdentPat(it) => it.name(),
_ => None,
},
}?;
sema.original_range_opt(name_syntax.syntax()).map(Into::into)
}),
);
InlayHint { InlayHint {
range, range,
kind: InlayKind::Parameter, kind: InlayKind::Parameter,
@ -70,16 +75,6 @@ pub(super) fn hints(
Some(()) Some(())
} }
pub(super) fn render_label(
param_name: impl Display,
config: &InlayHintsConfig,
linked_location: Option<hir::FileRange>,
) -> InlayHintLabel {
let colon = if config.render_colons { ":" } else { "" };
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into))
}
fn get_callable( fn get_callable(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
expr: &ast::Expr, expr: &ast::Expr,

View file

@ -91,7 +91,8 @@ pub use crate::{
inlay_hints::{ inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
LifetimeElisionHints,
}, },
join_lines::JoinLinesConfig, join_lines::JoinLinesConfig,
markup::Markup, markup::Markup,

View file

@ -11,8 +11,8 @@ use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve, Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty,
NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
}; };
use ide_db::{assists, rust_doc::format_docs, FxHasher}; use ide_db::{assists, rust_doc::format_docs, FxHasher};
@ -549,12 +549,11 @@ pub(crate) fn inlay_hint(
) -> Cancellable<lsp_types::InlayHint> { ) -> Cancellable<lsp_types::InlayHint> {
let hint_needs_resolve = |hint: &InlayHint| -> Option<TextRange> { let hint_needs_resolve = |hint: &InlayHint| -> Option<TextRange> {
hint.resolve_parent.filter(|_| { hint.resolve_parent.filter(|_| {
hint.text_edit.is_some() hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy)
|| hint || hint.label.parts.iter().any(|part| {
.label part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy)
.parts || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy)
.iter() })
.any(|part| part.linked_location.is_some() || part.tooltip.is_some())
}) })
}; };
@ -569,22 +568,21 @@ pub(crate) fn inlay_hint(
}); });
let mut something_to_resolve = false; let mut something_to_resolve = false;
let text_edits = if snap let text_edits = inlay_hint
.config .text_edit
.visual_studio_code_version() .take()
.is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) .and_then(|it| match it {
&& resolve_range_and_hash.is_some() LazyProperty::Computed(it) => Some(it),
&& fields_to_resolve.resolve_text_edits LazyProperty::Lazy => {
{ something_to_resolve |=
something_to_resolve |= inlay_hint.text_edit.is_some(); snap.config.visual_studio_code_version().is_none_or(|version| {
None VersionReq::parse(">=1.86.0").unwrap().matches(version)
} else { }) && resolve_range_and_hash.is_some()
inlay_hint && fields_to_resolve.resolve_text_edits;
.text_edit None
.take() }
.and_then(|it| it.computed()) })
.map(|it| text_edit_vec(line_index, it)) .map(|it| text_edit_vec(line_index, it));
};
let (label, tooltip) = inlay_hint_label( let (label, tooltip) = inlay_hint_label(
snap, snap,
fields_to_resolve, fields_to_resolve,
@ -637,22 +635,23 @@ fn inlay_hint_label(
let (label, tooltip) = match &*label.parts { let (label, tooltip) = match &*label.parts {
[InlayHintLabelPart { linked_location: None, .. }] => { [InlayHintLabelPart { linked_location: None, .. }] => {
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { let tooltip = tooltip.and_then(|it| match it {
*something_to_resolve |= tooltip.is_some(); LazyProperty::Computed(it) => Some(it),
None LazyProperty::Lazy => {
} else { *something_to_resolve |=
match tooltip.and_then(|it| it.computed()) { needs_resolve && fields_to_resolve.resolve_hint_tooltip;
Some(ide::InlayTooltip::String(s)) => { None
Some(lsp_types::InlayHintTooltip::String(s))
}
Some(ide::InlayTooltip::Markdown(s)) => {
Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: s,
}))
}
None => None,
} }
});
let hint_tooltip = match tooltip {
Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)),
Some(ide::InlayTooltip::Markdown(s)) => {
Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: s,
}))
}
None => None,
}; };
(lsp_types::InlayHintLabel::String(text), hint_tooltip) (lsp_types::InlayHintLabel::String(text), hint_tooltip)
} }
@ -661,31 +660,38 @@ fn inlay_hint_label(
.parts .parts
.into_iter() .into_iter()
.map(|part| { .map(|part| {
let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { let tooltip = part.tooltip.and_then(|it| match it {
*something_to_resolve |= part.tooltip.is_some(); LazyProperty::Computed(it) => Some(it),
None LazyProperty::Lazy => {
} else { *something_to_resolve |= fields_to_resolve.resolve_label_tooltip;
match part.tooltip.and_then(|it| it.computed()) { None
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintLabelPartTooltip::String(s))
}
Some(ide::InlayTooltip::Markdown(s)) => {
Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: s,
},
))
}
None => None,
} }
});
let tooltip = match tooltip {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintLabelPartTooltip::String(s))
}
Some(ide::InlayTooltip::Markdown(s)) => {
Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: s,
},
))
}
None => None,
}; };
let location = if needs_resolve && fields_to_resolve.resolve_label_location { let location = part
*something_to_resolve |= part.linked_location.is_some(); .linked_location
None .and_then(|it| match it {
} else { LazyProperty::Computed(it) => Some(it),
part.linked_location.map(|range| location(snap, range)).transpose()? LazyProperty::Lazy => {
}; *something_to_resolve |= fields_to_resolve.resolve_label_location;
None
}
})
.map(|range| location(snap, range))
.transpose()?;
Ok(lsp_types::InlayHintLabelPart { Ok(lsp_types::InlayHintLabelPart {
value: part.text, value: part.text,
tooltip, tooltip,