feat: add inlay hints for generic parameters

fixes #11091

By default, only hints for const generic parameters are shown.
This commit is contained in:
Liao Junxuan 2024-07-01 22:11:30 +08:00
parent a5b21ea0aa
commit 35b4957b80
No known key found for this signature in database
GPG key ID: ACFD0D166CA817F4
10 changed files with 443 additions and 14 deletions

View file

@ -29,6 +29,7 @@ mod closure_captures;
mod closure_ret; mod closure_ret;
mod discriminant; mod discriminant;
mod fn_lifetime_fn; mod fn_lifetime_fn;
mod generic_param;
mod implicit_drop; mod implicit_drop;
mod implicit_static; mod implicit_static;
mod param_name; mod param_name;
@ -40,6 +41,7 @@ pub struct InlayHintsConfig {
pub type_hints: bool, pub type_hints: bool,
pub discriminant_hints: DiscriminantHints, pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool, pub parameter_hints: bool,
pub generic_parameter_hints: GenericParameterHints,
pub chaining_hints: bool, pub chaining_hints: bool,
pub adjustment_hints: AdjustmentHints, pub adjustment_hints: AdjustmentHints,
pub adjustment_hints_mode: AdjustmentHintsMode, pub adjustment_hints_mode: AdjustmentHintsMode,
@ -94,6 +96,13 @@ pub enum DiscriminantHints {
Fieldless, Fieldless,
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GenericParameterHints {
pub type_hints: bool,
pub lifetime_hints: bool,
pub const_hints: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum LifetimeElisionHints { pub enum LifetimeElisionHints {
Always, Always,
@ -127,6 +136,7 @@ pub enum InlayKind {
GenericParamList, GenericParamList,
Lifetime, Lifetime,
Parameter, Parameter,
GenericParameter,
Type, Type,
Drop, Drop,
RangeExclusive, RangeExclusive,
@ -447,6 +457,7 @@ fn ty_to_text_edit(
// //
// * types of local variables // * types of local variables
// * names of function arguments // * names of function arguments
// * names of const generic parameters
// * types of chained expressions // * types of chained expressions
// //
// Optionally, one can enable additional hints for // Optionally, one can enable additional hints for
@ -454,6 +465,7 @@ fn ty_to_text_edit(
// * return types of closure expressions // * return types of closure expressions
// * elided lifetimes // * elided lifetimes
// * compiler inserted reborrows // * compiler inserted reborrows
// * names of generic type and lifetime parameters
// //
// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if // Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
// any of the // any of the
@ -543,6 +555,9 @@ fn hints(
node: SyntaxNode, node: SyntaxNode,
) { ) {
closing_brace::hints(hints, sema, config, file_id, node.clone()); closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args);
}
match_ast! { match_ast! {
match node { match node {
ast::Expr(expr) => { ast::Expr(expr) => {
@ -645,13 +660,18 @@ mod tests {
use crate::DiscriminantHints; use crate::DiscriminantHints;
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
use super::{ClosureReturnTypeHints, InlayFieldsToResolve}; use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve};
pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never, discriminant_hints: DiscriminantHints::Never,
render_colons: false, render_colons: false,
type_hints: false, type_hints: false,
parameter_hints: false, parameter_hints: false,
generic_parameter_hints: GenericParameterHints {
type_hints: false,
lifetime_hints: false,
const_hints: false,
},
chaining_hints: false, chaining_hints: false,
lifetime_elision_hints: LifetimeElisionHints::Never, lifetime_elision_hints: LifetimeElisionHints::Never,
closure_return_type_hints: ClosureReturnTypeHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never,

View file

@ -0,0 +1,315 @@
//! Implementation of inlay hints for generic parameters.
use ide_db::{active_parameter::generic_def_for_node, RootDatabase};
use syntax::{
ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName},
AstNode,
};
use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind};
use super::param_name::{is_argument_similar_to_param_name, render_label};
pub(crate) fn hints(
acc: &mut Vec<InlayHint>,
sema: &hir::Semantics<'_, RootDatabase>,
config: &InlayHintsConfig,
node: AnyHasGenericArgs,
) -> Option<()> {
let GenericParameterHints { type_hints, lifetime_hints, const_hints } =
config.generic_parameter_hints;
if !(type_hints || lifetime_hints || const_hints) {
return None;
}
let generic_arg_list = node.generic_arg_list()?;
let (generic_def, _, _, _) =
generic_def_for_node(sema, &generic_arg_list, &node.syntax().first_token()?)?;
let mut args = generic_arg_list.generic_args().peekable();
let start_with_lifetime = matches!(args.peek()?, ast::GenericArg::LifetimeArg(_));
let params = generic_def.params(sema.db).into_iter().filter(|p| {
if let hir::GenericParam::TypeParam(it) = p {
if it.is_implicit(sema.db) {
return false;
}
}
if !start_with_lifetime {
return !matches!(p, hir::GenericParam::LifetimeParam(_));
}
true
});
let hints = params.zip(args).filter_map(|(param, arg)| {
if matches!(arg, ast::GenericArg::AssocTypeArg(_)) {
return None;
}
let name = param.name(sema.db);
let param_name = name.as_str()?;
let should_hide = {
let argument = get_string_representation(&arg)?;
is_argument_similar_to_param_name(&argument, param_name)
};
if should_hide {
return None;
}
let range = sema.original_range_opt(arg.syntax())?.range;
let source_syntax = match param {
hir::GenericParam::TypeParam(it) => {
if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) {
return None;
}
sema.source(it.merge())?.value.syntax().clone()
}
hir::GenericParam::ConstParam(it) => {
if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) {
return None;
}
let syntax = sema.source(it.merge())?.value.syntax().clone();
let const_param = ast::ConstParam::cast(syntax)?;
const_param.name()?.syntax().clone()
}
hir::GenericParam::LifetimeParam(it) => {
if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) {
return None;
}
sema.source(it)?.value.syntax().clone()
}
};
let linked_location = sema.original_range_opt(&source_syntax);
let label = render_label(param_name, config, linked_location);
Some(InlayHint {
range,
position: crate::InlayHintPosition::Before,
pad_left: false,
pad_right: true,
kind: InlayKind::GenericParameter,
label,
text_edit: None,
})
});
acc.extend(hints);
Some(())
}
fn get_string_representation(arg: &ast::GenericArg) -> Option<String> {
return match arg {
ast::GenericArg::AssocTypeArg(_) => None,
ast::GenericArg::ConstArg(const_arg) => Some(const_arg.to_string()),
ast::GenericArg::LifetimeArg(lifetime_arg) => {
let lifetime = lifetime_arg.lifetime()?;
Some(lifetime.to_string())
}
ast::GenericArg::TypeArg(type_arg) => {
let ty = type_arg.ty()?;
Some(
type_path_segment(&ty)
.map_or_else(|| type_arg.to_string(), |segment| segment.to_string()),
)
}
};
fn type_path_segment(ty: &ast::Type) -> Option<ast::PathSegment> {
match ty {
ast::Type::ArrayType(it) => type_path_segment(&it.ty()?),
ast::Type::ForType(it) => type_path_segment(&it.ty()?),
ast::Type::ParenType(it) => type_path_segment(&it.ty()?),
ast::Type::PathType(path_type) => path_type.path()?.segment(),
ast::Type::PtrType(it) => type_path_segment(&it.ty()?),
ast::Type::RefType(it) => type_path_segment(&it.ty()?),
ast::Type::SliceType(it) => type_path_segment(&it.ty()?),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::{
inlay_hints::{
tests::{check_with_config, DISABLED_CONFIG},
GenericParameterHints,
},
InlayHintsConfig,
};
#[track_caller]
fn generic_param_name_hints_always(ra_fixture: &str) {
check_with_config(
InlayHintsConfig {
generic_parameter_hints: GenericParameterHints {
type_hints: true,
lifetime_hints: true,
const_hints: true,
},
..DISABLED_CONFIG
},
ra_fixture,
);
}
#[track_caller]
fn generic_param_name_hints_const_only(ra_fixture: &str) {
check_with_config(
InlayHintsConfig {
generic_parameter_hints: GenericParameterHints {
type_hints: false,
lifetime_hints: false,
const_hints: true,
},
..DISABLED_CONFIG
},
ra_fixture,
);
}
#[test]
fn type_only() {
generic_param_name_hints_always(
r#"
struct A<X, Y> {
x: X,
y: Y,
}
fn foo(a: A<usize, u32>) {}
//^^^^^ X ^^^ Y
"#,
)
}
#[test]
fn lifetime_and_type() {
generic_param_name_hints_always(
r#"
struct A<'a, X> {
x: &'a X
}
fn foo<'b>(a: A<'b, u32>) {}
//^^ 'a^^^ X
"#,
)
}
#[test]
fn omit_lifetime() {
generic_param_name_hints_always(
r#"
struct A<'a, X> {
x: &'a X
}
fn foo() {
let x: i32 = 1;
let a: A<i32> = A { x: &x };
// ^^^ X
}
"#,
)
}
#[test]
fn const_only() {
generic_param_name_hints_always(
r#"
struct A<const X: usize, const Y: usize> {};
fn foo(a: A<12, 2>) {}
//^^ X^ Y
"#,
)
}
#[test]
fn lifetime_and_type_and_const() {
generic_param_name_hints_always(
r#"
struct A<'a, X, const LEN: usize> {
x: &'a [X; LEN],
}
fn foo<'b>(a: A<
'b,
// ^^ 'a
u32,
// ^^^ X
3
// ^ LEN
>) {}
"#,
)
}
#[test]
fn const_only_config() {
generic_param_name_hints_const_only(
r#"
struct A<'a, X, const LEN: usize> {
x: &'a [X; LEN],
}
fn foo<'b>(a: A<
'b,
u32,
3
// ^ LEN
>) {}
"#,
)
}
#[test]
fn assoc_type() {
generic_param_name_hints_always(
r#"
trait Trait<T> {
type Assoc1;
type Assoc2;
}
fn foo() -> impl Trait<i32, Assoc1 = u32, Assoc2 = u32> {}
// ^^^ T
"#,
)
}
#[test]
fn hide_similar() {
generic_param_name_hints_always(
r#"
struct A<'a, X, const N: usize> {
x: &'a [X; N],
}
const N: usize = 3;
mod m {
type X = u32;
}
fn foo<'a>(a: A<'a, m::X, N>) {}
"#,
)
}
#[test]
fn mismatching_args() {
generic_param_name_hints_always(
r#"
struct A<X, const N: usize> {
x: [X; N]
}
type InvalidType = A<3, i32>;
"#,
)
}
}

View file

@ -3,6 +3,8 @@
//! fn max(x: i32, y: i32) -> i32 { x + y } //! fn max(x: i32, y: i32) -> i32 { x + y }
//! _ = max(/*x*/4, /*y*/4); //! _ = max(/*x*/4, /*y*/4);
//! ``` //! ```
use std::fmt::Display;
use either::Either; use either::Either;
use hir::{Callable, Semantics}; use hir::{Callable, Semantics};
use ide_db::{base_db::FileRange, RootDatabase}; use ide_db::{base_db::FileRange, RootDatabase};
@ -46,9 +48,7 @@ pub(super) fn hints(
.map(|(param, param_name, _, FileRange { range, .. })| { .map(|(param, param_name, _, FileRange { range, .. })| {
let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax()));
let colon = if config.render_colons { ":" } else { "" }; let label = render_label(&param_name, config, linked_location);
let label =
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
InlayHint { InlayHint {
range, range,
kind: InlayKind::Parameter, kind: InlayKind::Parameter,
@ -64,6 +64,16 @@ pub(super) fn hints(
Some(()) Some(())
} }
pub(super) fn render_label(
param_name: impl Display,
config: &InlayHintsConfig,
linked_location: Option<FileRange>,
) -> InlayHintLabel {
let colon = if config.render_colons { ":" } else { "" };
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location)
}
fn get_callable( fn get_callable(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
expr: &ast::Expr, expr: &ast::Expr,
@ -113,7 +123,7 @@ fn should_hide_param_name_hint(
}; };
let fn_name = fn_name.as_deref(); let fn_name = fn_name.as_deref();
is_param_name_suffix_of_fn_name(param_name, callable, fn_name) is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
|| is_argument_similar_to_param_name(argument, param_name) || is_argument_expr_similar_to_param_name(argument, param_name)
|| param_name.starts_with("ra_fixture") || param_name.starts_with("ra_fixture")
|| (callable.n_params() == 1 && is_obvious_param(param_name)) || (callable.n_params() == 1 && is_obvious_param(param_name))
|| is_adt_constructor_similar_to_param_name(sema, argument, param_name) || is_adt_constructor_similar_to_param_name(sema, argument, param_name)
@ -143,14 +153,17 @@ fn is_param_name_suffix_of_fn_name(
} }
} }
fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool { fn is_argument_expr_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
// check whether param_name and argument are the same or
// whether param_name is a prefix/suffix of argument(split at `_`)
let argument = match get_string_representation(argument) { let argument = match get_string_representation(argument) {
Some(argument) => argument, Some(argument) => argument,
None => return false, None => return false,
}; };
is_argument_similar_to_param_name(&argument, param_name)
}
/// Check whether param_name and argument are the same or
/// whether param_name is a prefix/suffix of argument(split at `_`).
pub(super) fn is_argument_similar_to_param_name(argument: &str, param_name: &str) -> bool {
// std is honestly too panic happy... // std is honestly too panic happy...
let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at)); let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));

