File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CodeStyle\CodeStyleOptions2.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.CodeStyle;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeStyle;
 
internal static class CodeStyleOptions2
{
    private const string PublicFeatureName = "CodeStyleOptions";
 
    private static readonly ImmutableArray<IOption2>.Builder s_editorConfigOptionsBuilder = ImmutableArray.CreateBuilder<IOption2>();
 
    private static PerLanguageOption2<CodeStyleOption2<T>> CreatePerLanguageOption<T>(
        OptionGroup group, string name, CodeStyleOption2<T> defaultValue, Func<CodeStyleOption2<T>, EditorConfigValueSerializer<CodeStyleOption2<T>>>? serializerFactory = null)
        => s_editorConfigOptionsBuilder.CreatePerLanguageEditorConfigOption(name, defaultValue, group, serializerFactory);
 
    private static Option2<CodeStyleOption2<T>> CreateOption<T>(
        OptionGroup group, string name, CodeStyleOption2<T> defaultValue, Func<CodeStyleOption2<T>, EditorConfigValueSerializer<CodeStyleOption2<T>>>? serializerFactory = null)
        => s_editorConfigOptionsBuilder.CreateEditorConfigOption(name, defaultValue, group, languageName: null, serializerFactory);
 
    private static Option2<T> CreateOption<T>(
        OptionGroup group, string name, T defaultValue, EditorConfigValueSerializer<T>? serializer = null)
        => s_editorConfigOptionsBuilder.CreateEditorConfigOption(name, defaultValue, group, serializer);
 
    private static PerLanguageOption2<CodeStyleOption2<bool>> CreateQualifyAccessOption(string name)
        => CreatePerLanguageOption(CodeStyleOptionGroups.ThisOrMe, name, defaultValue: SimplifierOptions.DefaultQualifyAccess);
 
    /// <summary>
    /// This option says if we should simplify away the <see langword="this"/>. or <see langword="Me"/>. in field access expressions.
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> QualifyFieldAccess = CreateQualifyAccessOption(
        "dotnet_style_qualification_for_field")
        .WithPublicOption(PublicFeatureName, "QualifyFieldAccess");
 
    /// <summary>
    /// This option says if we should simplify away the <see langword="this"/>. or <see langword="Me"/>. in property access expressions.
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> QualifyPropertyAccess = CreateQualifyAccessOption(
        "dotnet_style_qualification_for_property")
        .WithPublicOption(PublicFeatureName, "QualifyPropertyAccess");
 
    /// <summary>
    /// This option says if we should simplify away the <see langword="this"/>. or <see langword="Me"/>. in method access expressions.
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> QualifyMethodAccess = CreateQualifyAccessOption(
        "dotnet_style_qualification_for_method")
        .WithPublicOption(PublicFeatureName, "QualifyMethodAccess");
 
    /// <summary>
    /// This option says if we should simplify away the <see langword="this"/>. or <see langword="Me"/>. in event access expressions.
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> QualifyEventAccess = CreateQualifyAccessOption(
        "dotnet_style_qualification_for_event")
        .WithPublicOption(PublicFeatureName, "QualifyEventAccess");
 
    /// <summary>
    /// This option says if we should prefer keyword for Intrinsic Predefined Types in Declarations
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferIntrinsicPredefinedTypeKeywordInDeclaration = CreatePerLanguageOption(
        CodeStyleOptionGroups.PredefinedTypeNameUsage,
        defaultValue: SimplifierOptions.CommonDefaults.PreferPredefinedTypeKeywordInDeclaration,
        name: "dotnet_style_predefined_type_for_locals_parameters_members")
        .WithPublicOption(PublicFeatureName, "PreferIntrinsicPredefinedTypeKeywordInDeclaration");
 
