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);
}
}