2021-09-07 05:15:45 +00:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Text;
|
2022-06-05 18:44:51 +00:00
|
|
|
|
2021-09-07 05:15:45 +00:00
|
|
|
using Microsoft.CodeAnalysis;
|
|
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
namespace Nitride.Generators;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Implements a source generator that creates Set* methods for the various
|
|
|
|
/// properties that also returns the same object for purposes of chaining
|
|
|
|
/// together calls.
|
|
|
|
/// </summary>
|
|
|
|
[Generator]
|
|
|
|
public class WithPropertySourceGenerator : ISourceGenerator
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
public void Execute(GeneratorExecutionContext context)
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
// Get the generator infrastructure will create a receiver and
|
|
|
|
// populate it we can retrieve the populated instance via the
|
|
|
|
// context.
|
|
|
|
var syntaxReceiver = (WithPropertySyntaxReceiver?)context.SyntaxReceiver;
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
if (syntaxReceiver == null)
|
|
|
|
{
|
|
|
|
return;
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Report any messages.
|
|
|
|
foreach (string? message in syntaxReceiver.Messages)
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
context.Information(
|
|
|
|
MessageCode.Debug,
|
|
|
|
Location.Create(
|
|
|
|
"Temporary.g.cs",
|
|
|
|
TextSpan.FromBounds(0, 0),
|
|
|
|
new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 0))),
|
|
|
|
"Generating additional identifier code: {0}",
|
|
|
|
message);
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// If we didn't find anything, then there is nothing to do.
|
|
|
|
if (syntaxReceiver.ClassList.Count == 0)
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Go through each one.
|
|
|
|
foreach (WithPropertyClass classInfo in syntaxReceiver.ClassList)
|
|
|
|
{
|
|
|
|
this.GenerateClassFile(context, classInfo);
|
|
|
|
}
|
|
|
|
}
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
public void Initialize(GeneratorInitializationContext context)
|
|
|
|
{
|
|
|
|
// Register a factory that can create our custom syntax receiver
|
|
|
|
context.RegisterForSyntaxNotifications(() => new WithPropertySyntaxReceiver(context));
|
|
|
|
}
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
private void GenerateClassFile(GeneratorExecutionContext context, WithPropertyClass unit)
|
|
|
|
{
|
|
|
|
// Pull out some fields.
|
|
|
|
ClassDeclarationSyntax? cds = unit.ClassDeclaration;
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Create the partial class.
|
|
|
|
StringBuilder buffer = new();
|
|
|
|
buffer.AppendLine("#nullable enable");
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Copy the using statements from the file.
|
|
|
|
foreach (UsingDirectiveSyntax? uds in unit.UsingDirectiveList)
|
|
|
|
{
|
|
|
|
buffer.AppendLine(uds.ToString());
|
|
|
|
}
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
buffer.AppendLine();
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Create the namespace.
|
|
|
|
buffer.AppendLine($"namespace {unit.Namespace}");
|
|
|
|
buffer.AppendLine("{");
|
|
|
|
buffer.AppendLine($" public partial class {cds.Identifier}");
|
|
|
|
buffer.AppendLine(" {");
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// Go through the properties of the namespace.
|
|
|
|
IEnumerable<PropertyDeclarationSyntax> properties = cds.Members
|
|
|
|
.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration)
|
|
|
|
.Cast<PropertyDeclarationSyntax>();
|
|
|
|
bool first = true;
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
foreach (PropertyDeclarationSyntax pds in properties)
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
// See if we have a setter.
|
|
|
|
bool found = pds.AccessorList?.Accessors.Any(x => x.Keyword.ToString() == "set") ?? false;
|
2021-09-07 05:15:45 +00:00
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
if (!found)
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
continue;
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 18:44:51 +00:00
|
|
|
// If we aren't first, then add a newline before it.
|
|
|
|
if (first)
|
|
|
|
{
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
2021-09-07 05:15:45 +00:00
|
|
|
{
|
2022-06-05 18:44:51 +00:00
|
|
|
buffer.AppendLine();
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
2022-06-05 18:44:51 +00:00
|
|
|
|
|
|
|
// Write some documentation.
|
|
|
|
buffer.AppendLine(" /// <summary>");
|
|
|
|
buffer.AppendLine(
|
|
|
|
string.Format(
|
|
|
|
" /// Sets the {0} value and returns the operation for chaining.",
|
|
|
|
pds.Identifier.ToString()));
|
|
|
|
buffer.AppendLine(" /// </summary>");
|
|
|
|
|
|
|
|
// We have the components for writing out a setter.
|
|
|
|
buffer.AppendLine(
|
|
|
|
string.Format(" public {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(" }");
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
2022-06-05 18:44:51 +00:00
|
|
|
|
|
|
|
// 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);
|
2021-09-07 05:15:45 +00:00
|
|
|
}
|
|
|
|
}
|