    /// <summary>
    /// This option says if we should prefer keyword for Intrinsic Predefined Types in Member Access Expression
    /// </summary>
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferIntrinsicPredefinedTypeKeywordInMemberAccess = CreatePerLanguageOption(
        CodeStyleOptionGroups.PredefinedTypeNameUsage,
        defaultValue: SimplifierOptions.CommonDefaults.PreferPredefinedTypeKeywordInMemberAccess,
        name: "dotnet_style_predefined_type_for_member_access")
        .WithPublicOption(PublicFeatureName, "PreferIntrinsicPredefinedTypeKeywordInMemberAccess");
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferObjectInitializer = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_object_initializer",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferCollectionInitializer = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_collection_initializer",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<CollectionExpressionPreference>> PreferCollectionExpression = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_collection_expression",
        defaultValue: new CodeStyleOption2<CollectionExpressionPreference>(CollectionExpressionPreference.WhenTypesLooselyMatch, NotificationOption2.Suggestion),
        defaultValue => new(
            parseValue: s => CollectionExpressionPreferenceUtilities.Parse(s, defaultValue),
            serializeValue: v => CollectionExpressionPreferenceUtilities.GetEditorConfigString(v, defaultValue)));
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferSimplifiedBooleanExpressions = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_simplified_boolean_expressions",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly Option2<OperatorPlacementWhenWrappingPreference> OperatorPlacementWhenWrapping = CreateOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_operator_placement_when_wrapping",
        defaultValue: OperatorPlacementWhenWrappingPreference.BeginningOfLine,
        serializer: new(
            parseValue: str => OperatorPlacementUtilities.Parse(str, OperatorPlacementWhenWrappingPreference.BeginningOfLine),
            serializeValue: OperatorPlacementUtilities.GetEditorConfigString));
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferCoalesceExpression = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_coalesce_expression",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferNullPropagation = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_null_propagation",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferExplicitTupleNames = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_explicit_tuple_names",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferAutoProperties = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_auto_properties",
        defaultValue: CodeStyleOption2.TrueWithSilentEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferInferredTupleNames = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_inferred_tuple_names",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferInferredAnonymousTypeMemberNames = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_inferred_anonymous_type_member_names",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferIsNullCheckOverReferenceEqualityMethod = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_is_null_check_over_reference_equality_method",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferConditionalExpressionOverAssignment = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_conditional_expression_over_assignment",
        defaultValue: CodeStyleOption2.TrueWithSilentEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferConditionalExpressionOverReturn = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_conditional_expression_over_return",
        defaultValue: CodeStyleOption2.TrueWithSilentEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferCompoundAssignment = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_compound_assignment",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferSimplifiedInterpolation = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_simplified_interpolation",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    private static readonly BidirectionalMap<string, UnusedParametersPreference> s_unusedParametersPreferenceMap =
        new(
        [
            KeyValuePairUtil.Create("non_public", UnusedParametersPreference.NonPublicMethods),
            KeyValuePairUtil.Create("all", UnusedParametersPreference.AllMethods),
        ]);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<UnusedParametersPreference>> UnusedParameters = CreatePerLanguageOption(
        CodeStyleOptionGroups.Parameter,
        "dotnet_code_quality_unused_parameters",
        defaultValue: new CodeStyleOption2<UnusedParametersPreference>(UnusedParametersPreference.AllMethods, NotificationOption2.Suggestion),
        defaultValue => new(
            parseValue: str =>
            {
                if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(str, defaultValue.Notification, out var value, out var notification))
                {
                    return new CodeStyleOption2<UnusedParametersPreference>(s_unusedParametersPreferenceMap.GetValueOrDefault(value), notification);
                }
 
                return defaultValue;
            },
            serializeValue: option =>
            {
                Debug.Assert(s_unusedParametersPreferenceMap.ContainsValue(option.Value));
                var value = s_unusedParametersPreferenceMap.GetKeyOrDefault(option.Value) ?? s_unusedParametersPreferenceMap.GetKeyOrDefault(defaultValue.Value);
                return $"{value}{CodeStyleHelpers.GetEditorConfigStringNotificationPart(option, defaultValue)}";
            }));
 
    private static readonly BidirectionalMap<string, AccessibilityModifiersRequired> s_accessibilityModifiersRequiredMap =
        new(
        [
            KeyValuePairUtil.Create("never", CodeStyle.AccessibilityModifiersRequired.Never),
            KeyValuePairUtil.Create("always", CodeStyle.AccessibilityModifiersRequired.Always),
            KeyValuePairUtil.Create("for_non_interface_members", CodeStyle.AccessibilityModifiersRequired.ForNonInterfaceMembers),
            KeyValuePairUtil.Create("omit_if_default", CodeStyle.AccessibilityModifiersRequired.OmitIfDefault),
        ]);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<AccessibilityModifiersRequired>> AccessibilityModifiersRequired = CreatePerLanguageOption(
        CodeStyleOptionGroups.Modifier, "dotnet_style_require_accessibility_modifiers",
        defaultValue: new CodeStyleOption2<AccessibilityModifiersRequired>(SyntaxFormattingOptions.CommonDefaults.AccessibilityModifiersRequired, NotificationOption2.Silent),
        defaultValue => new(
            parseValue: str =>
            {
                if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(str, defaultValue.Notification, out var value, out var notificationOpt))
                {
                    Debug.Assert(s_accessibilityModifiersRequiredMap.ContainsKey(value));
                    return new CodeStyleOption2<AccessibilityModifiersRequired>(s_accessibilityModifiersRequiredMap.GetValueOrDefault(value), notificationOpt);
                }
 
                return defaultValue;
            },
            serializeValue: option =>
            {
                Debug.Assert(s_accessibilityModifiersRequiredMap.ContainsValue(option.Value));
                return $"{s_accessibilityModifiersRequiredMap.GetKeyOrDefault(option.Value)}{CodeStyleHelpers.GetEditorConfigStringNotificationPart(option, defaultValue)}";
            }));
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferReadonly = CreatePerLanguageOption(
        CodeStyleOptionGroups.Field,
        "dotnet_style_readonly_field",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly Option2<string> FileHeaderTemplate = CreateOption(
        CodeStyleOptionGroups.Usings,
        "file_header_template",
        DocumentFormattingOptions.Default.FileHeaderTemplate,
        EditorConfigValueSerializer.String(emptyStringRepresentation: "unset"));
 
    internal static readonly Option2<string> RemoveUnnecessarySuppressionExclusions = CreateOption(
        CodeStyleOptionGroups.Suppressions,
        "dotnet_remove_unnecessary_suppression_exclusions",
        defaultValue: "",
        EditorConfigValueSerializer.String(emptyStringRepresentation: "none"));
 
    private static readonly BidirectionalMap<string, ParenthesesPreference> s_parenthesesPreferenceMap =
        new(
        [
            KeyValuePairUtil.Create("always_for_clarity", ParenthesesPreference.AlwaysForClarity),
            KeyValuePairUtil.Create("never_if_unnecessary", ParenthesesPreference.NeverIfUnnecessary),
        ]);
 
    private static PerLanguageOption2<CodeStyleOption2<ParenthesesPreference>> CreateParenthesesOption(CodeStyleOption2<ParenthesesPreference> defaultValue, string name)
        => CreatePerLanguageOption(
            CodeStyleOptionGroups.Parentheses,
            name,
            defaultValue,
            defaultValue => new(
                parseValue: str =>
                {
                    if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(str, defaultValue.Notification, out var value, out var notification))
                    {
                        Debug.Assert(s_parenthesesPreferenceMap.ContainsKey(value));
                        return new CodeStyleOption2<ParenthesesPreference>(s_parenthesesPreferenceMap.GetValueOrDefault(value), notification);
                    }
 
                    return defaultValue;
                },
                serializeValue: option =>
                {
                    Debug.Assert(s_parenthesesPreferenceMap.ContainsValue(option.Value));
                    var value = s_parenthesesPreferenceMap.GetKeyOrDefault(option.Value) ?? s_parenthesesPreferenceMap.GetKeyOrDefault(ParenthesesPreference.AlwaysForClarity);
                    return $"{value}{CodeStyleHelpers.GetEditorConfigStringNotificationPart(option, defaultValue)}";
                }));
 
    private static readonly CodeStyleOption2<ParenthesesPreference> s_alwaysForClarityPreference =
        new(ParenthesesPreference.AlwaysForClarity, NotificationOption2.Silent);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<ParenthesesPreference>> ArithmeticBinaryParentheses =
        CreateParenthesesOption(
            defaultValue: s_alwaysForClarityPreference,
            "dotnet_style_parentheses_in_arithmetic_binary_operators");
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<ParenthesesPreference>> OtherBinaryParentheses =
        CreateParenthesesOption(
            defaultValue: s_alwaysForClarityPreference,
            "dotnet_style_parentheses_in_other_binary_operators");
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<ParenthesesPreference>> RelationalBinaryParentheses =
        CreateParenthesesOption(
            defaultValue: s_alwaysForClarityPreference,
            "dotnet_style_parentheses_in_relational_binary_operators");
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<ParenthesesPreference>> OtherParentheses =
        CreateParenthesesOption(
            defaultValue: new(ParenthesesPreference.NeverIfUnnecessary, NotificationOption2.Silent),
            "dotnet_style_parentheses_in_other_operators");
 
    private static readonly BidirectionalMap<string, ForEachExplicitCastInSourcePreference> s_forEachExplicitCastInSourcePreferencePreferenceMap =
        new(
        [
            KeyValuePairUtil.Create("always", ForEachExplicitCastInSourcePreference.Always),
            KeyValuePairUtil.Create("when_strongly_typed", ForEachExplicitCastInSourcePreference.WhenStronglyTyped),
        ]);
 
    internal static readonly Option2<CodeStyleOption2<ForEachExplicitCastInSourcePreference>> ForEachExplicitCastInSource = CreateOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_prefer_foreach_explicit_cast_in_source",
        defaultValue: new CodeStyleOption2<ForEachExplicitCastInSourcePreference>(ForEachExplicitCastInSourcePreference.WhenStronglyTyped, NotificationOption2.Suggestion),
        defaultValue => new(
            parseValue: str =>
            {
                if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(str, defaultValue.Notification, out var value, out var notification))
                {
                    Debug.Assert(s_forEachExplicitCastInSourcePreferencePreferenceMap.ContainsKey(value));
                    return new CodeStyleOption2<ForEachExplicitCastInSourcePreference>(
                        s_forEachExplicitCastInSourcePreferencePreferenceMap.GetValueOrDefault(value), notification);
                }
 
                return defaultValue;
            },
            serializeValue: option =>
            {
                Debug.Assert(s_forEachExplicitCastInSourcePreferencePreferenceMap.ContainsValue(option.Value));
                var value = s_forEachExplicitCastInSourcePreferencePreferenceMap.GetKeyOrDefault(option.Value) ??
                    s_forEachExplicitCastInSourcePreferencePreferenceMap.GetKeyOrDefault(defaultValue.Value);
                return $"{value}{CodeStyleHelpers.GetEditorConfigStringNotificationPart(option, defaultValue)}";
            }));
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferSystemHashCode = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_prefer_system_hash_code",
        defaultValue: new CodeStyleOption2<bool>(value: true, notification: NotificationOption2.Suggestion));
 
    public static readonly PerLanguageOption2<CodeStyleOption2<bool>> PreferNamespaceAndFolderMatchStructure = CreatePerLanguageOption(
        CodeStyleOptionGroups.ExpressionLevelPreferences,
        "dotnet_style_namespace_match_folder",
        defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> AllowMultipleBlankLines = CreatePerLanguageOption(
        CodeStyleOptionGroups.NewLinePreferences,
        "dotnet_style_allow_multiple_blank_lines_experimental",
        defaultValue: CodeStyleOption2.TrueWithSilentEnforcement);
 
    internal static readonly PerLanguageOption2<CodeStyleOption2<bool>> AllowStatementImmediatelyAfterBlock = CreatePerLanguageOption(
        CodeStyleOptionGroups.NewLinePreferences,
        "dotnet_style_allow_statement_immediately_after_block_experimental",
        defaultValue: CodeStyleOption2.TrueWithSilentEnforcement);
 
    /// <summary>
    /// Options that we expect the user to set in editorconfig.
    /// </summary>
    internal static readonly ImmutableArray<IOption2> EditorConfigOptions = s_editorConfigOptionsBuilder.ToImmutable();
}
 
