Skip to content

Commit af7c97f

Browse files
authored
Merge pull request #18376 from Veykril/veykril/push-ptmnsoqzsmqk
feat: Add text edits to more inlay hints
2 parents c286786 + b837ea4 commit af7c97f

File tree

5 files changed

+93
-63
lines changed

5 files changed

+93
-63
lines changed

crates/ide/src/inlay_hints.rs

-13
Original file line numberDiff line numberDiff line change
@@ -410,19 +410,6 @@ impl InlayHint {
410410
}
411411
}
412412

413-
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
414-
InlayHint {
415-
range,
416-
kind,
417-
label: InlayHintLabel::from("("),
418-
text_edit: None,
419-
position: InlayHintPosition::Before,
420-
pad_left: false,
421-
pad_right: false,
422-
resolve_parent: None,
423-
}
424-
}
425-
426413
pub fn needs_resolve(&self) -> Option<TextRange> {
427414
self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
428415
}

crates/ide/src/inlay_hints/adjustment.rs

+46-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! let _: u32 = /* <never-to-any> */ loop {};
44
//! let _: &u32 = /* &* */ &mut 0;
55
//! ```
6+
use std::ops::Not;
7+
68
use either::Either;
79
use hir::{
810
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
@@ -15,6 +17,7 @@ use syntax::{
1517
ast::{self, make, AstNode},
1618
ted,
1719
};
20+
use text_edit::TextEditBuilder;
1821

1922
use crate::{
2023
AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
@@ -51,13 +54,13 @@ pub(super) fn hints(
5154
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
5255

5356
if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr {
54-
if let [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), source: _, target }] =
55-
&*adjustments
56-
{
57-
// Don't show unnecessary reborrows for these, they will just repeat the inner ones again
58-
if source == target {
59-
return None;
60-
}
57+
// Don't show unnecessary reborrows for these, they will just repeat the inner ones again
58+
if matches!(
59+
&*adjustments,
60+
[Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), target, .. }]
61+
if source == target
62+
) {
63+
return None;
6164
}
6265
}
6366

@@ -101,6 +104,7 @@ pub(super) fn hints(
101104
};
102105
let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
103106

