Skip to content

Commit

Permalink
Merge pull request #18991 from Veykril/push-rmqmnrymwmoz
Browse files Browse the repository at this point in the history
Keep already computed inlay hint properties instead of late resolving them
  • Loading branch information
Veykril authored Jan 24, 2025
2 parents 3a163fa + 41e6a87 commit a62e2f5
Show file tree
Hide file tree
Showing 11 changed files with 433 additions and 322 deletions.
41 changes: 26 additions & 15 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use hir_def::{
body::BodyDiagnostic,
data::{adt::VariantData, TraitFlags},
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},
lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
Expand Down Expand Up @@ -2470,20 +2470,31 @@ impl Param {
}

pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
let parent = match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0,
_ => return None,
};
let body = db.body(parent);
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
Some(Local { parent, binding_id: self_param })
} 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 {
None
match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => {
let parent = DefWithBodyId::FunctionId(it);
let body = db.body(parent);
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
Some(Local { parent, binding_id: self_param })
} 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 {
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,
}
}

Expand Down
77 changes: 51 additions & 26 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ fn hints(
) {
closing_brace::hints(hints, sema, config, file_id, 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! {
Expand Down Expand Up @@ -300,22 +300,23 @@ pub struct InlayHintsConfig {
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
}

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 {
Lazy::Lazy
LazyProperty::Lazy
} else {
let edit = finish();
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
&& self.fields_to_resolve.resolve_label_tooltip
{
Lazy::Lazy
LazyProperty::Lazy
} else {
let tooltip = finish();
never!(
Expand All @@ -326,7 +327,20 @@ impl InlayHintsConfig {
.is_empty(),
"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)
}
}
}
Expand Down Expand Up @@ -441,23 +455,23 @@ pub struct InlayHint {
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
/// 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
/// hint does not support resolving.
pub resolve_parent: Option<TextRange>,
}

/// A type signaling that a value is either computed, or is available for computation.
#[derive(Clone, Debug)]
pub enum Lazy<T> {
pub enum LazyProperty<T> {
Computed(T),
Lazy,
}

impl<T> Lazy<T> {
impl<T> LazyProperty<T> {
pub fn computed(self) -> Option<T> {
match self {
Lazy::Computed(it) => Some(it),
LazyProperty::Computed(it) => Some(it),
_ => None,
}
}
Expand Down Expand Up @@ -508,8 +522,8 @@ pub struct InlayHintLabel {
impl InlayHintLabel {
pub fn simple(
s: impl Into<String>,
tooltip: Option<Lazy<InlayTooltip>>,
linked_location: Option<FileRange>,
tooltip: Option<LazyProperty<InlayTooltip>>,
linked_location: Option<LazyProperty<FileRange>>,
) -> InlayHintLabel {
InlayHintLabel {
parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }],
Expand Down Expand Up @@ -593,33 +607,37 @@ pub struct InlayHintLabelPart {
/// refers to (not necessarily the location itself).
/// When setting this, no tooltip must be set on the containing hint, or VS Code will display
/// 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
/// hover requests to show.
pub tooltip: Option<Lazy<InlayTooltip>>,
pub tooltip: Option<LazyProperty<InlayTooltip>>,
}

impl std::hash::Hash for InlayHintLabelPart {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state);
self.linked_location.hash(state);
self.linked_location.is_some().hash(state);
self.tooltip.is_some().hash(state);
}
}

impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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
.debug_struct("InlayHintLabelPart")
.field("text", text)
.field("linked_location", linked_location)
.field(
"tooltip",
&tooltip.as_ref().map_or("", |it| match it {
Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it,
Lazy::Lazy => "",
LazyProperty::Computed(
InlayTooltip::String(it) | InlayTooltip::Markdown(it),
) => it,
LazyProperty::Lazy => "",
}),
)
.finish(),
Expand All @@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> {
db: &'a RootDatabase,
result: InlayHintLabel,
last_part: String,
location: Option<FileRange>,
resolve: bool,
location: Option<LazyProperty<FileRange>>,
}

impl fmt::Write for InlayHintLabelBuilder<'_> {
Expand All @@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) {
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site();
let location =
FileRange { file_id: location.file_id, range: location.focus_or_full_range() };
self.location = Some(location);

self.location = Some(if self.resolve {
LazyProperty::Lazy
} else {
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) {
Expand Down Expand Up @@ -735,6 +759,7 @@ fn label_of_ty(
last_part: String::new(),
location: None,
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 r = label_builder.finish();
Expand Down Expand Up @@ -783,7 +808,7 @@ fn ty_to_text_edit(
ty: &hir::Type,
offset_to_insert: TextSize,
prefix: impl Into<String>,
) -> Option<Lazy<TextEdit>> {
) -> Option<LazyProperty<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow?
let rendered = sema
.scope(node_for_hint)
Expand Down
32 changes: 20 additions & 12 deletions crates/ide/src/inlay_hints/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ pub(super) fn hints(
return None;
}

let linked_location =
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() }
});
let sized_trait = famous_defs.core_marker_Sized();

for param in params.type_or_const_params() {
match param {
Expand All @@ -48,7 +44,17 @@ pub(super) fn hints(
}
hint.parts.push(InlayHintLabelPart {
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,
});
if has_bounds {
Expand Down Expand Up @@ -134,12 +140,14 @@ fn foo<T>() {}
InlayHintLabelPart {
text: "Sized",
linked_location: Some(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 135..140,
},
Computed(
FileRangeWrapper {
file_id: FileId(
1,
),
range: 135..140,
},
),
),
tooltip: "",
},
Expand Down
Loading

0 comments on commit a62e2f5

Please sign in to comment.