using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace MfGames.Nitride.Generators; /// /// Implements a source generator that creates Set* methods for the various /// properties that also returns the same object for purposes of chaining /// together calls. /// [Generator] public class WithPropertiesSourceGenerator : ClassAttributeSourceGeneratorBase { /// protected override WithPropertiesSyntaxReceiver CreateSyntaxReceiver( GeneratorInitializationContext context) { return new WithPropertiesSyntaxReceiver(context); } protected override void GenerateClassFile( GeneratorExecutionContext context, ClassAttributeReference unit) { // Pull out some fields. ClassDeclarationSyntax cds = unit.ClassDeclaration; // Create the partial class. StringBuilder buffer = new(); buffer.AppendLine("#nullable enable"); // Copy the using statements from the file. foreach (UsingDirectiveSyntax? uds in unit.UsingDirectiveList) { buffer.AppendLine(uds.ToString()); } buffer.AppendLine(); // Create the namespace. buffer.AppendLine($"namespace {unit.Namespace}"); buffer.AppendLine("{"); buffer.AppendLine($" public partial class {cds.Identifier}"); buffer.AppendLine(" {"); // Go through the properties of the namespace. IEnumerable properties = cds.Members .Where(m => m.Kind() == SyntaxKind.PropertyDeclaration) .Cast(); bool first = true; foreach (PropertyDeclarationSyntax pds in properties) { // See if we have a setter. bool found = pds.AccessorList?.Accessors .Any(x => x.Keyword.ToString() == "set") ?? false; if (!found) { continue; } // If we aren't first, then add a newline before it. if (first) { first = false; } else { buffer.AppendLine(); } // Write some documentation. buffer.AppendLine(" /// "); buffer.AppendLine( string.Format( " /// Sets the {0} value and returns the operation for chaining.", pds.Identifier.ToString())); buffer.AppendLine(" /// "); // We have the components for writing out a setter. buffer.AppendLine( string.Format( " public virtual {0} With{1}({2} value)", cds.Identifier, pds.Identifier, pds.Type)); buffer.AppendLine(" {"); buffer.AppendLine( string.Format(" this.{0} = value;", pds.Identifier)); buffer.AppendLine(" return this;"); buffer.AppendLine(" }"); } // Finish up the class. buffer.AppendLine(" }"); buffer.AppendLine("}"); // Create the source text and write out the file. var sourceText = SourceText.From(buffer.ToString(), Encoding.UTF8); context.AddSource(cds.Identifier + ".Generated.cs", sourceText); } }