Skip to content

Commit

Permalink
Merge pull request #71 from OhFlowi/doc/EnumExtensionMethods
Browse files Browse the repository at this point in the history
doc: Missing documentation on the generated code added
  • Loading branch information
EngRajabi authored Mar 19, 2024
2 parents 6fed25a + 8715c59 commit bfe0a7f
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 45 deletions.
150 changes: 105 additions & 45 deletions Supernova.Enum.Generators/EnumSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Supernova.Enum.Generators.Extensions;

namespace Supernova.Enum.Generators;

Expand All @@ -26,6 +27,9 @@ public void Execute(GeneratorExecutionContext context)
context.AddSource($"{SourceGeneratorHelper.AttributeName}Attribute.g.cs", SourceText.From($@"using System;
namespace {SourceGeneratorHelper.NameSpace}
{{
/// <summary>
/// An attribute that marks enums for which extension methods are to be generated.
/// </summary>
[AttributeUsage(AttributeTargets.Enum)]
public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute
{{
Expand Down Expand Up @@ -55,7 +59,6 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute
continue;

var symbol = semanticModel.GetDeclaredSymbol(e);
var symbolName = $"{symbol.ContainingNamespace}.{symbol.Name}";

//var attribute = symbol.GetAttributes()
// .FirstOrDefault(x => string.Equals(x.AttributeClass.Name, SourceGeneratorHelper.AttributeName,
Expand Down Expand Up @@ -106,38 +109,41 @@ public sealed class {SourceGeneratorHelper.AttributeName}Attribute : Attribute
using System.Collections.Immutable;
namespace {SourceGeneratorHelper.NameSpace}
{{
/// <summary>
/// Provides extension methods for operations related to the {symbol.Name} enumeration.
/// </summary>
{symbol.DeclaredAccessibility.ToString("G").ToLower()} static class {symbol.Name}EnumExtensions
{{");

//DisplayNames Dictionary
DisplayNamesDictionary(sourceBuilder, symbolName, e, enumDisplayNames);
DisplayNamesDictionary(sourceBuilder, symbol, e, enumDisplayNames);

//DisplayDescriptions Dictionary
DisplayDescriptionsDictionary(sourceBuilder, symbolName, e, enumDescriptions);
DisplayDescriptionsDictionary(sourceBuilder, symbol, e, enumDescriptions);

//ToStringFast
ToStringFast(sourceBuilder, symbolName, e);
ToStringFast(sourceBuilder, symbol, e);

//IsDefined enum
IsDefinedEnum(sourceBuilder, symbolName, e);
IsDefinedEnum(sourceBuilder, symbol, e);

//IsDefined string
IsDefinedString(sourceBuilder, e, symbolName);
IsDefinedString(sourceBuilder, e, symbol);

//ToDisplay string
ToDisplay(sourceBuilder, symbolName, e, enumDisplayNames);
ToDisplay(sourceBuilder, symbol, e, enumDisplayNames);

//ToDisplay string
ToDescription(sourceBuilder, symbolName, e, enumDescriptions);
ToDescription(sourceBuilder, symbol, e, enumDescriptions);

//GetValues
GetValuesFast(sourceBuilder, symbolName, e);
GetValuesFast(sourceBuilder, symbol, e);

//GetNames
GetNamesFast(sourceBuilder, symbolName, e);
GetNamesFast(sourceBuilder, symbol, e);

//GetLength
GetLengthFast(sourceBuilder, symbolName, e);
GetLengthFast(sourceBuilder, symbol, e);

sourceBuilder.Append(@"
}
Expand All @@ -149,11 +155,17 @@ namespace {SourceGeneratorHelper.NameSpace}
}
}

private static void ToDisplay(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e,
private static void ToDisplay(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumDisplayNames)
{
sourceBuilder.Append($@"
public static string {SourceGeneratorHelper.ExtensionMethodNameToDisplay}(this {symbolName} states, string defaultValue = null)
/// <summary>
/// Converts the <see cref=""{symbol.Name}"" /> enumeration value to its display string.
/// </summary>
/// <param name=""states"">The <see cref=""{symbol.Name}"" /> enumeration value.</param>
/// <param name=""defaultValue"">The default value to return if the enumeration value is not recognized.</param>
/// <returns>The display string of the <see cref=""{symbol.Name}"" /> value.</returns>
public static string {SourceGeneratorHelper.ExtensionMethodNameToDisplay}(this {symbol.FullName()} states, string defaultValue = null)
{{
return states switch
{{
Expand All @@ -165,20 +177,27 @@ private static void ToDisplay(StringBuilder sourceBuilder, string symbolName, En
? found
: key;
sourceBuilder.AppendLine(
$@" {symbolName}.{member.Identifier.ValueText} => ""{enumDisplayName ?? key}"",");
$@" {symbol}.{member.Identifier.ValueText} => ""{enumDisplayName ?? key}"",");
}

sourceBuilder.Append(
@" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null)
};
}");
}
");
}

private static void ToDescription(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e,
private static void ToDescription(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumDescriptions)
{
sourceBuilder.Append($@"
public static string {SourceGeneratorHelper.ExtensionMethodNameToDescription}(this {symbolName} states, string defaultValue = null)
/// <summary>
/// Gets the description of the <see cref=""{symbol.Name}"" /> enumeration value.
/// </summary>
/// <param name=""states"">The <see cref=""{symbol.Name}"" /> enumeration value.</param>
/// <param name=""defaultValue"">The default value to return if the enumeration value is not recognized.</param>
/// <returns>The description of the <see cref=""{symbol.Name}"" /> value.</returns>
public static string {SourceGeneratorHelper.ExtensionMethodNameToDescription}(this {symbol.FullName()} states, string defaultValue = null)
{{
return states switch
{{
Expand All @@ -190,68 +209,91 @@ private static void ToDescription(StringBuilder sourceBuilder, string symbolName
? found
: key;
sourceBuilder.AppendLine(
$@" {symbolName}.{member.Identifier.ValueText} => ""{enumDescription ?? key}"",");
$@" {symbol}.{member.Identifier.ValueText} => ""{enumDescription ?? key}"",");
}

sourceBuilder.Append(
@" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null)
};
}");
}
");
}

private static void IsDefinedString(StringBuilder sourceBuilder, EnumDeclarationSyntax e, string symbolName)
private static void IsDefinedString(StringBuilder sourceBuilder, EnumDeclarationSyntax e, ISymbol symbol)
{
sourceBuilder.Append($@"
/// <summary>
/// Checks if the specified string represents a defined <see cref=""{symbol.Name}"" /> value.
/// </summary>
/// <param name=""states"">The string representing a <see cref=""{symbol.Name}"" /> value.</param>
/// <returns>True if the string represents a defined <see cref=""{symbol.Name}"" /> value; otherwise, false.</returns>
public static bool {SourceGeneratorHelper.ExtensionMethodNameIsDefined}(string states)
{{
return states switch
{{
");
foreach (var member in e.Members.Select(x => x.Identifier.ValueText))
sourceBuilder.AppendLine($@" nameof({symbolName}.{member}) => true,");
sourceBuilder.AppendLine($@" nameof({symbol.FullName()}.{member}) => true,");
sourceBuilder.Append(
@" _ => false
};
}");
}
");
}

private static void IsDefinedEnum(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e)
private static void IsDefinedEnum(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
public static bool {SourceGeneratorHelper.ExtensionMethodNameIsDefined}({symbolName} states)
/// <summary>
/// Checks if the specified <see cref=""{symbol.Name}"" /> value is defined.
/// </summary>
/// <param name=""states"">The <see cref=""{symbol.Name}"" /> value to check.</param>
/// <returns>True if the <see cref=""{symbol.Name}"" /> value is defined; otherwise, false.</returns>
public static bool {SourceGeneratorHelper.ExtensionMethodNameIsDefined}({symbol.FullName()} states)
{{
return states switch
{{
");
foreach (var member in e.Members.Select(x => x.Identifier.ValueText))
sourceBuilder.AppendLine($@" {symbolName}.{member} => true,");
sourceBuilder.AppendLine($@" {symbol.FullName()}.{member} => true,");
sourceBuilder.Append(
@" _ => false
};
}");
}
");
}

private static void ToStringFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e)
private static void ToStringFast(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
public static string {SourceGeneratorHelper.ExtensionMethodNameToString}(this {symbolName} states, string defaultValue = null)
/// <summary>
/// Converts the <see cref=""{symbol.Name}"" /> enumeration value to its string representation.
/// </summary>
/// <param name=""states"">The <see cref=""{symbol.Name}"" /> enumeration value.</param>
/// <param name=""defaultValue"">The default value to return if the enumeration value is not recognized.</param>
/// <returns>The string representation of the <see cref=""{symbol.Name}"" /> value.</returns>
public static string {SourceGeneratorHelper.ExtensionMethodNameToString}(this {symbol.FullName()} states, string defaultValue = null)
{{
return states switch
{{
");
foreach (var member in e.Members.Select(x => x.Identifier.ValueText))
sourceBuilder.AppendLine($@" {symbolName}.{member} => nameof({symbolName}.{member}),");
sourceBuilder.AppendLine($@" {symbol.FullName()}.{member} => nameof({symbol.FullName()}.{member}),");
sourceBuilder.Append(
@" _ => defaultValue ?? throw new ArgumentOutOfRangeException(nameof(states), states, null)
};
}");
}
");
}

private static void DisplayNamesDictionary(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e,
private static void DisplayNamesDictionary(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumDisplayNames)
{
sourceBuilder.Append($@"
public static readonly ImmutableDictionary<{symbolName}, string> {SourceGeneratorHelper.PropertyDisplayNamesDictionary} = new Dictionary<{symbolName}, string>
/// <summary>
/// Provides a dictionary that maps <see cref=""{symbol.Name}"" /> values to their corresponding display names.
/// </summary>
public static readonly ImmutableDictionary<{symbol.FullName()}, string> {SourceGeneratorHelper.PropertyDisplayNamesDictionary} = new Dictionary<{symbol.FullName()}, string>
{{
");
foreach (var member in e.Members)
Expand All @@ -261,19 +303,22 @@ private static void DisplayNamesDictionary(StringBuilder sourceBuilder, string s
? found
: key;
sourceBuilder.AppendLine(
$@" {{{symbolName}.{member.Identifier.ValueText}, ""{enumDisplayName ?? key}""}},");
$@" {{{symbol}.{member.Identifier.ValueText}, ""{enumDisplayName ?? key}""}},");
}
sourceBuilder.Append(
@"
}.ToImmutableDictionary();
");
}

private static void DisplayDescriptionsDictionary(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e,
private static void DisplayDescriptionsDictionary(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e,
Dictionary<string, string> enumDescriptionNames)
{
sourceBuilder.Append($@"
public static readonly ImmutableDictionary<{symbolName}, string> {SourceGeneratorHelper.PropertyDisplayDescriptionsDictionary} = new Dictionary<{symbolName}, string>
/// <summary>
/// Provides a dictionary that maps <see cref=""{symbol.Name}"" /> values to their corresponding descriptions.
/// </summary>
public static readonly ImmutableDictionary<{symbol.FullName()}, string> {SourceGeneratorHelper.PropertyDisplayDescriptionsDictionary} = new Dictionary<{symbol.FullName()}, string>
{{
");
foreach (var member in e.Members)
Expand All @@ -283,53 +328,68 @@ private static void DisplayDescriptionsDictionary(StringBuilder sourceBuilder, s
? found
: key;
sourceBuilder.AppendLine(
$@" {{{symbolName}.{member.Identifier.ValueText}, ""{enumDescription ?? key}""}},");
$@" {{{symbol.FullName()}.{member.Identifier.ValueText}, ""{enumDescription ?? key}""}},");
}
sourceBuilder.Append(
@"
}.ToImmutableDictionary();
");
}

private static void GetValuesFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e)
private static void GetValuesFast(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
public static {symbolName}[] {SourceGeneratorHelper.ExtensionMethodNameGetValues}()
/// <summary>
/// Retrieves an array of all <see cref=""{symbol.Name}"" /> enumeration values.
/// </summary>
/// <returns>An array containing all <see cref=""{symbol.Name}"" /> enumeration values.</returns>
public static {symbol.FullName()}[] {SourceGeneratorHelper.ExtensionMethodNameGetValues}()
{{
return new[]
{{
");
foreach (var member in e.Members.Select(x => x.Identifier.ValueText))
sourceBuilder.AppendLine($@" {symbolName}.{member},");
sourceBuilder.AppendLine($@" {symbol.FullName()}.{member},");

sourceBuilder.Append(@" };
}");
}
");
}

private static void GetNamesFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e)
private static void GetNamesFast(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
/// <summary>
/// Retrieves an array of strings containing the names of all <see cref=""{symbol.Name}"" /> enumeration values.
/// </summary>
/// <returns>An array of strings containing the names of all <see cref=""{symbol.Name}"" /> enumeration values.</returns>
public static string[] {SourceGeneratorHelper.ExtensionMethodNameGetNames}()
{{
return new[]
{{
");
foreach (var member in e.Members.Select(x => x.Identifier.ValueText))
sourceBuilder.AppendLine($@" nameof({symbolName}.{member}),");
sourceBuilder.AppendLine($@" nameof({symbol.FullName()}.{member}),");

sourceBuilder.Append(@" };
}");
}
");
}

private static void GetLengthFast(StringBuilder sourceBuilder, string symbolName, EnumDeclarationSyntax e)
private static void GetLengthFast(StringBuilder sourceBuilder, ISymbol symbol, EnumDeclarationSyntax e)
{
sourceBuilder.Append($@"
/// <summary>
/// Gets the length of the <see cref=""{symbol.Name}"" /> enumeration.
/// </summary>
/// <returns>The length of the <see cref=""{symbol.Name}"" /> enumeration.</returns>
public static int {SourceGeneratorHelper.ExtensionMethodNameGetLength}()
{{
return {e.Members.Count};
");

sourceBuilder.Append(@"
}");
}
");
}
}
51 changes: 51 additions & 0 deletions Supernova.Enum.Generators/Extensions/SymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using Microsoft.CodeAnalysis;

namespace Supernova.Enum.Generators.Extensions;

/// <summary>
/// Provides extension methods for <see cref="ISymbol"/> objects.
/// </summary>
public static class SymbolExtensions
{
/// <summary>
/// Gets the full name of the symbol, including namespaces.
/// </summary>
/// <param name="symbol">The symbol.</param>
/// <returns>The full name of the symbol.</returns>
public static string FullName(this ISymbol symbol)
{
// TODO: Use NamespaceSymbolExtensions.FullName after Merge of #70
return $"{symbol.ContainingNamespace.FullNamespace()}.{symbol.Name}";
}

/// <summary>
/// Gets the full name of the namespace, including parent namespaces.
/// </summary>
/// <param name="namespaceSymbol">The namespace symbol.</param>
/// <param name="fullNamespace">Optional. The initial full name to start with.</param>
/// <returns>The full name of the namespace.</returns>
public static string FullNamespace(this INamespaceSymbol namespaceSymbol, string fullNamespace = null)
{
fullNamespace ??= string.Empty;

if (namespaceSymbol == null)
{
return fullNamespace;
}

if (namespaceSymbol.ContainingNamespace != null)
{
fullNamespace = namespaceSymbol.ContainingNamespace.FullNamespace(fullNamespace);
}

if (!fullNamespace.Equals(string.Empty, StringComparison.OrdinalIgnoreCase))
{
fullNamespace += ".";
}

fullNamespace += namespaceSymbol.Name;

return fullNamespace;
}
}

0 comments on commit bfe0a7f

Please sign in to comment.