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> { pub fn needs_resolve(&self) -> Option<TextRange> {
self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve()) 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 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0; //! let _: &u32 = /* &* */ &mut 0;
//! ``` //! ```
use std::ops::Not;
use either::Either; use either::Either;
use hir::{ use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
@ -15,6 +17,7 @@
ast::{self, make, AstNode}, ast::{self, make, AstNode},
ted, ted,
}; };
use text_edit::TextEditBuilder;
use crate::{ use crate::{
AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart, AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
@ -51,13 +54,13 @@ pub(super) fn hints(
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; 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 ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr {
if let [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), source: _, target }] = // Don't show unnecessary reborrows for these, they will just repeat the inner ones again
&*adjustments if matches!(
{ &*adjustments,
// Don't show unnecessary reborrows for these, they will just repeat the inner ones again [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), target, .. }]
if source == target { if source == target
return None; ) {
} return None;
} }
} }
@ -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 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 { for Adjustment { source, target, kind } in iter {
if source == target { if source == target {
cov_mark::hit!(same_type_adjustment); cov_mark::hit!(same_type_adjustment);
@ -110,6 +114,7 @@ pub(super) fn hints(
// FIXME: Add some nicer tooltips to each of these // FIXME: Add some nicer tooltips to each of these
let (text, coercion) = match kind { let (text, coercion) = match kind {
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
allow_edit = false;
("<never-to-any>", "never to any") ("<never-to-any>", "never to any")
} }
Adjust::Deref(None) => ("*", "dereference"), 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 // 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 // handling everything as a prefix expr makes the `(` and `)` insertion easier
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => { Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
allow_edit = false;
match cast { match cast {
PointerCast::ReifyFnPointer => { PointerCast::ReifyFnPointer => {
("<fn-item-to-fn-pointer>", "fn item to fn pointer") ("<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) { if needs_outer_parens || (!postfix && needs_inner_parens) {
post.label.append_str(")"); 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() { if allow_edit {
acc.push(post); 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(()) Some(())
} }

View File

@ -9,6 +9,7 @@
use span::EditionedFileId; use span::EditionedFileId;
use syntax::ast::{self, AstNode}; use syntax::ast::{self, AstNode};
use text_edit::TextEditBuilder;
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
@ -23,16 +24,7 @@ pub(super) fn hints(
return None; return None;
} }
let outer_paren_pat = pat let outer_paren_pat = pat.syntax().ancestors().skip(1).map_while(ast::ParenPat::cast).last();
.syntax()
.ancestors()
.skip(1)
.map_while(ast::Pat::cast)
.map_while(|pat| match pat {
ast::Pat::ParenPat(pat) => Some(pat),
_ => None,
})
.last();
let range = outer_paren_pat.as_ref().map_or_else( let range = outer_paren_pat.as_ref().map_or_else(
|| match pat { || match pat {
// for ident patterns that @ bind a name, render the un-ref patterns in front of the inner pattern // for ident patterns that @ bind a name, render the un-ref patterns in front of the inner pattern
@ -70,33 +62,30 @@ pub(super) fn hints(
hint.label.append_str(r); hint.label.append_str(r);
}); });
hint.pad_right = was_mut_last; hint.pad_right = was_mut_last;
if !hint.label.parts.is_empty() { let acc_base = acc.len();
acc.push(hint);
}
match pat { match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { 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 = sema.binding_mode_of_pat(pat)?;
let bm = match bm { let bm = match bm {
hir::BindingMode::Move => return None, hir::BindingMode::Move => None,
hir::BindingMode::Ref(Mutability::Mut) => "ref mut", hir::BindingMode::Ref(Mutability::Mut) => Some("ref mut"),
hir::BindingMode::Ref(Mutability::Shared) => "ref", hir::BindingMode::Ref(Mutability::Shared) => Some("ref"),
}; };
acc.push(InlayHint { if let Some(bm) = bm {
range: pat.syntax().text_range(), acc.push(InlayHint {
kind: InlayKind::BindingMode, range: pat.syntax().text_range(),
label: bm.into(), kind: InlayKind::BindingMode,
text_edit: None, label: bm.into(),
position: InlayHintPosition::Before, text_edit: None,
pad_left: false, position: InlayHintPosition::Before,
pad_right: true, pad_left: false,
resolve_parent: Some(pat.syntax().text_range()), pad_right: true,
}); resolve_parent: Some(pat.syntax().text_range()),
});
}
} }
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
acc.push(InlayHint::opening_paren_before( hint.label.append_str("(");
InlayKind::BindingMode,
pat.syntax().text_range(),
));
acc.push(InlayHint::closing_paren_after( acc.push(InlayHint::closing_paren_after(
InlayKind::BindingMode, InlayKind::BindingMode,
pat.syntax().text_range(), 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(()) Some(())
} }
@ -154,11 +161,10 @@ fn __(
} }
match &(0,) { match &(0,) {
(x,) | (x,) => (), (x,) | (x,) => (),
//^^^^^^^^^^^& //^^^^^^^^^^^)
//^^^^^^^^^^^&(
//^ ref //^ ref
//^ ref //^ ref
//^^^^^^^^^^^(
//^^^^^^^^^^^)
((x,) | (x,)) => (), ((x,) | (x,)) => (),
//^^^^^^^^^^^^^& //^^^^^^^^^^^^^&
//^ ref //^ ref

View File

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

View File

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