Skip to content

Commit f414c6f

Browse files
committed
feat: show truncated parts of inlay hints in tooltips
1 parent eaf37e2 commit f414c6f

File tree

2 files changed

+94
-21
lines changed

2 files changed

+94
-21
lines changed

crates/hir-ty/src/display.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ use crate::{
6464
pub trait HirWrite: fmt::Write {
6565
fn start_location_link(&mut self, _location: ModuleDefId) {}
6666
fn end_location_link(&mut self) {}
67+
/// Returns whether a new truncated part is created during this call. If so,
68+
/// the caller should call `end_truncated` at the end. Otherwise, the caller
69+
/// should not call `end_truncated`. This ensures that the truncated part is
70+
/// not nested.
71+
fn start_truncated(&mut self) -> bool {
72+
false
73+
}
74+
fn end_truncated(&mut self) {}
6775
}
6876

6977
// String will ignore link metadata
@@ -392,19 +400,23 @@ impl HirFormatter<'_> {
392400
sep: &str,
393401
) -> Result<(), HirDisplayError> {
394402
let mut first = true;
403+
let mut in_truncated = false;
395404
for e in iter {
396405
if !first {
397406
write!(self, "{sep}")?;
398407
}
399408
first = false;
400409

401410
// Abbreviate multiple omitted types with a single ellipsis.
402-
if self.should_truncate() {
403-
return write!(self, "{TYPE_HINT_TRUNCATION}");
404-
}
411+
in_truncated = !in_truncated && self.should_truncate() && self.fmt.start_truncated();
405412

406413
e.hir_fmt(self)?;
407414
}
415+
416+
if in_truncated {
417+
self.fmt.end_truncated();
418+
}
419+
408420
Ok(())
409421
}
410422

@@ -599,9 +611,8 @@ impl<T: HirDisplay + Internable> HirDisplay for Interned<T> {
599611

600612
impl HirDisplay for ProjectionTy {
601613
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
602-
if f.should_truncate() {
603-
return write!(f, "{TYPE_HINT_TRUNCATION}");
604-
}
614+
let in_truncated = f.should_truncate() && f.fmt.start_truncated();
615+
605616
let trait_ref = self.trait_ref(f.db);
606617
let self_ty = trait_ref.self_type_parameter(Interner);
607618

@@ -664,19 +675,30 @@ impl HirDisplay for ProjectionTy {
664675
.name
665676
.display(f.db, f.edition())
666677
)?;
678+
667679
let proj_params =
668680
&self.substitution.as_slice(Interner)[trait_ref.substitution.len(Interner)..];
669-
hir_fmt_generics(f, proj_params, None, None)
681+
hir_fmt_generics(f, proj_params, None, None)?;
682+
683+
if in_truncated {
684+
f.fmt.end_truncated();
685+
}
686+
687+
Ok(())
670688
}
671689
}
672690

673691
impl HirDisplay for OpaqueTy {
674692
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
675-
if f.should_truncate() {
676-
return write!(f, "{TYPE_HINT_TRUNCATION}");
693+
let in_truncated = f.should_truncate() && f.fmt.start_truncated();
694+
695+
self.substitution.at(Interner, 0).hir_fmt(f)?;
696+
697+
if in_truncated {
698+
f.fmt.end_truncated();
677699
}
678700

679-
self.substitution.at(Interner, 0).hir_fmt(f)
701+
Ok(())
680702
}
681703
}
682704

