Closed Xlinka closed 11 months ago
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup>
</Project>
using System;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
[Generator]
public class ProtoFluxBindingGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
}
public void Execute(GeneratorExecutionContext context)
{
var sourceBuilder = new StringBuilder();
var nodes = context.Compilation.SyntaxTrees
.SelectMany(tree => tree.GetRoot().DescendantNodes())
.OfType<ClassDeclarationSyntax>()
.Where(cls => cls.Identifier.Text.EndsWith("Node") && cls.BaseList != null);
foreach (var node in nodes)
{
var nodeName = node.Identifier.Text;
var bindingName = nodeName + "Binding";
var namespaceName = ((NamespaceDeclarationSyntax)node.Parent).Name;
var baseType = node.BaseList.Types.First().Type.ToString();
// Input Properties
var inputProperties = node.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(prop => prop.Modifiers.Any(mod => mod.Text == "public"));
sourceBuilder.AppendLine("using System;");
sourceBuilder.AppendLine("using FrooxEngine;");
sourceBuilder.AppendLine("using FrooxEngine.ProtoFlux;");
sourceBuilder.AppendLine($"namespace {namespaceName}");
sourceBuilder.AppendLine("{");
sourceBuilder.AppendLine($" public class {bindingName} : FrooxEngine.ProtoFlux.Runtimes.Execution.{baseType}Binding<{nodeName}>");
sourceBuilder.AppendLine(" {");
// Generate SyncRef Fields
foreach (var inputProperty in inputProperties)
{
var propertyType = inputProperty.Type.ToString();
var propertyName = inputProperty.Identifier.Text;
sourceBuilder.AppendLine($" public readonly SyncRef<INodeValueOutput<{propertyType}>> {propertyName};");
}
// NodeType Property
sourceBuilder.AppendLine($" public override Type NodeType => typeof({nodeName});");
// NodeInstance Property and Fields
sourceBuilder.AppendLine($" public {nodeName} TypedNodeInstance {{ get; private set; }}");
sourceBuilder.AppendLine(" public override INode NodeInstance => TypedNodeInstance;");
// Instantiate Method
sourceBuilder.AppendLine($" public override TN Instantiate<TN>()");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine($" if (TypedNodeInstance != null) throw new InvalidOperationException(\"Node has already been instantiated\");");
sourceBuilder.AppendLine($" var instance = (TypedNodeInstance = new {nodeName}());");
sourceBuilder.AppendLine(" return instance as TN;");
sourceBuilder.AppendLine(" }");
// AssociateInstanceInternal Method
sourceBuilder.AppendLine($" protected override void AssociateInstanceInternal(INode node)");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine($" if (node is not {nodeName} typedNodeInstance) throw new ArgumentException(\"Node instance is not of type \" + typeof({nodeName}));");
sourceBuilder.AppendLine(" TypedNodeInstance = typedNodeInstance;");
sourceBuilder.AppendLine(" }");
// ClearInstance Method
sourceBuilder.AppendLine(" public override void ClearInstance() => TypedNodeInstance = null;");
// GetInputInternal Method
sourceBuilder.AppendLine($" protected override ISyncRef GetInputInternal(ref int index)");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine(" var inputInternal = base.GetInputInternal(ref index);");
sourceBuilder.AppendLine(" if (inputInternal != null) return inputInternal;");
sourceBuilder.AppendLine(" switch (index)");
sourceBuilder.AppendLine(" {");
int index = 0;
foreach (var inputProperty in inputProperties)
{
var propertyName = inputProperty.Identifier.Text;
sourceBuilder.AppendLine($" case {index}: return {propertyName};");
index++;
}
sourceBuilder.AppendLine(" default:");
sourceBuilder.AppendLine($" index -= {inputProperties.Count()};");
sourceBuilder.AppendLine(" return null;");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine("}");
}
var sourceText = SourceText.From(sourceBuilder.ToString(), Encoding.UTF8);
context.AddSource("GeneratedBindings.cs", sourceText);
}
}
@moonheart08 this might interest you
also this doesn't function how id like it to for now so this is just a example precursor ?
using System;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.IO;
[Generator]
public class ProtoFluxBindingGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
}
public void Execute(GeneratorExecutionContext context)
{
var nodes = context.Compilation.SyntaxTrees
.SelectMany(tree => tree.GetRoot().DescendantNodes())
.OfType<ClassDeclarationSyntax>()
.Where(cls => cls.Identifier.Text.EndsWith("Node") && cls.BaseList != null);
foreach (var node in nodes)
{
string nodeName = node.Identifier.Text;
string bindingName = nodeName + "Binding"; // Use the node name and append "Binding"
var namespaceName = ((NamespaceDeclarationSyntax)node.Parent).Name;
var baseType = node.BaseList.Types.First().Type.ToString();
// Input Properties
var inputProperties = node.DescendantNodes()
.OfType<PropertyDeclarationSyntax>()
.Where(prop => prop.Modifiers.Any(mod => mod.Text == "public"));
var sourceBuilder = new StringBuilder();
sourceBuilder.AppendLine("using System;");
sourceBuilder.AppendLine("using FrooxEngine;");
sourceBuilder.AppendLine("using FrooxEngine.ProtoFlux;");
sourceBuilder.AppendLine($"namespace {namespaceName}");
sourceBuilder.AppendLine("{");
sourceBuilder.AppendLine($" public class {bindingName} : FrooxEngine.ProtoFlux.Runtimes.Execution.{baseType}Binding<{nodeName}>");
sourceBuilder.AppendLine(" {");
// Generate SyncRef Fields
foreach (var inputProperty in inputProperties)
{
var propertyType = inputProperty.Type.ToString();
var propertyName = inputProperty.Identifier.Text;
sourceBuilder.AppendLine($" public readonly SyncRef<INodeValueOutput<{propertyType}>> {propertyName};");
}
// NodeType Property
sourceBuilder.AppendLine($" public override Type NodeType => typeof({nodeName});");
// NodeInstance Property and Fields
sourceBuilder.AppendLine($" public {nodeName} TypedNodeInstance {{ get; private set; }}");
sourceBuilder.AppendLine(" public override INode NodeInstance => TypedNodeInstance;");
// Instantiate Method
sourceBuilder.AppendLine($" public override TN Instantiate<TN>()");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine($" if (TypedNodeInstance != null) throw new InvalidOperationException(\"Node has already been instantiated\");");
sourceBuilder.AppendLine($" var instance = (TypedNodeInstance = new {nodeName}());");
sourceBuilder.AppendLine(" return instance as TN;");
sourceBuilder.AppendLine(" }");
// AssociateInstanceInternal Method
sourceBuilder.AppendLine($" protected override void AssociateInstanceInternal(INode node)");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine($" if (node is not {nodeName} typedNodeInstance) throw new ArgumentException(\"Node instance is not of type \" + typeof({nodeName}));");
sourceBuilder.AppendLine(" TypedNodeInstance = typedNodeInstance;");
sourceBuilder.AppendLine(" }");
// ClearInstance Method
sourceBuilder.AppendLine(" public override void ClearInstance() => TypedNodeInstance = null;");
// GetInputInternal Method
sourceBuilder.AppendLine($" protected override ISyncRef GetInputInternal(ref int index)");
sourceBuilder.AppendLine(" {");
sourceBuilder.AppendLine(" var inputInternal = base.GetInputInternal(ref index);");
sourceBuilder.AppendLine(" if (inputInternal != null) return inputInternal;");
sourceBuilder.AppendLine(" switch (index)");
sourceBuilder.AppendLine(" {");
int index = 0;
foreach (var inputProperty in inputProperties)
{
var propertyName = inputProperty.Identifier.Text;
sourceBuilder.AppendLine($" case {index}: return {propertyName};");
index++;
}
sourceBuilder.AppendLine(" default:");
sourceBuilder.AppendLine($" index -= {inputProperties.Count()};");
sourceBuilder.AppendLine(" return null;");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine(" }");
sourceBuilder.AppendLine("}");
string sourceCode = sourceBuilder.ToString();
string fileName = $"{bindingName}.cs";
// Write the generated source code to a file
File.WriteAllText(fileName, sourceCode);
// Add the generated file to the compilation
context.AddSource(fileName, SourceText.From(sourceCode, Encoding.UTF8));
}
}
}
Awaiting Official one for now will write bindings manually by hand
Instead of waiting for the main team to make a Protoflux generator public we can make our own for the time being using a source generator this will take some time to figure out all the bugs but I'm sure we can do it. as this will help the plugin community as a whole.