Merge pull request #18376 from Veykril/veykril/push-ptmnsoqzsmqk

feat: Add text edits to more inlay hints
This commit is contained in:
Lukas Wirth 2024-10-23 08:39:25 +00:00 committed by GitHub
commit bde2000a98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 93 additions and 63 deletions

View File

@ -410,19 +410,6 @@ fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
}
}
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
range,
kind,
label: InlayHintLabel::from("("),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
pad_right: false,
resolve_parent: None,
}
}
pub fn needs_resolve(&self) -> Option<TextRange> {
self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
}

View File

@ -3,6 +3,8 @@
//! let _: u32 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0;
//! ```
use std::ops::Not;
use either::Either;
use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
@ -15,6 +17,7 @@
ast::{self, make, AstNode},
ted,
};
use text_edit::TextEditBuilder;
use crate::{
AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
@ -51,15 +54,15 @@ pub(super) fn hints(
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr {
if let [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), source: _, target }] =
&*adjustments
{
// Don't show unnecessary reborrows for these, they will just repeat the inner ones again
if source == target {
if matches!(
&*adjustments,
[Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), target, .. }]
if source == target
) {
return None;
}
}
}
let (postfix, needs_outer_parens, needs_inner_parens) =
mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
@ -101,6 +104,7 @@ pub(super) fn hints(
};
let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
let mut allow_edit = !postfix;
for Adjustment { source, target, kind } in iter {
if source == target {
cov_mark::hit!(same_type_adjustment);
@ -110,6 +114,7 @@ pub(super) fn hints(
// FIXME: Add some nicer tooltips to each of these
let (text, coercion) = match kind {
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
allow_edit = false;
("<never-to-any>", "never to any")
}
Adjust::Deref(None) => ("*", "dereference"),
@ -130,6 +135,7 @@ pub(super) fn hints(
// some of these could be represented via `as` casts, but that's not too nice and
// handling everything as a prefix expr makes the `(` and `)` insertion easier
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
allow_edit = false;
match cast {
PointerCast::ReifyFnPointer => {
("<fn-item-to-fn-pointer>", "fn item to fn pointer")
@ -170,12 +176,41 @@ pub(super) fn hints(
if needs_outer_parens || (!postfix && needs_inner_parens) {
post.label.append_str(")");
}
if !pre.label.parts.is_empty() {
acc.push(pre);
let mut pre = pre.label.parts.is_empty().not().then_some(pre);
let mut post = post.label.parts.is_empty().not().then_some(post);
if pre.is_none() && post.is_none() {
return None;
}
if !post.label.parts.is_empty() {
acc.push(post);
if allow_edit {
let edit = {
let mut b = TextEditBuilder::default();
if let Some(pre) = &pre {
b.insert(
pre.range.start(),
pre.label.parts.iter().map(|part| &*part.text).collect::<String>(),
);
}
if let Some(post) = &post {
b.insert(
post.range.end(),
post.label.parts.iter().map(|part| &*part.text).collect::<String>(),
);
}
b.finish()
};
match (&mut pre, &mut post) {
(Some(pre), Some(post)) => {
pre.text_edit = Some(edit.clone());
post.text_edit = Some(edit);
}
(Some(pre), None) => pre.text_edit = Some(edit),
(None, Some(post)) => post.text_edit = Some(edit),
(None, None) => (),
}
}
acc.extend(pre);
acc.extend(post);
Some(())
}

View File

@ -9,6 +9,7 @@
use span::EditionedFileId;
use syntax::ast::{self, AstNode};
use text_edit::TextEditBuilder;
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
@ -23,16 +24,7 @@ pub(super) fn hints(
return None;
}
let outer_paren_pat = pat
.syntax()
.ancestors()
.skip(1)
.map_while(ast::Pat::cast)
.map_while(|pat| match pat {
ast::Pat::ParenPat(pat) => Some(pat),
_ => None,
})
.last();
let outer_paren_pat = pat.syntax().ancestors().skip(1).map_while(ast::ParenPat::cast).last();
let range = outer_paren_pat.as_ref().map_or_else(
|| match pat {
// for ident patterns that @ bind a name, render the un-ref patterns in front of the inner pattern
@ -70,17 +62,16 @@ pub(super) fn hints(
hint.label.append_str(r);
});
hint.pad_right = was_mut_last;
if !hint.label.parts.is_empty() {
acc.push(hint);
}
let acc_base = acc.len();
match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
let bm = sema.binding_mode_of_pat(pat)?;
let bm = match bm {
hir::BindingMode::Move => return None,
hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
hir::BindingMode::Ref(Mutability::Shared) => "ref",
hir::BindingMode::Move => None,
hir::BindingMode::Ref(Mutability::Mut) => Some("ref mut"),
hir::BindingMode::Ref(Mutability::Shared) => Some("ref"),
};
if let Some(bm) = bm {
acc.push(InlayHint {
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
@ -92,11 +83,9 @@ pub(super) fn hints(
resolve_parent: Some(pat.syntax().text_range()),
});
}
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
acc.push(InlayHint::opening_paren_before(
InlayKind::BindingMode,
pat.syntax().text_range(),
));
hint.label.append_str("(");
acc.push(InlayHint::closing_paren_after(
InlayKind::BindingMode,
pat.syntax().text_range(),
@ -104,6 +93,24 @@ pub(super) fn hints(
}
_ => (),
}
if !hint.label.parts.is_empty() {
acc.push(hint);
}
if let hints @ [_, ..] = &mut acc[acc_base..] {
let mut edit = TextEditBuilder::default();
for h in &mut *hints {
edit.insert(
match h.position {
InlayHintPosition::Before => h.range.start(),
InlayHintPosition::After => h.range.end(),
},
h.label.parts.iter().map(|p| &*p.text).collect(),
);
}
let edit = edit.finish();
hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
}
Some(())
}
@ -154,11 +161,10 @@ fn __(
}
match &(0,) {
(x,) | (x,) => (),
//^^^^^^^^^^^&
//^ ref
//^ ref
//^^^^^^^^^^^(
//^^^^^^^^^^^)
//^^^^^^^^^^^&(
//^ ref
//^ ref
((x,) | (x,)) => (),
//^^^^^^^^^^^^^&
//^ ref

View File

@ -8,6 +8,7 @@
use ide_db::{famous_defs::FamousDefs, RootDatabase};
use span::EditionedFileId;
use syntax::ast::{self, AstNode, HasName};
use text_edit::TextEdit;
use crate::{
DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
@ -65,11 +66,11 @@ fn variant_hints(
let eq_ = if eq_token.is_none() { " =" } else { "" };
let label = InlayHintLabel::simple(
match d {
Ok(x) => {
if x >= 10 {
format!("{eq_} {x} ({x:#X})")
Ok(val) => {
if val >= 10 {
format!("{eq_} {val} ({val:#X})")
} else {
format!("{eq_} {x}")
format!("{eq_} {val}")
}
}
Err(_) => format!("{eq_} ?"),
@ -87,7 +88,7 @@ fn variant_hints(
},
kind: InlayKind::Discriminant,
label,
text_edit: None,
text_edit: d.ok().map(|val| TextEdit::insert(range.start(), format!("{eq_} {val}"))),
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,

View File

@ -9,6 +9,7 @@
ast::{self, AstNode},
SyntaxKind,
};
use text_edit::TextEdit;
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
@ -38,7 +39,7 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".into(),
text_edit: None,
text_edit: Some(TextEdit::insert(t.text_range().start(), "'static ".into())),
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,