View file

@ -89,8 +89,8 @@ pub use crate::{
}, },
inlay_hints::{ inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
}, },
join_lines::JoinLinesConfig, join_lines::JoinLinesConfig,
markup::Markup, markup::Markup,

View file

@ -131,6 +131,11 @@ impl StaticIndex<'_> {
discriminant_hints: crate::DiscriminantHints::Fieldless, discriminant_hints: crate::DiscriminantHints::Fieldless,
type_hints: true, type_hints: true,
parameter_hints: true, parameter_hints: true,
generic_parameter_hints: crate::GenericParameterHints {
type_hints: false,
lifetime_hints: false,
const_hints: true,
},
chaining_hints: true, chaining_hints: true,
closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock,
lifetime_elision_hints: crate::LifetimeElisionHints::Never, lifetime_elision_hints: crate::LifetimeElisionHints::Never,

View file

@ -1008,6 +1008,11 @@ impl flags::AnalysisStats {
type_hints: true, type_hints: true,
discriminant_hints: ide::DiscriminantHints::Always, discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true, parameter_hints: true,
generic_parameter_hints: ide::GenericParameterHints {
type_hints: true,
lifetime_hints: true,
const_hints: true,
},
chaining_hints: true, chaining_hints: true,
adjustment_hints: ide::AdjustmentHints::Always, adjustment_hints: ide::AdjustmentHints::Always,
adjustment_hints_mode: ide::AdjustmentHintsMode::Postfix, adjustment_hints_mode: ide::AdjustmentHintsMode::Postfix,

View file

@ -10,9 +10,9 @@ use dirs::config_dir;
use flycheck::{CargoOptions, FlycheckConfig}; use flycheck::{CargoOptions, FlycheckConfig};
use ide::{ use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig,
Snippet, SnippetScope, SourceRootId, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId,
}; };
use ide_db::{ use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@ -509,6 +509,12 @@ config_data! {
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false,
/// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix,
/// Whether to show const generic parameter name inlay hints.
inlayHints_genericParameterHints_const_enable: bool= false,
/// Whether to show generic lifetime parameter name inlay hints.
inlayHints_genericParameterHints_lifetime_enable: bool = true,
/// Whether to show generic type parameter name inlay hints.
inlayHints_genericParameterHints_type_enable: bool = false,
/// Whether to show implicit drop hints. /// Whether to show implicit drop hints.
inlayHints_implicitDrops_enable: bool = false, inlayHints_implicitDrops_enable: bool = false,
/// Whether to show inlay type hints for elided lifetimes in function signatures. /// Whether to show inlay type hints for elided lifetimes in function signatures.
@ -1391,6 +1397,11 @@ impl Config {
render_colons: self.inlayHints_renderColons().to_owned(), render_colons: self.inlayHints_renderColons().to_owned(),
type_hints: self.inlayHints_typeHints_enable().to_owned(), type_hints: self.inlayHints_typeHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(), parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
generic_parameter_hints: GenericParameterHints {
type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
lifetime_hints: self.inlayHints_genericParameterHints_lifetime_enable().to_owned(),
const_hints: self.inlayHints_genericParameterHints_const_enable().to_owned(),
},
chaining_hints: self.inlayHints_chainingHints_enable().to_owned(), chaining_hints: self.inlayHints_chainingHints_enable().to_owned(),
discriminant_hints: match self.inlayHints_discriminantHints_enable() { discriminant_hints: match self.inlayHints_discriminantHints_enable() {
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
@ -2874,6 +2885,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"Only show discriminant hints on fieldless enum variants." "Only show discriminant hints on fieldless enum variants."
] ]
}, },
"GenericParameterHintsDef" => set! {
"type": "string",
"enum": [
"always",
"never",
"const_only"
],
"enumDescriptions": [
"Always show generic parameter hints.",
"Never show generic parameter hints.",
"Only show const generic parameter hints."
]
},
"AdjustmentHintsModeDef" => set! { "AdjustmentHintsModeDef" => set! {
"type": "string", "type": "string",
"enum": [ "enum": [

View file

@ -501,7 +501,9 @@ pub(crate) fn inlay_hint(
padding_left: Some(inlay_hint.pad_left), padding_left: Some(inlay_hint.pad_left),
padding_right: Some(inlay_hint.pad_right), padding_right: Some(inlay_hint.pad_right),
kind: match inlay_hint.kind { kind: match inlay_hint.kind {
InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER), InlayKind::Parameter | InlayKind::GenericParameter => {
Some(lsp_types::InlayHintKind::PARAMETER)
}
InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE), InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
_ => None, _ => None,
}, },

View file

@ -655,6 +655,21 @@ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
-- --
Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
-- --
[[rust-analyzer.inlayHints.genericParameterHints.const.enable]]rust-analyzer.inlayHints.genericParameterHints.const.enable (default: `false`)::
+
--
Whether to show const generic parameter name inlay hints.
--
[[rust-analyzer.inlayHints.genericParameterHints.lifetime.enable]]rust-analyzer.inlayHints.genericParameterHints.lifetime.enable (default: `true`)::
+
--
Whether to show generic lifetime parameter name inlay hints.
--
[[rust-analyzer.inlayHints.genericParameterHints.type.enable]]rust-analyzer.inlayHints.genericParameterHints.type.enable (default: `false`)::
+
--
Whether to show generic type parameter name inlay hints.
--
[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`):: [[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`)::
+ +
-- --

View file

@ -1909,6 +1909,36 @@
} }
} }
}, },
{
"title": "inlayHints",
"properties": {
"rust-analyzer.inlayHints.genericParameterHints.const.enable": {
"markdownDescription": "Whether to show const generic parameter name inlay hints.",
"default": false,
"type": "boolean"
}
}
},
{
"title": "inlayHints",
"properties": {
"rust-analyzer.inlayHints.genericParameterHints.lifetime.enable": {
"markdownDescription": "Whether to show generic lifetime parameter name inlay hints.",
"default": true,
"type": "boolean"
}
}
},
{
"title": "inlayHints",
"properties": {
"rust-analyzer.inlayHints.genericParameterHints.type.enable": {
"markdownDescription": "Whether to show generic type parameter name inlay hints.",
"default": false,
"type": "boolean"
}
}
},
{ {
"title": "inlayHints", "title": "inlayHints",
"properties": { "properties": {