-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
208 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace UnityAttributes.EnumTypeFor; | ||
|
||
internal sealed record EnumMemberToProcess(string Name) | ||
{ | ||
public string Name { get; } = Name; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#nullable enable | ||
using System.Collections.Generic; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace UnityAttributes.EnumTypeFor; | ||
|
||
internal sealed record EnumToProcess(ITypeSymbol EnumSymbol, ISymbol ForTypeSymbol, List<EnumMemberToProcess> Members, | ||
string? FullNamespace, bool ShortName) | ||
{ | ||
public string FullCsharpName { get; } = EnumSymbol.ToDisplayString(); | ||
public string DocumentationId { get; } = DocumentationCommentId.CreateDeclarationId(EnumSymbol); | ||
public bool ShortName { get; } = ShortName; | ||
|
||
public ITypeSymbol EnumSymbol { get; } = EnumSymbol; | ||
public ISymbol ForTypeSymbol { get; } = ForTypeSymbol; | ||
public List<EnumMemberToProcess> Members { get; } = Members; | ||
public string? FullNamespace { get; } = FullNamespace; | ||
public string ClassName { get; } = | ||
$"{EnumSymbol.Name}For{(ShortName ? ForTypeSymbol.Name : ForTypeSymbol.ToDisplayString().Replace(".", "_"))}"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
namespace UnityAttributes.EnumTypeFor; | ||
|
||
public static class EnumTypeForAttribute | ||
{ | ||
public const string ATTRIBUTE_SHORT_NAME = "EnumTypeFor"; | ||
public const string ATTRIBUTE_FULL_NAME = ATTRIBUTE_SHORT_NAME + "Attribute"; | ||
|
||
public const string ATTRIBUTE_TEXT = | ||
$$""" | ||
/// <auto-generated /> | ||
[global::System.AttributeUsage(global::System.AttributeTargets.Enum, Inherited = true, AllowMultiple = true)] | ||
internal sealed class {{ATTRIBUTE_FULL_NAME}} : global::System.Attribute | ||
{ | ||
private System.Type _type; | ||
private bool _shortName; | ||
public {{ATTRIBUTE_FULL_NAME}}(System.Type type, bool shortName = true) | ||
{ | ||
_type = type; | ||
_shortName = shortName; | ||
} | ||
} | ||
"""; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Text; | ||
using UnityAttributes.Common; | ||
|
||
namespace UnityAttributes.EnumTypeFor; | ||
|
||
[Generator] | ||
public sealed class EnumTypeForGenerator : IIncrementalGenerator | ||
{ | ||
public void Initialize(IncrementalGeneratorInitializationContext context) | ||
{ | ||
var enums = context.SyntaxProvider | ||
.CreateSyntaxProvider( | ||
predicate: static (node, _) => IsSyntaxTargetForGeneration(node), | ||
transform: static (syntaxContext, token) => GetSemanticTargetForGeneration(syntaxContext, token)) | ||
.SelectMany(static (array, _) => array) | ||
.Collect() | ||
.SelectMany(static (array, _) => array); | ||
|
||
context.RegisterPostInitializationOutput(i => i.AddSource( | ||
$"{EnumTypeForAttribute.ATTRIBUTE_FULL_NAME}.g", EnumTypeForAttribute.ATTRIBUTE_TEXT)); | ||
|
||
context.RegisterSourceOutput(enums, GenerateCode); | ||
} | ||
|
||
private static bool IsSyntaxTargetForGeneration(SyntaxNode node) | ||
{ | ||
return node is EnumDeclarationSyntax; | ||
} | ||
|
||
private static List<EnumToProcess> GetSemanticTargetForGeneration(GeneratorSyntaxContext ctx, | ||
CancellationToken token) | ||
{ | ||
var enumDeclarationSyntax = (EnumDeclarationSyntax) ctx.Node; | ||
|
||
var enumDeclarationSymbol = ctx.SemanticModel.GetDeclaredSymbol(enumDeclarationSyntax, token); | ||
if (enumDeclarationSymbol is not ITypeSymbol enumDeclarationTypeSymbol) | ||
{ | ||
return []; | ||
} | ||
|
||
var enumNamespace = enumDeclarationTypeSymbol.GetNamespace(); | ||
|
||
var membersToProcess = new List<EnumMemberToProcess>(); | ||
foreach (var enumMemberDeclarationSyntax in enumDeclarationSyntax.Members) | ||
{ | ||
membersToProcess.Add(new EnumMemberToProcess(enumMemberDeclarationSyntax.Identifier.Text)); | ||
} | ||
|
||
var list = new List<EnumToProcess>(); | ||
foreach (var attributeSyntax in enumDeclarationSyntax.AllAttributesWhere | ||
(syntax => syntax.Name.IsEqualByName(EnumTypeForAttribute.ATTRIBUTE_SHORT_NAME))) | ||
{ | ||
if (attributeSyntax.ArgumentList is null) | ||
{ | ||
continue; | ||
} | ||
|
||
var arguments = attributeSyntax.ArgumentList.Arguments; | ||
if (arguments.Count == 0) | ||
{ | ||
continue; | ||
} | ||
|
||
if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpressionSyntax) | ||
{ | ||
continue; | ||
} | ||
|
||
var forTypeSymbol = ctx.SemanticModel.GetSymbolInfo(typeOfExpressionSyntax.Type).Symbol; | ||
if (forTypeSymbol is null) | ||
{ | ||
continue; | ||
} | ||
|
||
var shortName = true; | ||
if (arguments.Count > 1) | ||
{ | ||
if (arguments[1].Expression is LiteralExpressionSyntax { Token.Text: "false" }) | ||
{ | ||
shortName = false; | ||
} | ||
} | ||
|
||
list.Add(new EnumToProcess( | ||
enumDeclarationTypeSymbol, forTypeSymbol, membersToProcess, enumNamespace, shortName)); | ||
} | ||
|
||
return list; | ||
} | ||
|
||
private static void GenerateCode(SourceProductionContext context, EnumToProcess enumToProcess) | ||
{ | ||
var code = GenerateCode(enumToProcess); | ||
context.AddSource($"{enumToProcess.FullCsharpName}_for_{enumToProcess.ForTypeSymbol.ToDisplayString()}.g", | ||
SourceText.From(code, Encoding.UTF8)); | ||
} | ||
|
||
private static string GenerateCode(EnumToProcess enumToProcess) | ||
{ | ||
var builder = new CodeBuilder(); | ||
|
||
var isVisible = enumToProcess.EnumSymbol.IsVisibleOutsideOfAssembly(); | ||
var methodVisibility = isVisible ? "public" : "internal"; | ||
|
||
var typeName = enumToProcess.ForTypeSymbol.ToDisplayString(); | ||
|
||
builder.AppendLine("/// <auto-generated />").AppendLine(); | ||
|
||
if (!string.IsNullOrEmpty(enumToProcess.FullNamespace)) | ||
{ | ||
builder.Append("namespace ").Append(enumToProcess.FullNamespace!).AppendLine(); | ||
builder.OpenBrackets(); | ||
} | ||
|
||
builder.AppendLineWithIdent("[System.Serializable]"); | ||
builder.AppendIdent().Append(methodVisibility).Append(" class ").AppendLine(enumToProcess.ClassName); | ||
builder.OpenBrackets(); | ||
|
||
foreach (var member in enumToProcess.Members) | ||
{ | ||
builder | ||
.AppendIdent().Append("[UnityEngine.SerializeField] public ") | ||
.Append(typeName).Append(" ") | ||
.Append(member.Name).AppendLine(";"); | ||
} | ||
|
||
builder.AppendLine(); | ||
builder | ||
.AppendIdent().Append("public ").Append(typeName) | ||
.Append(" Get(").Append(enumToProcess.FullCsharpName).AppendLine(" key)"); | ||
builder.OpenBrackets(); | ||
builder.AppendLineWithIdent("return key switch"); | ||
builder.OpenBrackets(); | ||
foreach (var member in enumToProcess.Members) | ||
{ | ||
builder.AppendIdent().Append(enumToProcess.FullCsharpName).Append(".").Append(member.Name) | ||
.Append(" => ").Append(member.Name).AppendLine(","); | ||
} | ||
builder.AppendLineWithIdent("_ => throw new System.ArgumentOutOfRangeException(nameof(key), key, null),"); | ||
builder.DecreaseIdent().AppendLineWithIdent("};"); | ||
builder.CloseBrackets(); | ||
|
||
builder.CloseBrackets(); | ||
|
||
if (!string.IsNullOrEmpty(enumToProcess.FullNamespace)) | ||
{ | ||
builder.CloseBrackets(); | ||
} | ||
|
||
return builder.ToString(); } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters