Created
January 22, 2022 03:14
-
-
Save zbecknell/7cd0b1d0caf293e19545210f4e7cb014 to your computer and use it in GitHub Desktop.
A helpful generator for Fluxor.
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
using System; | |
using System.Collections.Generic; | |
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
using System.Linq; | |
namespace Generator; | |
[Generator] | |
public class BasicStateGenerator : ISourceGenerator | |
{ | |
public void Initialize(GeneratorInitializationContext context) | |
{ | |
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); | |
} | |
public void Execute(GeneratorExecutionContext context) | |
{ | |
if (!(context.SyntaxReceiver is SyntaxReceiver receiver)) | |
{ | |
return; | |
} | |
var compilation = context.Compilation; | |
var stateAttribute = compilation.GetTypeByMetadataName("Fluxor.FeatureStateAttribute"); | |
try | |
{ | |
foreach (var candidate in receiver.CandidateTypes) | |
{ | |
var model = compilation.GetSemanticModel(candidate.SyntaxTree); | |
var typeSymbol = model.GetDeclaredSymbol(candidate); | |
if (typeSymbol is null) | |
{ | |
continue; | |
} | |
var attributes = typeSymbol.GetAttributes().ToList(); | |
if (attributes.Any(ad => | |
ad.AttributeClass?.Equals(stateAttribute, SymbolEqualityComparer.Default) ?? false)) | |
{ | |
GenerateStateBoilerplate(context, typeSymbol); | |
} | |
} | |
} | |
catch (Exception e) | |
{ | |
context.AddSource("Errors.g.cs", $"/* {e.Message} \n\n {e.StackTrace} \n*/"); | |
} | |
} | |
private void GenerateStateBoilerplate(GeneratorExecutionContext context, ISymbol type) | |
{ | |
var source = @"using Fluxor; | |
namespace {{namespace}}; | |
public static class GeneratedHandlers | |
{ | |
[ReducerMethod] | |
public static {{typeName}} OnUpdateState({{typeName}} state, Update{{typeName}}Action action) | |
{ | |
return action.NewState; | |
} | |
} | |
public class Update{{typeName}}Action | |
{ | |
public Update{{typeName}}Action({{typeName}} newState) | |
{ | |
NewState = newState; | |
} | |
public {{typeName}} NewState { get; set; } | |
} | |
public static class GeneratedDispatcherExtensions | |
{ | |
public static void Update{{typeName}}(this IDispatcher dispatcher, {{typeName}} newState) | |
{ | |
dispatcher.Dispatch(new Update{{typeName}}Action(newState)); | |
} | |
} | |
public static class Generated{{typeName}}Extensions | |
{ | |
/// <summary> | |
/// Updates the {{typeName}} data state. This will cause any components using this state to automatically re-render. | |
/// </summary> | |
public static void Update(this {{typeName}} state, IDispatcher dispatcher) | |
{ | |
dispatcher.Update{{typeName}}(state); | |
} | |
} | |
"; | |
source = source.Replace("{{namespace}}", type.ContainingNamespace.ToString()); | |
source = source.Replace("{{typeName}}", type.Name); | |
context.AddSource($"{type.ContainingNamespace.ToString().Replace(".", "_")}{type.Name}.g.cs", source); | |
} | |
} | |
internal class SyntaxReceiver : ISyntaxReceiver | |
{ | |
internal List<TypeDeclarationSyntax> CandidateTypes { get; } = new(); | |
public void OnVisitSyntaxNode(SyntaxNode syntaxNode) | |
{ | |
// We're only examining nodes with attributes | |
if (syntaxNode is RecordDeclarationSyntax { AttributeLists.Count: > 0 } recordDeclarationSyntax) | |
{ | |
CandidateTypes.Add(recordDeclarationSyntax); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment