File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\NamingStyles\EditorConfig\EditorConfigNamingStyleParser_NamingStyle.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.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.EditorConfig.Parsing;
using Microsoft.CodeAnalysis.EditorConfig.Parsing.NamingStyles;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
 
internal static partial class EditorConfigNamingStyleParser
{
    internal static bool TryGetNamingStyleData(
        Section section,
        string namingRuleName,
        IReadOnlyDictionary<string, (string value, TextLine? line)> properties,
        [NotNullWhen(true)] out NamingScheme? namingScheme)
    {
        return TryGetNamingStyleData(
            namingRuleName,
            properties,
            s => s.value,
            x => x.line,
            s => (s.value, s.line),
            (nameTuple, prefixTuple, suffixTuple, wordSeparatorTuple, capitalizationTuple) =>
            {
                var (name, nameTextLine) = nameTuple;
                var (prefix, prefixTextLine) = prefixTuple;
                var (suffix, suffixTextLine) = suffixTuple;
                var (wordSeparator, wordSeparatorTextLine) = wordSeparatorTuple;
                var (capitalization, capitalizationTextLine) = capitalizationTuple;
 
                return new NamingScheme(
                    OptionName: (section, nameTextLine?.Span, name),
                    Prefix: (section, prefixTextLine?.Span, prefix),
                    Suffix: (section, suffixTextLine?.Span, suffix),
                    WordSeparator: (section, wordSeparatorTextLine?.Span, wordSeparator),
                    Capitalization: (section, capitalizationTextLine?.Span, capitalization));
            },
            out namingScheme);
    }
 
    private static bool TryGetNamingStyleData(
        string namingRuleName,
        IReadOnlyDictionary<string, string> rawOptions,
        out NamingStyle namingStyle)
    {
        return TryGetNamingStyleData<string, object?, NamingStyle>(
            namingRuleName,
            rawOptions,
            s => s,
            x => null,
            s => (s ?? string.Empty, null),
            (nameTuple, prefixTuple, suffixTuple, wordSeparatorTuple, capitalizationTuple) =>
            {
                var (namingStyleName, _) = nameTuple;
                var (prefix, _) = prefixTuple;
                var (suffix, _) = suffixTuple;
                var (wordSeparator, _) = wordSeparatorTuple;
                var (capitalization, _) = capitalizationTuple;
 
                return new NamingStyle(
                    Guid.NewGuid(),
                    namingStyleName,
                    prefix,
                    suffix,
                    wordSeparator,
                    capitalization);
            },
            out namingStyle);
    }
 
    private static bool TryGetNamingStyleData<T, TData, TResult>(
        string namingRuleName,
        IReadOnlyDictionary<string, T> rawOptions,
        Func<T, string> nameSelector,
        Func<T, TData> dataSelector,
        Func<T?, (string value, TData data)> tupleSelector,
        Func<(string namingStyleName, TData data),
             (string prefix, TData data),
             (string suffix, TData data),
             (string wordSeparator, TData data),
             (Capitalization capitalization, TData data), TResult> constructor,
        [NotNullWhen(true)] out TResult? namingStyle)
    {
        namingStyle = default;
        if (!TryGetNamingStyleTitle(namingRuleName, rawOptions, nameSelector, dataSelector, out var namingStyleTitle))
        {
            return false;
        }
 
        var requiredPrefix = GetNamingRequiredPrefix(namingStyleTitle.name, rawOptions, tupleSelector);
        var requiredSuffix = GetNamingRequiredSuffix(namingStyleTitle.name, rawOptions, tupleSelector);
        var wordSeparator = GetNamingWordSeparator(namingStyleTitle.name, rawOptions, tupleSelector);
        if (!TryGetNamingCapitalization(namingStyleTitle.name, rawOptions, tupleSelector, out var capitalization))
        {
            return false;
        }
 
        namingStyle = constructor(namingStyleTitle, requiredPrefix, requiredSuffix, wordSeparator, capitalization);
        return namingStyle is not null;
    }
 
    private static bool TryGetNamingStyleTitle<T, TData>(
        string namingRuleName,
        IReadOnlyDictionary<string, T> conventionsDictionary,
        Func<T, string> nameSelector,
        Func<T, TData> dataSelector,
        out (string name, TData data) result)
    {
        if (conventionsDictionary.TryGetValue($"dotnet_naming_rule.{namingRuleName}.style", out var namingStyleName))
        {
            var name = nameSelector(namingStyleName);
            result = (name, dataSelector(namingStyleName));
            return name != null;
        }
 
        result = default;
        return false;
    }
 
    private static (string prefix, TData data) GetNamingRequiredPrefix<T, TData>(
        string namingStyleName,
        IReadOnlyDictionary<string, T> properties,
        Func<T?, (string value, TData data)> tupleSelector)
        => GetValueFromDictionary(namingStyleName, "required_prefix", properties, tupleSelector);
 
    private static (string suffix, TData data) GetNamingRequiredSuffix<T, TData>(
        string namingStyleName,
        IReadOnlyDictionary<string, T> properties,
        Func<T?, (string value, TData data)> tupleSelector)
        => GetValueFromDictionary(namingStyleName, "required_suffix", properties, tupleSelector);
 
    private static (string wordSeparator, TData data) GetNamingWordSeparator<T, TData>(
        string namingStyleName,
        IReadOnlyDictionary<string, T> properties,
        Func<T?, (string value, TData data)> tupleSelector)
        => GetValueFromDictionary(namingStyleName, "word_separator", properties, tupleSelector);
 
    private static bool TryGetNamingCapitalization<T, TData>(
        string namingStyleName,
        IReadOnlyDictionary<string, T> properties,
        Func<T?, (string value, TData data)> tupleSelector,
        out (Capitalization capitalization, TData data) result)
    {
        var (value, data) = GetValueFromDictionary(namingStyleName, "capitalization", properties, tupleSelector);
        if (TryParseCapitalizationScheme(value, out var capitalization))
        {
            result = (capitalization.Value, data);
            return true;
        }
 
        result = default;
        return false;
    }
 
    private static (string value, TData data) GetValueFromDictionary<T, TData>(
        string namingStyleName,
        string optionName,
        IReadOnlyDictionary<string, T> conventionsDictionary,
        Func<T?, (string value, TData data)> tupleSelector)
    {
        _ = conventionsDictionary.TryGetValue($"dotnet_naming_style.{namingStyleName}.{optionName}", out var result);
        return tupleSelector(result);
    }
 
    private static bool TryParseCapitalizationScheme(
        string namingStyleCapitalization,
        [NotNullWhen(true)] out Capitalization? capitalization)
    {
        capitalization = namingStyleCapitalization switch
        {
            "pascal_case" => Capitalization.PascalCase,
            "camel_case" => Capitalization.CamelCase,
            "first_word_upper" => Capitalization.FirstUpper,
            "all_upper" => Capitalization.AllUpper,
            "all_lower" => Capitalization.AllLower,
            _ => null,
        };
 
        return capitalization is not null;
    }
 
    public static string ToEditorConfigString(this Capitalization capitalization)
        => capitalization switch
        {
            Capitalization.PascalCase => "pascal_case",
            Capitalization.CamelCase => "camel_case",
            Capitalization.FirstUpper => "first_word_upper",
            Capitalization.AllUpper => "all_upper",
            Capitalization.AllLower => "all_lower",
            _ => throw ExceptionUtilities.UnexpectedValue(capitalization),
        };
}