From b89c4f0a0529ca90dbe13d41fdd5e52331770900 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 20 Dec 2022 22:07:00 +0000 Subject: [PATCH 1/4] Implement postfix adjustment hints I'd say "First stab at implementing..." but I've been working on this for a month already lol --- crates/ide/src/inlay_hints.rs | 3 + crates/ide/src/inlay_hints/adjustment.rs | 252 ++++++++++++++++++++--- crates/ide/src/static_index.rs | 1 + crates/rust-analyzer/src/config.rs | 3 + crates/rust-analyzer/src/to_proto.rs | 4 + docs/user/generated_config.adoc | 5 + editors/code/package.json | 5 + 7 files changed, 246 insertions(+), 27 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 86d25e2f5ad..7315a37ebc7 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -35,6 +35,7 @@ pub struct InlayHintsConfig { pub parameter_hints: bool, pub chaining_hints: bool, pub adjustment_hints: AdjustmentHints, + pub adjustment_hints_postfix: bool, pub adjustment_hints_hide_outside_unsafe: bool, pub closure_return_type_hints: ClosureReturnTypeHints, pub binding_mode_hints: bool, @@ -82,6 +83,7 @@ pub enum InlayKind { ClosureReturnTypeHint, GenericParamListHint, AdjustmentHint, + AdjustmentHintPostfix, LifetimeHint, ParameterHint, TypeHint, @@ -446,6 +448,7 @@ mod tests { lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, adjustment_hints: AdjustmentHints::Never, + adjustment_hints_postfix: false, adjustment_hints_hide_outside_unsafe: false, binding_mode_hints: false, hide_named_constructor_hints: false, diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 1c13f31cf24..367bd2f6619 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -5,7 +5,11 @@ //! ``` use hir::{Adjust, AutoBorrow, Mutability, OverloadedDeref, PointerCast, Safety, Semantics}; use ide_db::RootDatabase; -use syntax::ast::{self, AstNode}; + +use syntax::{ + ast::{self, make, AstNode}, + ted, +}; use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind}; @@ -32,28 +36,14 @@ pub(super) fn hints( return None; } - let parent = expr.syntax().parent().and_then(ast::Expr::cast); let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; - let needs_parens = match parent { - Some(parent) => { - match parent { - ast::Expr::AwaitExpr(_) - | ast::Expr::CallExpr(_) - | ast::Expr::CastExpr(_) - | ast::Expr::FieldExpr(_) - | ast::Expr::MethodCallExpr(_) - | ast::Expr::TryExpr(_) => true, - // FIXME: shorthands need special casing, though not sure if adjustments are even valid there - ast::Expr::RecordExpr(_) => false, - ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr), - _ => false, - } - } - None => false, - }; - if needs_parens { + + let (needs_outer_parens, needs_inner_parens) = + needs_parens_for_adjustment_hints(expr, config.adjustment_hints_postfix); + + if needs_outer_parens { acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::OpeningParenthesis, @@ -61,7 +51,32 @@ pub(super) fn hints( tooltip: None, }); } - for adjustment in adjustments.into_iter().rev() { + + if config.adjustment_hints_postfix && needs_inner_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::OpeningParenthesis, + label: "(".into(), + tooltip: None, + }); + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::ClosingParenthesis, + label: ")".into(), + tooltip: None, + }); + } + + let (mut tmp0, mut tmp1); + let iter: &mut dyn Iterator = if config.adjustment_hints_postfix { + tmp0 = adjustments.into_iter(); + &mut tmp0 + } else { + tmp1 = adjustments.into_iter().rev(); + &mut tmp1 + }; + + for adjustment in iter { if adjustment.source == adjustment.target { continue; } @@ -97,12 +112,34 @@ pub(super) fn hints( }; acc.push(InlayHint { range: expr.syntax().text_range(), - kind: InlayKind::AdjustmentHint, - label: text.into(), + kind: if config.adjustment_hints_postfix { + InlayKind::AdjustmentHintPostfix + } else { + InlayKind::AdjustmentHint + }, + label: if config.adjustment_hints_postfix { + format!(".{}", text.trim_end()).into() + } else { + text.into() + }, tooltip: None, }); } - if needs_parens { + if !config.adjustment_hints_postfix && needs_inner_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::OpeningParenthesis, + label: "(".into(), + tooltip: None, + }); + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::ClosingParenthesis, + label: ")".into(), + tooltip: None, + }); + } + if needs_outer_parens { acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::ClosingParenthesis, @@ -113,6 +150,69 @@ pub(super) fn hints( Some(()) } +/// Returns whatever we need to add paretheses on the inside and/or outside of `expr`, +/// if we are going to add (`postfix`) adjustments hints to it. +fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) { + // This is a very miserable pile of hacks... + // + // `Expr::needs_parens_in` requires that the expression is the child of the other expression, + // that is supposed to be its parent. + // + // But we want to check what would happen if we add `*`/`.*` to the inner expression. + // To check for inner we need `` expr.needs_parens_in(`*expr`) ``, + // to check for outer we need `` `*expr`.needs_parens_in(parent) ``, + // where "expr" is the `expr` parameter, `*expr` is the editted `expr`, + // and "parent" is the parent of the original expression... + // + // For this we utilize mutable mutable trees, which is a HACK, but it works. + + // Make `&expr`/`expr?` + let dummy_expr = { + // `make::*` function go through a string, so they parse wrongly. + // for example `` make::expr_try(`|| a`) `` would result in a + // `|| (a?)` and not `(|| a)?`. + // + // Thus we need dummy parens to preserve the relationship we want. + // The parens are then simply ignored by the following code. + let dummy_paren = make::expr_paren(expr.clone()); + if postfix { + make::expr_try(dummy_paren) + } else { + make::expr_ref(dummy_paren, false) + } + }; + + // Do the dark mutable tree magic. + // This essentially makes `dummy_expr` and `expr` switch places (families), + // so that `expr`'s parent is not `dummy_expr`'s parent. + let dummy_expr = dummy_expr.clone_for_update(); + let expr = expr.clone_for_update(); + ted::replace(expr.syntax(), dummy_expr.syntax()); + + let parent = dummy_expr.syntax().parent(); + let expr = if postfix { + let ast::Expr::TryExpr(e) = &dummy_expr else { unreachable!() }; + let Some(ast::Expr::ParenExpr(e)) = e.expr() else { unreachable!() }; + + e.expr().unwrap() + } else { + let ast::Expr::RefExpr(e) = &dummy_expr else { unreachable!() }; + let Some(ast::Expr::ParenExpr(e)) = e.expr() else { unreachable!() }; + + e.expr().unwrap() + }; + + // At this point + // - `parent` is the parrent of the original expression + // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) + // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) + + let needs_outer_parens = parent.map_or(false, |p| dummy_expr.needs_parens_in(p)); + let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone()); + + (needs_outer_parens, needs_inner_parens) +} + #[cfg(test)] mod tests { use crate::{ @@ -125,7 +225,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized +//- minicore: coerce_unsized, fn fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -148,12 +248,16 @@ fn main() { //^^^^ let _: unsafe fn() = main as fn(); //^^^^^^^^^^^^ + //^^^^^^^^^^^^( + //^^^^^^^^^^^^) let _: fn() = || {}; //^^^^^ let _: unsafe fn() = || {}; //^^^^^ let _: *const u32 = &mut 0u32 as *mut u32; //^^^^^^^^^^^^^^^^^^^^^ + //^^^^^^^^^^^^^^^^^^^^^( + //^^^^^^^^^^^^^^^^^^^^^) let _: &mut [_] = &mut [0; 0]; //^^^^^^^^^^^ //^^^^^^^^^^^&mut $ @@ -206,6 +310,11 @@ fn main() { //^^^^^^^ //^^^^^^^&mut $ //^^^^^^^* + + let _: &mut dyn Fn() = &mut || (); + //^^^^^^^^^^ + //^^^^^^^^^^&mut $ + //^^^^^^^^^^* } #[derive(Copy, Clone)] @@ -215,12 +324,101 @@ impl Struct { fn by_ref(&self) {} fn by_ref_mut(&mut self) {} } -trait Trait {} -impl Trait for Struct {} "#, ) } + #[test] + fn adjustment_hints_postfix() { + check_with_config( + InlayHintsConfig { + adjustment_hints: AdjustmentHints::Always, + adjustment_hints_postfix: true, + ..DISABLED_CONFIG + }, + r#" +//- minicore: coerce_unsized, fn +fn main() { + + Struct.consume(); + Struct.by_ref(); + //^^^^^^.& + Struct.by_ref_mut(); + //^^^^^^.&mut + + (&Struct).consume(); + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + (&Struct).by_ref(); + + (&mut Struct).consume(); + //^^^^^^^^^^^( + //^^^^^^^^^^^) + //^^^^^^^^^^^.* + (&mut Struct).by_ref(); + //^^^^^^^^^^^( + //^^^^^^^^^^^) + //^^^^^^^^^^^.* + //^^^^^^^^^^^.& + (&mut Struct).by_ref_mut(); + + // Check that block-like expressions don't duplicate hints + let _: &mut [u32] = (&mut []); + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.&mut + //^^^^^^^. + let _: &mut [u32] = { &mut [] }; + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.&mut + //^^^^^^^. + let _: &mut [u32] = unsafe { &mut [] }; + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.&mut + //^^^^^^^. + let _: &mut [u32] = if true { + &mut [] + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.&mut + //^^^^^^^. + } else { + loop {} + //^^^^^^^. + }; + let _: &mut [u32] = match () { () => &mut [] } + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.&mut + //^^^^^^^. + + let _: &mut dyn Fn() = &mut || (); + //^^^^^^^^^^( + //^^^^^^^^^^) + //^^^^^^^^^^.* + //^^^^^^^^^^.&mut + //^^^^^^^^^^. +} + +#[derive(Copy, Clone)] +struct Struct; +impl Struct { + fn consume(self) {} + fn by_ref(&self) {} + fn by_ref_mut(&mut self) {} +} +"#, + ); + } + #[test] fn never_to_never_is_never_shown() { check_with_config( diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 6e31a1420ea..c6cca0d8698 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -115,6 +115,7 @@ impl StaticIndex<'_> { closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, adjustment_hints: crate::AdjustmentHints::Never, + adjustment_hints_postfix: false, adjustment_hints_hide_outside_unsafe: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0f515a6d5cb..4d11a84091c 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -333,6 +333,8 @@ config_data! { inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false", + /// Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc). + inlayHints_expressionAdjustmentHints_postfix: bool = "false", /// Whether to show inlay type hints for elided lifetimes in function signatures. inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. @@ -1252,6 +1254,7 @@ impl Config { }, AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, }, + adjustment_hints_postfix: self.data.inlayHints_expressionAdjustmentHints_postfix, adjustment_hints_hide_outside_unsafe: self .data .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe, diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a12bd3952cc..e736b2ff9a3 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -452,6 +452,7 @@ pub(crate) fn inlay_hint( | InlayKind::ChainingHint | InlayKind::GenericParamListHint | InlayKind::ClosingParenthesis + | InlayKind::AdjustmentHintPostfix | InlayKind::LifetimeHint | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()), }, @@ -465,6 +466,7 @@ pub(crate) fn inlay_hint( | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint | InlayKind::AdjustmentHint + | InlayKind::AdjustmentHintPostfix | InlayKind::LifetimeHint | InlayKind::ParameterHint => false, }), @@ -475,6 +477,7 @@ pub(crate) fn inlay_hint( | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint | InlayKind::AdjustmentHint + | InlayKind::AdjustmentHintPostfix | InlayKind::TypeHint | InlayKind::DiscriminantHint | InlayKind::ClosingBraceHint => false, @@ -493,6 +496,7 @@ pub(crate) fn inlay_hint( | InlayKind::GenericParamListHint | InlayKind::LifetimeHint | InlayKind::AdjustmentHint + | InlayKind::AdjustmentHintPostfix | InlayKind::ClosingBraceHint => None, }, text_edits: None, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index d9794e7052a..60c16ecadf5 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -469,6 +469,11 @@ Whether to show inlay hints for type adjustments. -- Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. -- +[[rust-analyzer.inlayHints.expressionAdjustmentHints.postfix]]rust-analyzer.inlayHints.expressionAdjustmentHints.postfix (default: `false`):: ++ +-- +Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc). +-- [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index f508dde4f60..aeb1d97c5f6 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1000,6 +1000,11 @@ "default": false, "type": "boolean" }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.postfix": { + "markdownDescription": "Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc).", + "default": false, + "type": "boolean" + }, "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", "default": "never", From 12b7f9f7bfb4d50ee29232d9d40430fa22db7157 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 21 Dec 2022 15:00:05 +0000 Subject: [PATCH 2/4] Add an option to minimize parentheses for adjustment hints --- crates/ide/src/inlay_hints.rs | 14 ++- crates/ide/src/inlay_hints/adjustment.rs | 111 ++++++++++++++++++++--- crates/ide/src/lib.rs | 4 +- crates/ide/src/static_index.rs | 3 +- crates/rust-analyzer/src/config.rs | 35 ++++++- docs/user/generated_config.adoc | 4 +- editors/code/package.json | 20 +++- 7 files changed, 162 insertions(+), 29 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 7315a37ebc7..48a7bbfecff 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -35,7 +35,7 @@ pub struct InlayHintsConfig { pub parameter_hints: bool, pub chaining_hints: bool, pub adjustment_hints: AdjustmentHints, - pub adjustment_hints_postfix: bool, + pub adjustment_hints_mode: AdjustmentHintsMode, pub adjustment_hints_hide_outside_unsafe: bool, pub closure_return_type_hints: ClosureReturnTypeHints, pub binding_mode_hints: bool, @@ -75,6 +75,14 @@ pub enum AdjustmentHints { Never, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum AdjustmentHintsMode { + Prefix, + Postfix, + PreferPrefix, + PreferPostfix, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum InlayKind { BindingModeHint, @@ -432,7 +440,7 @@ mod tests { use itertools::Itertools; use test_utils::extract_annotations; - use crate::inlay_hints::AdjustmentHints; + use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::DiscriminantHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; @@ -448,7 +456,7 @@ mod tests { lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, adjustment_hints: AdjustmentHints::Never, - adjustment_hints_postfix: false, + adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, binding_mode_hints: false, hide_named_constructor_hints: false, diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 367bd2f6619..47e854e4bd7 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -11,7 +11,7 @@ use syntax::{ ted, }; -use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -40,8 +40,8 @@ pub(super) fn hints( let desc_expr = descended.as_ref().unwrap_or(expr); let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; - let (needs_outer_parens, needs_inner_parens) = - needs_parens_for_adjustment_hints(expr, config.adjustment_hints_postfix); + let (postfix, needs_outer_parens, needs_inner_parens) = + mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode); if needs_outer_parens { acc.push(InlayHint { @@ -52,7 +52,7 @@ pub(super) fn hints( }); } - if config.adjustment_hints_postfix && needs_inner_parens { + if postfix && needs_inner_parens { acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::OpeningParenthesis, @@ -68,7 +68,7 @@ pub(super) fn hints( } let (mut tmp0, mut tmp1); - let iter: &mut dyn Iterator = if config.adjustment_hints_postfix { + let iter: &mut dyn Iterator = if postfix { tmp0 = adjustments.into_iter(); &mut tmp0 } else { @@ -112,20 +112,16 @@ pub(super) fn hints( }; acc.push(InlayHint { range: expr.syntax().text_range(), - kind: if config.adjustment_hints_postfix { + kind: if postfix { InlayKind::AdjustmentHintPostfix } else { InlayKind::AdjustmentHint }, - label: if config.adjustment_hints_postfix { - format!(".{}", text.trim_end()).into() - } else { - text.into() - }, + label: if postfix { format!(".{}", text.trim_end()).into() } else { text.into() }, tooltip: None, }); } - if !config.adjustment_hints_postfix && needs_inner_parens { + if !postfix && needs_inner_parens { acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::OpeningParenthesis, @@ -150,6 +146,41 @@ pub(super) fn hints( Some(()) } +/// Returns whatever the hint should be postfix and if we need to add paretheses on the inside and/or outside of `expr`, +/// if we are going to add (`postfix`) adjustments hints to it. +fn mode_and_needs_parens_for_adjustment_hints( + expr: &ast::Expr, + mode: AdjustmentHintsMode, +) -> (bool, bool, bool) { + use {std::cmp::Ordering::*, AdjustmentHintsMode::*}; + + match mode { + Prefix | Postfix => { + let postfix = matches!(mode, Postfix); + let (inside, outside) = needs_parens_for_adjustment_hints(expr, postfix); + (postfix, inside, outside) + } + PreferPrefix | PreferPostfix => { + let prefer_postfix = matches!(mode, PreferPostfix); + + let (pre_inside, pre_outside) = needs_parens_for_adjustment_hints(expr, false); + let prefix = (false, pre_inside, pre_outside); + let pre_count = pre_inside as u8 + pre_outside as u8; + + let (post_inside, post_outside) = needs_parens_for_adjustment_hints(expr, true); + let postfix = (true, post_inside, post_outside); + let post_count = post_inside as u8 + post_outside as u8; + + match pre_count.cmp(&post_count) { + Less => prefix, + Greater => postfix, + Equal if prefer_postfix => postfix, + Equal => prefix, + } + } + } +} + /// Returns whatever we need to add paretheses on the inside and/or outside of `expr`, /// if we are going to add (`postfix`) adjustments hints to it. fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) { @@ -217,7 +248,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, mod tests { use crate::{ inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, - AdjustmentHints, InlayHintsConfig, + AdjustmentHints, AdjustmentHintsMode, InlayHintsConfig, }; #[test] @@ -333,7 +364,7 @@ impl Struct { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, - adjustment_hints_postfix: true, + adjustment_hints_mode: AdjustmentHintsMode::Postfix, ..DISABLED_CONFIG }, r#" @@ -419,6 +450,58 @@ impl Struct { ); } + #[test] + fn adjustment_hints_prefer_prefix() { + check_with_config( + InlayHintsConfig { + adjustment_hints: AdjustmentHints::Always, + adjustment_hints_mode: AdjustmentHintsMode::PreferPrefix, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let _: u32 = loop {}; + //^^^^^^^ + + Struct.by_ref(); + //^^^^^^.& + + let (): () = return (); + //^^^^^^^^^ + + struct Struct; + impl Struct { fn by_ref(&self) {} } +} + "#, + ) + } + + #[test] + fn adjustment_hints_prefer_postfix() { + check_with_config( + InlayHintsConfig { + adjustment_hints: AdjustmentHints::Always, + adjustment_hints_mode: AdjustmentHintsMode::PreferPostfix, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let _: u32 = loop {}; + //^^^^^^^. + + Struct.by_ref(); + //^^^^^^.& + + let (): () = return (); + //^^^^^^^^^ + + struct Struct; + impl Struct { fn by_ref(&self) {} } +} + "#, + ) + } + #[test] fn never_to_never_is_never_shown() { check_with_config( diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 200958a4330..239456cb281 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -81,8 +81,8 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ - AdjustmentHints, ClosureReturnTypeHints, DiscriminantHints, InlayHint, InlayHintLabel, - InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, + AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, + InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index c6cca0d8698..a6b30ba1396 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -13,6 +13,7 @@ use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; use crate::{ hover::hover_for_definition, + inlay_hints::AdjustmentHintsMode, moniker::{def_to_moniker, MonikerResult}, parent_module::crates_for, Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig, @@ -115,7 +116,7 @@ impl StaticIndex<'_> { closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, adjustment_hints: crate::AdjustmentHints::Never, - adjustment_hints_postfix: false, + adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 4d11a84091c..27a86db382d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -333,8 +333,8 @@ config_data! { inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false", - /// Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc). - inlayHints_expressionAdjustmentHints_postfix: bool = "false", + /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"", /// Whether to show inlay type hints for elided lifetimes in function signatures. inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. @@ -1254,7 +1254,12 @@ impl Config { }, AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, }, - adjustment_hints_postfix: self.data.inlayHints_expressionAdjustmentHints_postfix, + adjustment_hints_mode: match self.data.inlayHints_expressionAdjustmentHints_mode { + AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix, + AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix, + AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix, + AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix, + }, adjustment_hints_hide_outside_unsafe: self .data .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe, @@ -1771,6 +1776,15 @@ enum DiscriminantHintsDef { Fieldless, } +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +enum AdjustmentHintsModeDef { + Prefix, + Postfix, + PreferPrefix, + PreferPostfix, +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum FilesWatcherDef { @@ -2104,6 +2118,21 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Only show discriminant hints on fieldless enum variants." ] }, + "AdjustmentHintsModeDef" => set! { + "type": "string", + "enum": [ + "prefix", + "postfix", + "prefer_prefix", + "prefer_postfix", + ], + "enumDescriptions": [ + "Always show adjustment hints as prefix (`*expr`).", + "Always show adjustment hints as postfix (`expr.*`).", + "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.", + "Show prefix or postfix depending on which uses less parenthesis, prefering postfix.", + ] + }, "CargoFeaturesDef" => set! { "anyOf": [ { diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 60c16ecadf5..0aaf07ebf38 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -469,10 +469,10 @@ Whether to show inlay hints for type adjustments. -- Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. -- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.postfix]]rust-analyzer.inlayHints.expressionAdjustmentHints.postfix (default: `false`):: +[[rust-analyzer.inlayHints.expressionAdjustmentHints.mode]]rust-analyzer.inlayHints.expressionAdjustmentHints.mode (default: `"prefix"`):: + -- -Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc). +Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). -- [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: + diff --git a/editors/code/package.json b/editors/code/package.json index aeb1d97c5f6..5ffce2f5536 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1000,10 +1000,22 @@ "default": false, "type": "boolean" }, - "rust-analyzer.inlayHints.expressionAdjustmentHints.postfix": { - "markdownDescription": "Whether to show inlay hints for type adjustments as postfix ops (`.*` instead of `*`, etc).", - "default": false, - "type": "boolean" + "rust-analyzer.inlayHints.expressionAdjustmentHints.mode": { + "markdownDescription": "Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).", + "default": "prefix", + "type": "string", + "enum": [ + "prefix", + "postfix", + "prefer_prefix", + "prefer_postfix" + ], + "enumDescriptions": [ + "Always show adjustment hints as prefix (`*expr`).", + "Always show adjustment hints as postfix (`expr.*`).", + "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.", + "Show prefix or postfix depending on which uses less parenthesis, prefering postfix." + ] }, "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", From a9676cfbe3f81515483fb563f20c74e27cfa7c41 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 21 Dec 2022 15:31:57 +0000 Subject: [PATCH 3/4] Add a "bug" test for adjustment hints to check for status quo --- crates/ide/src/inlay_hints/adjustment.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 47e854e4bd7..9f6984b59e9 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -609,4 +609,20 @@ fn a() { "#, ); } + + #[test] + fn bug() { + check_with_config( + InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, + r#" +fn main() { + // These should be identical, but they are not... + + let () = return; + let (): () = return; + //^^^^^^ +} + "#, + ) + } } From b6169c2a2e5a24e9905a2614e6fe469fb37f50c9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 9 Jan 2023 13:37:37 +0000 Subject: [PATCH 4/4] Add a fixme to remove hacks --- crates/ide/src/inlay_hints/adjustment.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 9f6984b59e9..bdd7c05e008 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -196,6 +196,8 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, // and "parent" is the parent of the original expression... // // For this we utilize mutable mutable trees, which is a HACK, but it works. + // + // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this* // Make `&expr`/`expr?` let dummy_expr = {