Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #12263 - andylizi:hide-type-hint-closure, r=Veykril
feat: hide type inlay hints for initializations of closures

This PR adds an option to hide the inlay hints for `let IDENT_PAT = CLOSURE_EXPR;`, which is a somewhat common coding pattern. Currently the inlay hints for the assigned variable and the closure expression itself are both displayed, making it rather repetitive.
In order to be consistent with closure return type hints, only closures with block bodies will be hid by this option.
Personally I'd feel comfortable making it always enabled (or at least when closure return type hints are enabled), but considering the precedent set in #10761, I introduced an off-by-default option for this.
changelog feature: option to hide type inlay hints for initializations of closures
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 77 | ||||
| -rw-r--r-- | crates/ide/src/static_index.rs | 1 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/config.rs | 6 | ||||
| -rw-r--r-- | docs/user/generated_config.adoc | 6 | ||||
| -rw-r--r-- | editors/code/package.json | 5 |
5 files changed, 90 insertions, 5 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 5eb63a42ad..3689bd5a42 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -26,6 +26,7 @@ pub struct InlayHintsConfig { pub lifetime_elision_hints: LifetimeElisionHints, pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, + pub hide_closure_initialization_hints: bool, pub max_length: Option<usize>, pub closing_brace_hints_min_lines: Option<usize>, } @@ -467,10 +468,11 @@ fn closure_ret_hints( return None; } - let param_list = match closure.body() { - Some(ast::Expr::BlockExpr(_)) => closure.param_list()?, - _ => return None, - }; + if !closure_has_block_body(&closure) { + return None; + } + + let param_list = closure.param_list()?; let closure = sema.descend_node_into_attributes(closure.clone()).pop()?; let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted(); @@ -693,7 +695,7 @@ fn bind_pat_hints( let desc_pat = descended.as_ref().unwrap_or(pat); let ty = sema.type_of_pat(&desc_pat.clone().into())?.original; - if should_not_display_type_hint(sema, pat, &ty) { + if should_not_display_type_hint(sema, config, pat, &ty) { return None; } @@ -848,6 +850,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir fn should_not_display_type_hint( sema: &Semantics<RootDatabase>, + config: &InlayHintsConfig, bind_pat: &ast::IdentPat, pat_ty: &hir::Type, ) -> bool { @@ -863,6 +866,18 @@ fn should_not_display_type_hint( } } + if config.hide_closure_initialization_hints { + if let Some(parent) = bind_pat.syntax().parent() { + if let Some(it) = ast::LetStmt::cast(parent.clone()) { + if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { + if closure_has_block_body(&closure) { + return true; + } + } + } + } + } + for node in bind_pat.syntax().ancestors() { match_ast! { match node { @@ -889,6 +904,10 @@ fn should_not_display_type_hint( false } +fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { + matches!(closure.body(), Some(ast::Expr::BlockExpr(_))) +} + fn should_hide_param_name_hint( sema: &Semantics<RootDatabase>, callable: &hir::Callable, @@ -1083,6 +1102,7 @@ mod tests { reborrow_hints: ReborrowHints::Always, binding_mode_hints: false, hide_named_constructor_hints: false, + hide_closure_initialization_hints: false, param_names_for_lifetime_elision_hints: false, max_length: None, closing_brace_hints_min_lines: None, @@ -2035,6 +2055,53 @@ fn main() { } #[test] + fn skip_closure_type_hints() { + check_with_config( + InlayHintsConfig { + type_hints: true, + hide_closure_initialization_hints: true, + ..DISABLED_CONFIG + }, + r#" +//- minicore: fn +fn main() { + let multiple_2 = |x: i32| { x * 2 }; + + let multiple_2 = |x: i32| x * 2; + // ^^^^^^^^^^ |i32| -> i32 + + let (not) = (|x: bool| { !x }); + // ^^^ |bool| -> bool + + let (is_zero, _b) = (|x: usize| { x == 0 }, false); + // ^^^^^^^ |usize| -> bool + // ^^ bool + + let plus_one = |x| { x + 1 }; + // ^ u8 + foo(plus_one); + + let add_mul = bar(|x: u8| { x + 1 }); + // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized + + let closure = if let Some(6) = add_mul(2).checked_sub(1) { + // ^^^^^^^ fn(i32) -> i32 + |x: i32| { x * 2 } + } else { + |x: i32| { x * 3 } + }; +} + +fn foo(f: impl FnOnce(u8) -> u8) {} + +fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 { + move |x: u8| f(x) * 2 +} +"#, + ); + } + + #[test] fn hint_truncation() { check_with_config( InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 01d7213630..006b18b7fd 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -113,6 +113,7 @@ impl StaticIndex<'_> { lifetime_elision_hints: crate::LifetimeElisionHints::Never, reborrow_hints: crate::ReborrowHints::Never, hide_named_constructor_hints: false, + hide_closure_initialization_hints: false, param_names_for_lifetime_elision_hints: false, binding_mode_hints: false, max_length: Some(25), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index afa83e6206..d7ae4c72f5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -281,6 +281,9 @@ config_data! { inlayHints_renderColons: bool = "true", /// Whether to show inlay type hints for variables. inlayHints_typeHints_enable: bool = "true", + /// Whether to hide inlay type hints for `let` statements that initialize to a closure. + /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + inlayHints_typeHints_hideClosureInitialization: bool = "false", /// Whether to hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = "false", @@ -1000,6 +1003,9 @@ impl Config { LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial, }, hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor, + hide_closure_initialization_hints: self + .data + .inlayHints_typeHints_hideClosureInitialization, reborrow_hints: match self.data.inlayHints_reborrowHints_enable { ReborrowHintsDef::Always => ide::ReborrowHints::Always, ReborrowHintsDef::Never => ide::ReborrowHints::Never, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 30722af08c..955f085d3f 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -407,6 +407,12 @@ Whether to render leading colons for type hints, and trailing colons for paramet -- Whether to show inlay type hints for variables. -- +[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`):: ++ +-- +Whether to hide inlay type hints for `let` statements that initialize to a closure. +Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. +-- [[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index e81dfff278..f46c7ea92d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -868,6 +868,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.inlayHints.typeHints.hideClosureInitialization": { + "markdownDescription": "Whether to hide inlay type hints for `let` statements that initialize to a closure.\nOnly applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.", + "default": false, + "type": "boolean" + }, "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": { "markdownDescription": "Whether to hide inlay type hints for constructors.", "default": false, |