Skip to content

Instantly share code, notes, and snippets.

@zbecknell
Created January 22, 2022 03:14
Show Gist options
  • Save zbecknell/7cd0b1d0caf293e19545210f4e7cb014 to your computer and use it in GitHub Desktop.
Save zbecknell/7cd0b1d0caf293e19545210f4e7cb014 to your computer and use it in GitHub Desktop.
A helpful generator for Fluxor.
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