Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/inlay_hints.rs')
-rw-r--r--crates/ide/src/inlay_hints.rs71
1 files changed, 49 insertions, 22 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 040f1f72a6..2fc3629252 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
use std::{
fmt::{self, Write},
- mem::take,
+ mem::{self, take},
};
use either::Either;
@@ -297,6 +297,17 @@ 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 {
+ Lazy::Lazy
+ } else {
+ let edit = finish();
+ never!(edit.is_empty(), "inlay hint produced an empty text edit");
+ Lazy::Computed(edit)
+ }
+ }
+}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct InlayFieldsToResolve {
@@ -408,12 +419,32 @@ 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<TextEdit>,
+ pub text_edit: Option<Lazy<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> {
+ Computed(T),
+ Lazy,
+}
+
+impl<T> Lazy<T> {
+ pub fn computed(self) -> Option<T> {
+ match self {
+ Lazy::Computed(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub fn is_lazy(&self) -> bool {
+ matches!(self, Self::Lazy)
+ }
+}
+
impl std::hash::Hash for InlayHint {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.range.hash(state);
@@ -422,7 +453,7 @@ impl std::hash::Hash for InlayHint {
self.pad_right.hash(state);
self.kind.hash(state);
self.label.hash(state);
- self.text_edit.is_some().hash(state);
+ mem::discriminant(&self.text_edit).hash(state);
}
}
@@ -439,10 +470,6 @@ impl InlayHint {
resolve_parent: None,
}
}
-
- pub fn needs_resolve(&self) -> Option<TextRange> {
- self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
- }
}
#[derive(Debug, Hash)]
@@ -503,10 +530,6 @@ impl InlayHintLabel {
}
self.parts.push(part);
}
-
- pub fn needs_resolve(&self) -> bool {
- self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
- }
}
impl From<String> for InlayHintLabel {
@@ -725,19 +748,22 @@ fn hint_iterator(
fn ty_to_text_edit(
sema: &Semantics<'_, RootDatabase>,
+ config: &InlayHintsConfig,
node_for_hint: &SyntaxNode,
ty: &hir::Type,
offset_to_insert: TextSize,
- prefix: String,
-) -> Option<TextEdit> {
- let scope = sema.scope(node_for_hint)?;
+ prefix: impl Into<String>,
+) -> Option<Lazy<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow?
- let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
-
- let mut builder = TextEdit::builder();
- builder.insert(offset_to_insert, prefix);
- builder.insert(offset_to_insert, rendered);
- Some(builder.finish())
+ let rendered = sema
+ .scope(node_for_hint)
+ .and_then(|scope| ty.display_source_code(scope.db, scope.module().into(), false).ok())?;
+ Some(config.lazy_text_edit(|| {
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix.into());
+ builder.insert(offset_to_insert, rendered);
+ builder.finish()
+ }))
}
fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
@@ -847,7 +873,7 @@ mod tests {
let edits = inlay_hints
.into_iter()
- .filter_map(|hint| hint.text_edit)
+ .filter_map(|hint| hint.text_edit?.computed())
.reduce(|mut acc, next| {
acc.union(next).expect("merging text edits failed");
acc
@@ -867,7 +893,8 @@ mod tests {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
- let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+ let edits: Vec<_> =
+ inlay_hints.into_iter().filter_map(|hint| hint.text_edit?.computed()).collect();
assert!(edits.is_empty(), "unexpected edits: {edits:?}");
}