internal static class CodeStyleOptionGroups
{
    public static readonly OptionGroup CodeStyle = new(name: "code_style", description: "", priority: 1);
 
    public static readonly OptionGroup Usings = new("usings", description: CompilerExtensionsResources.Organize_usings, priority: 1, parent: CodeStyle);
    public static readonly OptionGroup ThisOrMe = new("this_or_me", description: CompilerExtensionsResources.this_dot_and_Me_dot_preferences, priority: 2, parent: CodeStyle);
    public static readonly OptionGroup PredefinedTypeNameUsage = new("predefined_type_name_usage", description: CompilerExtensionsResources.Language_keywords_vs_BCL_types_preferences, priority: 3, parent: CodeStyle);
    public static readonly OptionGroup Parentheses = new("parentheses", description: CompilerExtensionsResources.Parentheses_preferences, priority: 4, parent: CodeStyle);
    public static readonly OptionGroup Modifier = new("modifier", description: CompilerExtensionsResources.Modifier_preferences, priority: 5, parent: CodeStyle);
    public static readonly OptionGroup ExpressionLevelPreferences = new("expression_level_preferences", description: CompilerExtensionsResources.Expression_level_preferences, priority: 7, parent: CodeStyle);
    public static readonly OptionGroup Field = new("field", description: CompilerExtensionsResources.Field_preferences, priority: 8, parent: CodeStyle);
    public static readonly OptionGroup Parameter = new("parameter", description: CompilerExtensionsResources.Parameter_preferences, priority: 9, parent: CodeStyle);
    public static readonly OptionGroup Suppressions = new("suppressions", description: CompilerExtensionsResources.Suppression_preferences, priority: 10, parent: CodeStyle);
    public static readonly OptionGroup NewLinePreferences = new("new_line_preferences", description: CompilerExtensionsResources.New_line_preferences, priority: 11, parent: CodeStyle);
}