@@ -1039,9 +1061,7 @@ impl HirDisplay for Ty {
10391061
&self,
10401062
f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>,
10411063
) -> Result<(), HirDisplayError> {
1042-
if f.should_truncate() {
1043-
return write!(f, "{TYPE_HINT_TRUNCATION}");
1044-
}
1064+
let in_truncated = f.should_truncate() && f.fmt.start_truncated();
10451065

10461066
match self.kind(Interner) {
10471067
TyKind::Never => write!(f, "!")?,
@@ -1451,11 +1471,15 @@ impl HirDisplay for Ty {
14511471
_ => unreachable!(),
14521472
}
14531473
if sig.params().is_empty() {
1454-
} else if f.should_truncate() {
1455-
write!(f, "{TYPE_HINT_TRUNCATION}")?;
14561474
} else {
1475+
let in_truncated = f.should_truncate() && f.fmt.start_truncated();
1476+
14571477
f.write_joined(sig.params(), ", ")?;
1458-
};
1478+
1479+
if in_truncated {
1480+
f.fmt.end_truncated();
1481+
}
1482+
}
14591483
match f.closure_style {
14601484
ClosureStyle::ImplFn => write!(f, ")")?,
14611485
ClosureStyle::RANotation => write!(f, "|")?,
@@ -1632,6 +1656,11 @@ impl HirDisplay for Ty {
16321656
}
16331657
TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
16341658
}
1659+
1660+
if in_truncated {
1661+
f.fmt.end_truncated();
1662+
}
1663+
16351664
Ok(())
16361665
}
16371666
}
@@ -1983,9 +2012,7 @@ impl HirDisplay for TraitRef {
19832012

19842013
impl HirDisplay for WhereClause {
19852014
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
1986-
if f.should_truncate() {
1987-
return write!(f, "{TYPE_HINT_TRUNCATION}");
1988-
}
2015+
let in_truncated = f.should_truncate() && f.fmt.start_truncated();
19892016

19902017
match self {
19912018
WhereClause::Implemented(trait_ref) => {
@@ -2017,6 +2044,11 @@ impl HirDisplay for WhereClause {
20172044
WhereClause::TypeOutlives(..) => {}
20182045
WhereClause::LifetimeOutlives(..) => {}
20192046
}
2047+
2048+
if in_truncated {
2049+
f.fmt.end_truncated();
2050+
}
2051+
20202052
Ok(())
20212053
}
20222054
}

crates/ide/src/inlay_hints.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEd
1212
use ide_db::{FxHashSet, text_edit::TextEdit};
1313
use itertools::Itertools;
1414
use smallvec::{SmallVec, smallvec};
15-
use stdx::never;
15+
use stdx::{always, never};
1616
use syntax::{
1717
SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent,
1818
ast::{self, AstNode, HasGenericParams},
@@ -672,6 +672,8 @@ struct InlayHintLabelBuilder<'a> {
672672
last_part: String,
673673
resolve: bool,
674674
location: Option<LazyProperty<FileRange>>,
675+
tooltip: Option<LazyProperty<InlayTooltip>>,
676+
in_truncated_part: bool,
675677
}
676678

677679
impl fmt::Write for InlayHintLabelBuilder<'_> {
@@ -682,6 +684,10 @@ impl fmt::Write for InlayHintLabelBuilder<'_> {
682684

683685
impl HirWrite for InlayHintLabelBuilder<'_> {
684686
fn start_location_link(&mut self, def: ModuleDefId) {
687+
// If the label is truncated, we do not need to add the location link.
688+
if self.in_truncated_part {
689+
return;
690+
}
685691
never!(self.location.is_some(), "location link is already started");
686692
self.make_new_part();
687693

@@ -697,7 +703,40 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
697703
}
698704

699705
fn end_location_link(&mut self) {
706+
if self.in_truncated_part {
707+
return;
708+
}
709+
self.make_new_part();
710+
}
711+
712+
fn start_truncated(&mut self) -> bool {
713+
never!(self.location.is_some(), "location link is already started");
714+
// If currently in the truncated part, do not create a new part and continue writing into
715+
// the `last_part`.
716+
if self.in_truncated_part {
717+
return false;
718+
}
719+
700720
self.make_new_part();
721+
self.last_part.push_str("```rust\n");
722+
self.in_truncated_part = true;
723+
true
724+
}
725+
726+
fn end_truncated(&mut self) {
727+
always!(self.in_truncated_part, "truncated is not started");
728+
729+
if self.resolve {
730+
self.last_part = "…".to_owned();
731+
self.tooltip = Some(LazyProperty::Lazy);
732+
} else {
733+
let mut tooltip = mem::replace(&mut self.last_part, "…".to_owned());
734+
tooltip.push_str("\n```");
735+
self.tooltip = Some(LazyProperty::Computed(InlayTooltip::Markdown(tooltip)));
736+
}
737+
738+
self.make_new_part();
739+
self.in_truncated_part = false;
701740
}
702741
}
703742

@@ -708,7 +747,7 @@ impl InlayHintLabelBuilder<'_> {
708747
self.result.parts.push(InlayHintLabelPart {
709748
text,
710749
linked_location: self.location.take(),
711-
tooltip: None,
750+
tooltip: self.tooltip.take(),
712751
});
713752
}
714753
}
@@ -778,6 +817,8 @@ fn label_of_ty(
778817
db: sema.db,
779818
last_part: String::new(),
780819
location: None,
820+
tooltip: None,
821+
in_truncated_part: false,
781822
result: InlayHintLabel::default(),
782823
resolve: config.fields_to_resolve.resolve_label_location,
783824
};

0 commit comments

Comments
 (0)