diff --git a/src/ManiaTemplates/Components/MtComponent.cs b/src/ManiaTemplates/Components/MtComponent.cs index 64512a4..484415f 100644 --- a/src/ManiaTemplates/Components/MtComponent.cs +++ b/src/ManiaTemplates/Components/MtComponent.cs @@ -243,7 +243,7 @@ private static HashSet GetSlotNamesInTemplate(XmlNode node) { foreach (var slotName in GetSlotNamesInTemplate(childNode)) { - if (slotNames.Contains(slotName)) + if (slotNames.Contains(slotName) && slotName != "default") { throw new DuplicateSlotException($"""A slot with the name "{slotName}" already exists."""); } diff --git a/src/ManiaTemplates/Components/MtComponentSlot.cs b/src/ManiaTemplates/Components/MtComponentSlot.cs index 0a38fa8..5593b3c 100644 --- a/src/ManiaTemplates/Components/MtComponentSlot.cs +++ b/src/ManiaTemplates/Components/MtComponentSlot.cs @@ -1,12 +1,8 @@ -using ManiaTemplates.ControlElements; -using ManiaTemplates.Lib; - -namespace ManiaTemplates.Components; +namespace ManiaTemplates.Components; public class MtComponentSlot { public required int Scope { get; init; } public required string RenderMethodT4 { get; init; } - public required MtDataContext Context { get; init; } public string Name { get; init; } = "default"; } \ No newline at end of file diff --git a/src/ManiaTemplates/ControlElements/MtDataContext.cs b/src/ManiaTemplates/ControlElements/MtDataContext.cs index 4a6e7ca..1d4f634 100644 --- a/src/ManiaTemplates/ControlElements/MtDataContext.cs +++ b/src/ManiaTemplates/ControlElements/MtDataContext.cs @@ -24,7 +24,6 @@ public MtDataContext NewContext(MtDataContext otherContext) public override string ToString() { - return $"C{_name}"; - // return $"MtContext_{_name}_" + GetHashCode().ToString().Replace("-", "N"); + return _name ?? ""; } } \ No newline at end of file diff --git a/src/ManiaTemplates/Lib/MtTransformer.cs b/src/ManiaTemplates/Lib/MtTransformer.cs index 232d4d1..4f7220a 100644 --- a/src/ManiaTemplates/Lib/MtTransformer.cs +++ b/src/ManiaTemplates/Lib/MtTransformer.cs @@ -1,5 +1,4 @@ using System.CodeDom; -using System.Diagnostics; using System.Dynamic; using System.Globalization; using System.Text; @@ -46,6 +45,9 @@ public string BuildManialink(MtComponent rootComponent, string className, int ve { _namespaces.AddRange(rootComponent.Namespaces); + var renderBodyArguments = + string.Join(',', rootComponent.Slots.Select(slotName => "DoNothing").ToList()); + var body = ProcessNode( XmlStringToNode(rootComponent.TemplateContent), _engine.BaseMtComponents.Overload(rootComponent.ImportedComponents), @@ -61,7 +63,7 @@ public string BuildManialink(MtComponent rootComponent, string className, int ve CreateImportStatements(), ManiaLinkStart(className, version, rootComponent.DisplayLayer), "<#", - "RenderBody();", + $"RenderBody({renderBodyArguments});", "#>", ManiaLinkEnd(), CreateTemplatePropertiesBlock(rootComponent), @@ -79,7 +81,7 @@ public string BuildManialink(MtComponent rootComponent, string className, int ve /// private string CreateDoNothingMethod() { - return _maniaTemplateLanguage.FeatureBlock("""string DoNothing(){ return ""; }""").ToString(); + return _maniaTemplateLanguage.FeatureBlock("private static void DoNothing(){}").ToString(); } /// @@ -89,8 +91,8 @@ private string CreateInsertedManiaScriptsList() { return new StringBuilder() .AppendLine(_maniaTemplateLanguage.FeatureBlockStart()) - .AppendLine("List __insertedOneTimeManiaScripts = new List();") - .AppendLine("List __maniaScriptRenderMethods = new List();") + .AppendLine("private List __insertedOneTimeManiaScripts = new List();") + .AppendLine("private List __maniaScriptRenderMethods = new List();") .AppendLine(_maniaTemplateLanguage.FeatureBlockEnd()) .ToString(); } @@ -102,7 +104,7 @@ private string CreateBodyRenderMethod(string body, MtComponent rootComponent) { var methodArguments = new List(); AppendSlotRenderArgumentsToList(methodArguments, rootComponent); - var bodyRenderMethod = new StringBuilder($"void RenderBody({string.Join(',', methodArguments)}) {{\n"); + var bodyRenderMethod = new StringBuilder($"private void RenderBody({string.Join(',', methodArguments)}) {{\n"); //Root mania script block var rootScriptBlock = ""; @@ -196,45 +198,43 @@ private string CreateRenderMethodsBlock() /// Creates the slot-render method for a given data context. /// private string CreateSlotRenderMethod(MtComponent component, int scope, MtDataContext context, string slotName, - MtComponent rootComponent, string slotContent, MtComponent? parentComponent = null) + MtComponent rootComponent, string slotContent, MtComponent parentComponent) { var methodArguments = new List(); var methodName = GetSlotRenderMethodName(scope, slotName); + //Add slot render methods + AppendSlotRenderArgumentsToList(methodArguments, parentComponent); //Add component properties as arguments - if (parentComponent != null) + foreach (var (localVariableName, localVariableType) in context) { - foreach (var (localVariableName, localVariableType) in context) - { - if (!rootComponent.Properties.ContainsKey(localVariableName)) - { - methodArguments.Add($"{localVariableType} {localVariableName}"); - } - } - - if (parentComponent != rootComponent) + if (!rootComponent.Properties.ContainsKey(localVariableName)) { - AppendComponentPropertiesToMethodArgumentsList(parentComponent, methodArguments); + methodArguments.Add($"{localVariableType} {localVariableName}"); } } - else + + if (parentComponent != rootComponent) { - foreach (var (localVariableName, localVariableType) in context) - { - if (!rootComponent.Properties.ContainsKey(localVariableName)) - { - methodArguments.Add($"{localVariableType} {localVariableName}"); - } - } + AppendComponentPropertiesToMethodArgumentsList(parentComponent, methodArguments); } - //Add slot render methods - AppendSlotRenderArgumentsToList(methodArguments, parentComponent ?? component); - + //Start slot render method declaration var output = new StringBuilder(_maniaTemplateLanguage.FeatureBlockStart()) - .AppendLine("void " + CreateMethodCall(methodName, string.Join(',', methodArguments), "") + " {"); + .AppendLine("private void " + CreateMethodCall(methodName, string.Join(',', methodArguments), "") + " {"); + //Declare component default variables + foreach (var prop in component.Properties.Values) + { + if (prop.Default == null || (parentComponent.Properties.ContainsKey(prop.Name))) + { + continue; + } + + output.AppendLine($"const {prop.Type} {prop.Name} = {WrapIfString(prop, prop.Default)};"); + } + output .AppendLine(_maniaTemplateLanguage.FeatureBlockEnd()) .AppendLine(slotContent) @@ -249,8 +249,7 @@ private string CreateSlotRenderMethod(MtComponent component, int scope, MtDataCo /// Process a ManiaTemplate node. /// private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, MtDataContext context, - MtComponent rootComponent, - MtComponent? parentComponent = null) + MtComponent rootComponent, MtComponent parentComponent) { Snippet snippet = new(); @@ -277,11 +276,12 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M var component = _engine.GetComponent(availableMtComponents[tag].TemplateKey); var slotContents = GetSlotContentsBySlotName(childNode, component, availableMtComponents, currentContext, - rootComponent); + parentComponent, rootComponent); var componentRenderMethodCall = ProcessComponentNode( childNode.GetHashCode(), component, + parentComponent, currentContext, attributeList, ProcessNode( @@ -292,8 +292,7 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M parentComponent: component ), slotContents, - rootComponent: rootComponent, - parentComponent: parentComponent + rootComponent: rootComponent ); subSnippet.AppendLine(_maniaTemplateLanguage.FeatureBlockStart()) @@ -314,7 +313,7 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M case "slot": var slotName = GetNameFromNodeAttributes(attributeList); subSnippet.AppendLine(_maniaTemplateLanguage.FeatureBlockStart()) - .AppendLine(CreateMethodCall($"__slotRenderer_{slotName.ToLower()}?.Invoke")) + .AppendLine(CreateMethodCall($"__slotRenderer_{slotName.ToLower()}")) .AppendLine(_maniaTemplateLanguage.FeatureBlockEnd()); break; @@ -355,7 +354,7 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M } private Dictionary GetSlotContentsBySlotName(XmlNode componentNode, - MtComponent component, MtComponentMap availableMtComponents, MtDataContext context, MtComponent rootComponent) + MtComponent component, MtComponentMap availableMtComponents, MtDataContext context, MtComponent parentComponent, MtComponent rootComponent) { var contentsByName = new Dictionary(); @@ -394,7 +393,7 @@ private Dictionary GetSlotContentsBySlotName(XmlNode componentNo return contentsByName.ToDictionary( kvp => kvp.Key, - kvp => ProcessNode(kvp.Value, availableMtComponents, context, rootComponent) + kvp => ProcessNode(kvp.Value, availableMtComponents, context, rootComponent, parentComponent) ); } @@ -404,12 +403,12 @@ private Dictionary GetSlotContentsBySlotName(XmlNode componentNo private string ProcessComponentNode( int scope, MtComponent component, + MtComponent parentComponent, MtDataContext currentContext, MtComponentAttributes attributeList, string componentBody, IReadOnlyDictionary slotContents, - MtComponent rootComponent, - MtComponent? parentComponent = null + MtComponent rootComponent ) { foreach (var slotName in component.Slots) @@ -423,7 +422,6 @@ private string ProcessComponentNode( _slots.Add(new MtComponentSlot { Scope = scope, - Context = currentContext, Name = slotName, RenderMethodT4 = CreateSlotRenderMethod( component, @@ -432,17 +430,17 @@ private string ProcessComponentNode( slotName, rootComponent, slotContent, - parentComponent: parentComponent + parentComponent ) }); } - var renderMethodName = GetComponentRenderMethodName(component); + var renderMethodName = GetComponentRenderMethodName(component, currentContext); if (!_renderMethods.ContainsKey(renderMethodName)) { _renderMethods.Add( renderMethodName, - CreateComponentRenderMethod(component, renderMethodName, componentBody) + CreateComponentRenderMethod(component, renderMethodName, componentBody, currentContext) ); } @@ -451,6 +449,12 @@ private string ProcessComponentNode( //Create available arguments var renderArguments = new List(); + + //Add local variables to component render method call + foreach (var localVariableName in currentContext.Keys) + { + renderArguments.Add($"{localVariableName}: {localVariableName}"); + } //Attach attributes to render method call foreach (var (attributeName, attributeValue) in attributeList) @@ -490,29 +494,27 @@ private string ProcessComponentNode( var slotArguments = new HashSet(); - //Add local variables + //Add local variables to slot render call foreach (var localVariableName in currentContext.Keys) { - slotArguments.Add(localVariableName); + slotArguments.Add($"{localVariableName}: {localVariableName}"); } //Pass slot renderers - if (parentComponent != null && parentComponent != rootComponent) + if (parentComponent != rootComponent) { foreach (var propertyName in parentComponent.Properties.Keys) { - slotArguments.Add(propertyName); - } - - foreach (var parentSlotName in parentComponent.Slots) - { - slotArguments.Add($"__slotRenderer_{parentSlotName}"); + slotArguments.Add($"{propertyName}: {propertyName}"); } } - var joinedSlotArguments = slotArguments.Select(argument => $"{argument}: {argument}"); + foreach (var parentSlotName in parentComponent.Slots) + { + slotArguments.Add($"__slotRenderer_{parentSlotName}: __slotRenderer_{parentSlotName}"); + } - renderComponentCall.Append(string.Join(", ", joinedSlotArguments)).Append(')'); + renderComponentCall.Append(string.Join(", ", slotArguments)).Append(')'); i++; } @@ -528,21 +530,27 @@ private string ProcessComponentNode( /// /// Creates the method which renders the contents of a component. /// - private string CreateComponentRenderMethod(MtComponent component, string renderMethodName, string componentBody) + private string CreateComponentRenderMethod(MtComponent component, string renderMethodName, string componentBody, MtDataContext currentContext) { var renderMethod = new StringBuilder(_maniaTemplateLanguage.FeatureBlockStart()) - .Append("void ") + .Append("private void ") .Append(renderMethodName) .Append('('); //open method arguments var arguments = new List(); - - //add component properties as arguments with defaults - AppendComponentPropertiesToMethodArgumentsList(component, arguments); + + //Add local variables to component render method call + foreach (var (localVariableName, localVariableType) in currentContext) + { + arguments.Add($"{localVariableType} {localVariableName}"); + } //add slot render methods AppendSlotRenderArgumentsToList(arguments, component); + + //add component properties as arguments with defaults + AppendComponentPropertiesToMethodArgumentsList(component, arguments); //close method arguments renderMethod.Append(string.Join(", ", arguments)) @@ -591,7 +599,7 @@ private static void AppendComponentPropertiesToMethodArgumentsList(MtComponent c /// private static void AppendSlotRenderArgumentsToList(List arguments, MtComponent component) { - arguments.AddRange(component.Slots.Select(slotName => $"Action __slotRenderer_{slotName} = null")); + arguments.AddRange(component.Slots.Select(slotName => $"Action __slotRenderer_{slotName}")); } /// @@ -859,9 +867,9 @@ private static string CreateMethodCall(string methodName, string methodArguments /// /// Returns the method name that renders the given component. /// - private string GetComponentRenderMethodName(MtComponent component) + private string GetComponentRenderMethodName(MtComponent component, MtDataContext context) { - return $"Render_Component_{component.Id()}"; + return $"Render_Component_{component.Id()}{context}"; } /// diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/expected/single-slot-unfilled.xml b/tests/ManiaTemplates.Tests/IntegrationTests/expected/single-slot-unfilled.xml index 4e4372b..81731fe 100644 --- a/tests/ManiaTemplates.Tests/IntegrationTests/expected/single-slot-unfilled.xml +++ b/tests/ManiaTemplates.Tests/IntegrationTests/expected/single-slot-unfilled.xml @@ -3,4 +3,20 @@