Skip to content

Commit

Permalink
Make InlayHint::linked_location computation lazy
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Jan 21, 2025
1 parent 831e353 commit 63ceff7
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 72 deletions.
22 changes: 18 additions & 4 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ 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> {
if self.fields_to_resolve.resolve_text_edits {
Expand Down Expand Up @@ -329,6 +330,19 @@ impl InlayHintsConfig {
Lazy::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<Lazy<FileRange>> {
if self.fields_to_resolve.resolve_label_location {
Some(Lazy::Lazy)
} else {
finish().map(Lazy::Computed)
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -509,7 +523,7 @@ impl InlayHintLabel {
pub fn simple(
s: impl Into<String>,
tooltip: Option<Lazy<InlayTooltip>>,
linked_location: Option<FileRange>,
linked_location: Option<Lazy<FileRange>>,
) -> InlayHintLabel {
InlayHintLabel {
parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }],
Expand Down Expand Up @@ -593,7 +607,7 @@ 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<Lazy<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>>,
Expand All @@ -602,7 +616,7 @@ pub struct InlayHintLabelPart {
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);
}
}
Expand Down Expand Up @@ -663,7 +677,7 @@ impl InlayHintLabelBuilder<'_> {
if !text.is_empty() {
self.result.parts.push(InlayHintLabelPart {
text,
linked_location: self.location.take(),
linked_location: self.location.take().map(Lazy::Computed),
tooltip: None,
});
}
Expand Down
18 changes: 12 additions & 6 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
7 changes: 5 additions & 2 deletions crates/ide/src/inlay_hints/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ mod tests {

use crate::{
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},
Lazy,
},
InlayHintsConfig,
};

Expand All @@ -99,7 +102,7 @@ mod tests {
let (analysis, file_id) = fixture::file(ra_fixture);
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| {
if let Some(loc) = &mut hint.linked_location {
if let Some(Lazy::Computed(loc)) = &mut hint.linked_location {
loc.range = TextRange::empty(TextSize::from(0));
}
});
Expand Down
6 changes: 4 additions & 2 deletions crates/ide/src/inlay_hints/closing_brace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T,
};

use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
use crate::{
inlay_hints::Lazy, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
};

pub(super) fn hints(
acc: &mut Vec<InlayHint>,
Expand Down Expand Up @@ -141,7 +143,7 @@ pub(super) fn hints(
acc.push(InlayHint {
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
label: InlayHintLabel::simple(label, None, linked_location.map(Lazy::Computed)),
text_edit: None,
position: InlayHintPosition::After,
pad_left: true,
Expand Down
17 changes: 11 additions & 6 deletions crates/ide/src/inlay_hints/closure_captures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ pub(super) fn hints(
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
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!(
"{}{}",
Expand All @@ -73,8 +69,17 @@ pub(super) fn hints(
}
hint.label.append_part(InlayHintLabelPart {
text: label,
linked_location: source.name().and_then(|name| {
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into)
linked_location: config.lazy_location_opt(|| {
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,
});
Expand Down
53 changes: 29 additions & 24 deletions crates/ide/src/inlay_hints/generic_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ pub(crate) fn hints(
return None;
}

let allowed = match (param, &arg) {
(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 = {
Expand All @@ -60,34 +72,27 @@ pub(crate) fn hints(

let range = sema.original_range_opt(arg.syntax())?.range;

let source_syntax = match param {
hir::GenericParam::TypeParam(it) => {
if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) {
return None;
}
sema.source(it.merge()).map(|it| it.value.syntax().clone())
}
hir::GenericParam::ConstParam(it) => {
if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) {
return None;
}
let syntax = sema.source(it.merge())?.value.syntax().clone();
let const_param = ast::ConstParam::cast(syntax)?;
const_param.name().map(|it| it.syntax().clone())
}
hir::GenericParam::LifetimeParam(it) => {
if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) {
return None;
}
sema.source(it).map(|it| it.value.syntax().clone())
}
};
let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it));
let colon = if config.render_colons { ":" } else { "" };
let label = InlayHintLabel::simple(
format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
None,
linked_location.map(Into::into),
config.lazy_location_opt(|| {
let source_syntax = match param {
hir::GenericParam::TypeParam(it) => {
sema.source(it.merge()).map(|it| it.value.syntax().clone())
}
hir::GenericParam::ConstParam(it) => {
let syntax = sema.source(it.merge())?.value.syntax().clone();
let const_param = ast::ConstParam::cast(syntax)?;
const_param.name().map(|it| it.syntax().clone())
}
hir::GenericParam::LifetimeParam(it) => {
sema.source(it).map(|it| it.value.syntax().clone())
}
};
let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it));
linked_location.map(Into::into)
}),
);

Some(InlayHint {
Expand Down
29 changes: 15 additions & 14 deletions crates/ide/src/inlay_hints/implicit_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub(super) fn hints(
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
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
};
let range = match terminator.span {
Expand Down Expand Up @@ -91,25 +91,26 @@ pub(super) fn hints(
},
MirSpan::Unknown => continue,
};
let binding_source = source_map
.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 binding = &hir.bindings[binding_idx];
let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
if name.starts_with("<ra@") {
continue; // Ignore desugared variables
}
let mut label = InlayHintLabel::simple(
name,
Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))),
binding_source,
None,
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.append_str(")");
Expand Down
24 changes: 11 additions & 13 deletions crates/ide/src/inlay_hints/param_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,21 @@ pub(super) fn hints(
!should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg)
})
.map(|(param, param_name, _, hir::FileRange { range, .. })| {
let linked_location = (|| {
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())
})();

let colon = if config.render_colons { ":" } else { "" };
let label = InlayHintLabel::simple(
format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
None,
linked_location.map(Into::into),
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 {
range,
Expand Down
5 changes: 4 additions & 1 deletion crates/rust-analyzer/src/lsp/to_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,10 @@ fn inlay_hint_label(
*something_to_resolve |= part.linked_location.is_some();
None
} else {
part.linked_location.map(|range| location(snap, range)).transpose()?
part.linked_location
.and_then(|it| it.computed())
.map(|range| location(snap, range))
.transpose()?
};
Ok(lsp_types::InlayHintLabelPart {
value: part.text,
Expand Down

0 comments on commit 63ceff7

Please sign in to comment.