107+
let mut allow_edit = !postfix;
104108
for Adjustment { source, target, kind } in iter {
105109
if source == target {
106110
cov_mark::hit!(same_type_adjustment);
@@ -110,6 +114,7 @@ pub(super) fn hints(
110114
// FIXME: Add some nicer tooltips to each of these
111115
let (text, coercion) = match kind {
112116
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
117+
allow_edit = false;
113118
("<never-to-any>", "never to any")
114119
}
115120
Adjust::Deref(None) => ("*", "dereference"),
@@ -130,6 +135,7 @@ pub(super) fn hints(
130135
// some of these could be represented via `as` casts, but that's not too nice and
131136
// handling everything as a prefix expr makes the `(` and `)` insertion easier
132137
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
138+
allow_edit = false;
133139
match cast {
134140
PointerCast::ReifyFnPointer => {
135141
("<fn-item-to-fn-pointer>", "fn item to fn pointer")
@@ -170,12 +176,41 @@ pub(super) fn hints(
170176
if needs_outer_parens || (!postfix && needs_inner_parens) {
171177
post.label.append_str(")");
172178
}
173-
if !pre.label.parts.is_empty() {
174-
acc.push(pre);
179+
180+
let mut pre = pre.label.parts.is_empty().not().then_some(pre);
181+
let mut post = post.label.parts.is_empty().not().then_some(post);
182+
if pre.is_none() && post.is_none() {
183+
return None;
175184
}
176-
if !post.label.parts.is_empty() {
177-
acc.push(post);
185+
if allow_edit {
186+
let edit = {
187+
let mut b = TextEditBuilder::default();
188+
if let Some(pre) = &pre {
189+
b.insert(
190+
pre.range.start(),
191+
pre.label.parts.iter().map(|part| &*part.text).collect::<String>(),
192+
);
193+
}
194+
if let Some(post) = &post {
195+
b.insert(
196+
post.range.end(),
197+
post.label.parts.iter().map(|part| &*part.text).collect::<String>(),
198+
);
199+
}
200+
b.finish()
201+
};
202+
match (&mut pre, &mut post) {
203+
(Some(pre), Some(post)) => {
204+
pre.text_edit = Some(edit.clone());
205+
post.text_edit = Some(edit);
206+
}
207+
(Some(pre), None) => pre.text_edit = Some(edit),
208+
(None, Some(post)) => post.text_edit = Some(edit),
209+
(None, None) => (),
210+
}
178211
}
212+
acc.extend(pre);
213+
acc.extend(post);
179214
Some(())
180215
}
181216

crates/ide/src/inlay_hints/binding_mode.rs

+39-33
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use ide_db::famous_defs::FamousDefs;
99

1010
use span::EditionedFileId;
1111
use syntax::ast::{self, AstNode};
12+
use text_edit::TextEditBuilder;
1213

1314
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
1415

@@ -23,16 +24,7 @@ pub(super) fn hints(
2324
return None;
2425
}
2526

26-
let outer_paren_pat = pat
27-
.syntax()
28-
.ancestors()
29-
.skip(1)
30-
.map_while(ast::Pat::cast)
31-
.map_while(|pat| match pat {
32-
ast::Pat::ParenPat(pat) => Some(pat),
33-
_ => None,
34-
})
35-
.last();
27+
let outer_paren_pat = pat.syntax().ancestors().skip(1).map_while(ast::ParenPat::cast).last();
3628
let range = outer_paren_pat.as_ref().map_or_else(
3729
|| match pat {
3830
// for ident patterns that @ bind a name, render the un-ref patterns in front of the inner pattern
@@ -70,40 +62,55 @@ pub(super) fn hints(
7062
hint.label.append_str(r);
7163
});
7264
hint.pad_right = was_mut_last;
73-
if !hint.label.parts.is_empty() {
74-
acc.push(hint);
75-
}
65+
let acc_base = acc.len();
7666
match pat {
7767
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
7868
let bm = sema.binding_mode_of_pat(pat)?;
7969
let bm = match bm {
80-
hir::BindingMode::Move => return None,
81-
hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
82-
hir::BindingMode::Ref(Mutability::Shared) => "ref",
70+
hir::BindingMode::Move => None,
71+
hir::BindingMode::Ref(Mutability::Mut) => Some("ref mut"),
72+
hir::BindingMode::Ref(Mutability::Shared) => Some("ref"),
8373
};
84-
acc.push(InlayHint {
85-
range: pat.syntax().text_range(),
86-
kind: InlayKind::BindingMode,
87-
label: bm.into(),
88-
text_edit: None,
89-
position: InlayHintPosition::Before,
90-
pad_left: false,
91-
pad_right: true,
92-
resolve_parent: Some(pat.syntax().text_range()),
93-
});
74+
if let Some(bm) = bm {
75+
acc.push(InlayHint {
76+
range: pat.syntax().text_range(),
77+
kind: InlayKind::BindingMode,
78+
label: bm.into(),
79+
text_edit: None,
80+
position: InlayHintPosition::Before,
81+
pad_left: false,
82+
pad_right: true,
83+
resolve_parent: Some(pat.syntax().text_range()),
84+
});
85+
}
9486
}
9587
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
96-
acc.push(InlayHint::opening_paren_before(
97-
InlayKind::BindingMode,
98-
pat.syntax().text_range(),
99-
));
88+
hint.label.append_str("(");
10089
acc.push(InlayHint::closing_paren_after(
10190
InlayKind::BindingMode,
10291
pat.syntax().text_range(),
10392
));
10493
}
10594
_ => (),
10695
}
96+
if !hint.label.parts.is_empty() {
97+
acc.push(hint);
98+
}
99+
100+
if let hints @ [_, ..] = &mut acc[acc_base..] {
101+
let mut edit = TextEditBuilder::default();
102+
for h in &mut *hints {
103+
edit.insert(
104+
match h.position {
105+
InlayHintPosition::Before => h.range.start(),
106+
InlayHintPosition::After => h.range.end(),
107+
},
108+
h.label.parts.iter().map(|p| &*p.text).collect(),
109+
);
110+
}
111+
let edit = edit.finish();
112+
hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
113+
}
107114

108115
Some(())
109116
}
@@ -154,11 +161,10 @@ fn __(
154161
}
155162
match &(0,) {
156163
(x,) | (x,) => (),
157-
//^^^^^^^^^^^&
164+
//^^^^^^^^^^^)
165+
//^^^^^^^^^^^&(
158166
//^ ref
159167
//^ ref
160-
//^^^^^^^^^^^(
161-
//^^^^^^^^^^^)
162168
((x,) | (x,)) => (),
163169
//^^^^^^^^^^^^^&
164170
//^ ref

crates/ide/src/inlay_hints/discriminant.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use hir::Semantics;
88
use ide_db::{famous_defs::FamousDefs, RootDatabase};
99
use span::EditionedFileId;
1010
use syntax::ast::{self, AstNode, HasName};
11+
use text_edit::TextEdit;
1112

1213
use crate::{
1314
DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
@@ -65,11 +66,11 @@ fn variant_hints(
6566
let eq_ = if eq_token.is_none() { " =" } else { "" };
6667
let label = InlayHintLabel::simple(
6768
match d {
68-
Ok(x) => {
69-
if x >= 10 {
70-
format!("{eq_} {x} ({x:#X})")
69+
Ok(val) => {
70+
if val >= 10 {
71+
format!("{eq_} {val} ({val:#X})")
7172
} else {
72-
format!("{eq_} {x}")
73+
format!("{eq_} {val}")
7374
}
7475
}
7576
Err(_) => format!("{eq_} ?"),
@@ -87,7 +88,7 @@ fn variant_hints(
8788
},
8889
kind: InlayKind::Discriminant,
8990
label,
90-
text_edit: None,
91+
text_edit: d.ok().map(|val| TextEdit::insert(range.start(), format!("{eq_} {val}"))),
9192
position: InlayHintPosition::After,
9293
pad_left: false,
9394
pad_right: false,

crates/ide/src/inlay_hints/implicit_static.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use syntax::{
99
ast::{self, AstNode},
1010
SyntaxKind,
1111
};
12+
use text_edit::TextEdit;
1213

1314
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
1415

@@ -38,7 +39,7 @@ pub(super) fn hints(
3839
range: t.text_range(),
3940
kind: InlayKind::Lifetime,
4041
label: "'static".into(),
41-
text_edit: None,
42+
text_edit: Some(TextEdit::insert(t.text_range().start(), "'static ".into())),
4243
position: InlayHintPosition::After,
4344
pad_left: false,
4445
pad_right: true,

0 commit comments

Comments
 (0)