From df8fc78eced2a720d26592a1540140aabc37c85a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 23 Dec 2022 11:28:46 +0100 Subject: [PATCH] Enum variant discriminants hints --- crates/ide/src/inlay_hints.rs | 15 +++ crates/ide/src/inlay_hints/discrimant.rs | 142 +++++++++++++++++++++++ crates/ide/src/lib.rs | 4 +- crates/ide/src/static_index.rs | 1 + crates/rust-analyzer/src/config.rs | 32 +++++ crates/rust-analyzer/src/to_proto.rs | 5 + docs/user/generated_config.adoc | 5 + editors/code/package.json | 15 +++ 8 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 crates/ide/src/inlay_hints/discrimant.rs diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 2b78dc51d9..073252e17b 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -24,12 +24,14 @@ mod chaining; mod param_name; mod binding_mode; mod bind_pat; +mod discrimant; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { pub location_links: bool, pub render_colons: bool, pub type_hints: bool, + pub discriminant_hints: DiscriminantHints, pub parameter_hints: bool, pub chaining_hints: bool, pub adjustment_hints: AdjustmentHints, @@ -51,6 +53,13 @@ pub enum ClosureReturnTypeHints { Never, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DiscriminantHints { + Always, + Never, + Fieldless, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum LifetimeElisionHints { Always, @@ -76,6 +85,7 @@ pub enum InlayKind { LifetimeHint, ParameterHint, TypeHint, + DiscriminantHint, OpeningParenthesis, ClosingParenthesis, } @@ -365,6 +375,9 @@ fn hints( ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), _ => None, }, + ast::Variant(v) => { + discrimant::hints(hints, famous_defs, config, file_id, &v) + }, // FIXME: fn-ptr type, dyn fn type, and trait object type elisions ast::Type(_) => None, _ => None, @@ -418,12 +431,14 @@ mod tests { use test_utils::extract_annotations; use crate::inlay_hints::AdjustmentHints; + use crate::DiscriminantHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; use super::ClosureReturnTypeHints; pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { location_links: false, + discriminant_hints: DiscriminantHints::Never, render_colons: false, type_hints: false, parameter_hints: false, diff --git a/crates/ide/src/inlay_hints/discrimant.rs b/crates/ide/src/inlay_hints/discrimant.rs new file mode 100644 index 0000000000..f32c4bdf28 --- /dev/null +++ b/crates/ide/src/inlay_hints/discrimant.rs @@ -0,0 +1,142 @@ +//! Implementation of "enum variant discriminant" inlay hints: +//! ```no_run +//! enum Foo { +//! Bar/* = 0*/, +//! } +//! ``` +use ide_db::{base_db::FileId, famous_defs::FamousDefs}; +use syntax::ast::{self, AstNode, HasName}; + +use crate::{DiscriminantHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip}; + +pub(super) fn hints( + acc: &mut Vec, + FamousDefs(sema, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + _: FileId, + variant: &ast::Variant, +) -> Option<()> { + let field_list = match config.discriminant_hints { + DiscriminantHints::Always => variant.field_list(), + DiscriminantHints::Fieldless => match variant.field_list() { + Some(_) => return None, + None => None, + }, + DiscriminantHints::Never => return None, + }; + + if variant.eq_token().is_some() { + return None; + } + + let name = variant.name()?; + + let descended = sema.descend_node_into_attributes(variant.clone()).pop(); + let desc_pat = descended.as_ref().unwrap_or(variant); + let v = sema.to_def(desc_pat)?; + let d = v.eval(sema.db); + + acc.push(InlayHint { + range: match field_list { + Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()), + None => name.syntax().text_range(), + }, + kind: InlayKind::DiscriminantHint, + label: match &d { + Ok(v) => format!("{}", v).into(), + Err(_) => "?".into(), + }, + tooltip: Some(InlayTooltip::String(match &d { + Ok(_) => "enum variant discriminant".into(), + Err(e) => format!("{e:?}").into(), + })), + }); + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::inlay_hints::{ + tests::{check_with_config, DISABLED_CONFIG}, + DiscriminantHints, InlayHintsConfig, + }; + + #[track_caller] + fn check_discriminants(ra_fixture: &str) { + check_with_config( + InlayHintsConfig { discriminant_hints: DiscriminantHints::Always, ..DISABLED_CONFIG }, + ra_fixture, + ); + } + + #[track_caller] + fn check_discriminants_fieldless(ra_fixture: &str) { + check_with_config( + InlayHintsConfig { + discriminant_hints: DiscriminantHints::Fieldless, + ..DISABLED_CONFIG + }, + ra_fixture, + ); + } + + #[test] + fn fieldless() { + check_discriminants( + r#" +enum Enum { + Variant, + //^^^^^^^0 + Variant1, + //^^^^^^^^1 + Variant2, + //^^^^^^^^2 + Variant5 = 5, + Variant6, + //^^^^^^^^6 +} +"#, + ); + } + + #[test] + fn datacarrying_mixed() { + check_discriminants( + r#" +enum Enum { + Variant(), + //^^^^^^^^^0 + Variant1, + //^^^^^^^^1 + Variant2 {}, + //^^^^^^^^^^^2 + Variant3, + //^^^^^^^^3 + Variant5 = 5, + Variant6, + //^^^^^^^^6 +} +"#, + ); + } + + #[test] + fn datacarrying_mixed_fieldless_set() { + check_discriminants_fieldless( + r#" +enum Enum { + Variant(), + Variant1, + //^^^^^^^^1 + Variant2 {}, + Variant3, + //^^^^^^^^3 + Variant5 = 5, + Variant6, + //^^^^^^^^6 +} +"#, + ); + } +} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6698bf766a..200958a433 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, InlayHint, InlayHintLabel, InlayHintsConfig, - InlayKind, InlayTooltip, LifetimeElisionHints, + AdjustmentHints, 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 a8ffc38708..331ff82265 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -108,6 +108,7 @@ impl StaticIndex<'_> { &InlayHintsConfig { location_links: true, render_colons: true, + discriminant_hints: crate::DiscriminantHints::Fieldless, type_hints: true, parameter_hints: true, chaining_hints: true, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index cba04b00c3..0fe4c2bb99 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -327,6 +327,8 @@ config_data! { inlayHints_closingBraceHints_minLines: usize = "25", /// Whether to show inlay type hints for return types of closures. inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"", + /// Whether to show enum variant discriminant hints. + inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"", /// Whether to show inlay hints for type adjustments. inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. @@ -1218,6 +1220,11 @@ impl Config { type_hints: self.data.inlayHints_typeHints_enable, parameter_hints: self.data.inlayHints_parameterHints_enable, chaining_hints: self.data.inlayHints_chainingHints_enable, + discriminant_hints: match self.data.inlayHints_discriminantHints_enable { + DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, + DiscriminantHintsDef::Never => ide::DiscriminantHints::Never, + DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless, + }, closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable { ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always, ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never, @@ -1579,6 +1586,7 @@ mod de_unit_v { named_unit_variant!(skip_trivial); named_unit_variant!(mutable); named_unit_variant!(reborrow); + named_unit_variant!(fieldless); named_unit_variant!(with_block); } @@ -1742,6 +1750,17 @@ enum AdjustmentHintsDef { Reborrow, } +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +enum DiscriminantHintsDef { + #[serde(deserialize_with = "true_or_always")] + Always, + #[serde(deserialize_with = "false_or_never")] + Never, + #[serde(deserialize_with = "de_unit_v::fieldless")] + Fieldless, +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum FilesWatcherDef { @@ -2064,6 +2083,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Only show auto borrow and dereference adjustment hints." ] }, + "DiscriminantHintsDef" => set! { + "type": "string", + "enum": [ + "always", + "never", + "fieldless" + ], + "enumDescriptions": [ + "Always show all discriminant hints.", + "Never show discriminant hints.", + "Only show discriminant hints on fieldless enum variants." + ] + }, "CargoFeaturesDef" => set! { "anyOf": [ { diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a7106acc78..0a11aaf3af 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -434,6 +434,7 @@ pub(crate) fn inlay_hint( InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"), InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "), InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "), + InlayKind::DiscriminantHint => inlay_hint.label.prepend_str(" = "), _ => {} } @@ -447,6 +448,7 @@ pub(crate) fn inlay_hint( // after annotated thing InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint + | InlayKind::DiscriminantHint | InlayKind::ChainingHint | InlayKind::GenericParamListHint | InlayKind::ClosingParenthesis @@ -457,6 +459,7 @@ pub(crate) fn inlay_hint( InlayKind::TypeHint => !render_colons, InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true, InlayKind::ClosingParenthesis + | InlayKind::DiscriminantHint | InlayKind::OpeningParenthesis | InlayKind::BindingModeHint | InlayKind::ClosureReturnTypeHint @@ -473,6 +476,7 @@ pub(crate) fn inlay_hint( | InlayKind::GenericParamListHint | InlayKind::AdjustmentHint | InlayKind::TypeHint + | InlayKind::DiscriminantHint | InlayKind::ClosingBraceHint => false, InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"), InlayKind::ParameterHint | InlayKind::LifetimeHint => true, @@ -483,6 +487,7 @@ pub(crate) fn inlay_hint( Some(lsp_types::InlayHintKind::TYPE) } InlayKind::ClosingParenthesis + | InlayKind::DiscriminantHint | InlayKind::OpeningParenthesis | InlayKind::BindingModeHint | InlayKind::GenericParamListHint diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 867fd5620f..91f8e98449 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -454,6 +454,11 @@ to always show them). -- Whether to show inlay type hints for return types of closures. -- +[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`):: ++ +-- +Whether to show enum variant discriminant hints. +-- [[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index fad67ce803..b45058a6cf 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -960,6 +960,21 @@ "Only show type hints for return types of closures with blocks." ] }, + "rust-analyzer.inlayHints.discriminantHints.enable": { + "markdownDescription": "Whether to show enum variant discriminant hints.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "fieldless" + ], + "enumDescriptions": [ + "Always show all discriminant hints.", + "Never show discriminant hints.", + "Only show discriminant hints on fieldless enum variants." + ] + }, "rust-analyzer.inlayHints.expressionAdjustmentHints.enable": { "markdownDescription": "Whether to show inlay hints for type adjustments.", "default": "never",