diff --git a/src/ManiaTemplates/Exceptions/CurlyBraceCountMismatchException.cs b/src/ManiaTemplates/Exceptions/CurlyBraceCountMismatchException.cs new file mode 100644 index 0000000..74998af --- /dev/null +++ b/src/ManiaTemplates/Exceptions/CurlyBraceCountMismatchException.cs @@ -0,0 +1,16 @@ +namespace ManiaTemplates.Exceptions; + +public class CurlyBraceCountMismatchException : Exception +{ + public CurlyBraceCountMismatchException() + { + } + + public CurlyBraceCountMismatchException(string message) : base(message) + { + } + + public CurlyBraceCountMismatchException(string message, Exception inner) : base(message, inner) + { + } +} \ No newline at end of file diff --git a/src/ManiaTemplates/Exceptions/EmptyNodeAttributeException.cs b/src/ManiaTemplates/Exceptions/EmptyNodeAttributeException.cs new file mode 100644 index 0000000..e5fccfa --- /dev/null +++ b/src/ManiaTemplates/Exceptions/EmptyNodeAttributeException.cs @@ -0,0 +1,16 @@ +namespace ManiaTemplates.Exceptions; + +public class EmptyNodeAttributeException : Exception +{ + public EmptyNodeAttributeException() + { + } + + public EmptyNodeAttributeException(string message) : base(message) + { + } + + public EmptyNodeAttributeException(string message, Exception inner) : base(message, inner) + { + } +} \ No newline at end of file diff --git a/src/ManiaTemplates/Exceptions/InterpolationRecursionException.cs b/src/ManiaTemplates/Exceptions/InterpolationRecursionException.cs new file mode 100644 index 0000000..9ef3bb0 --- /dev/null +++ b/src/ManiaTemplates/Exceptions/InterpolationRecursionException.cs @@ -0,0 +1,16 @@ +namespace ManiaTemplates.Exceptions; + +public class InterpolationRecursionException : Exception +{ + public InterpolationRecursionException() + { + } + + public InterpolationRecursionException(string message) : base(message) + { + } + + public InterpolationRecursionException(string message, Exception inner) : base(message, inner) + { + } +} \ No newline at end of file diff --git a/src/ManiaTemplates/Lib/MtTransformer.cs b/src/ManiaTemplates/Lib/MtTransformer.cs index 26e326d..244d0c3 100644 --- a/src/ManiaTemplates/Lib/MtTransformer.cs +++ b/src/ManiaTemplates/Lib/MtTransformer.cs @@ -57,7 +57,9 @@ public string BuildManialink(MtComponent rootComponent, string className, int ve var body = ProcessNode( XmlStringToNode(rootComponent.TemplateContent), _engine.BaseMtComponents.Overload(rootComponent.ImportedComponents), - rootContext + rootContext, + parentComponent: rootComponent, + isRootNode: true ); var template = new Snippet @@ -86,7 +88,7 @@ public string BuildManialink(MtComponent rootComponent, string className, int ve /// private string CreateDoNothingMethod() { - return _maniaTemplateLanguage.FeatureBlock(@"string DoNothing(){return """";}").ToString(); + return _maniaTemplateLanguage.FeatureBlock("""string DoNothing(){ return ""; }""").ToString(); } /// @@ -119,7 +121,7 @@ private string CreateBodyRenderMethod(string body, MtDataContext context, MtComp bodyRenderMethod.AppendLine($"var __data = {renderBodyArguments};"); //Root mania script block - string rootScriptBlock = ""; + var rootScriptBlock = ""; if (rootComponent.Scripts.Count > 0) { rootScriptBlock = CreateManiaScriptBlock(rootComponent); @@ -234,12 +236,12 @@ private string CreateSlotRenderMethod(MtComponent component, int scope, MtDataCo if (context.ParentContext != null) { - output.AppendLine(CreateLocalVariablesFromContext(context.ParentContext)); + output.AppendLine(CreateLocalVariablesFromContext(context.ParentContext, parentComponent?.Properties.Keys)); variablesInherited.AddRange(context.ParentContext.Keys); } output - .AppendLine(CreateLocalVariablesFromContext(context, variablesInherited)) + .AppendLine(CreateLocalVariablesFromContext(context, parentComponent?.Properties.Keys)) .AppendLine(_maniaTemplateLanguage.FeatureBlockEnd()) .AppendLine(slotContent) .AppendLine(_maniaTemplateLanguage.FeatureBlockStart()) @@ -270,7 +272,7 @@ private static string CreateLocalVariablesFromContext(MtDataContext context, /// Process a ManiaTemplate node. /// private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, MtDataContext context, - MtComponent? parentComponent = null) + MtComponent? parentComponent = null, bool isRootNode = false) { Snippet snippet = new(); @@ -312,7 +314,8 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M component ), slotContents, - parentComponent + parentComponent, + isRootNode ); subSnippet.AppendLine(_maniaTemplateLanguage.FeatureBlockStart()) @@ -345,7 +348,8 @@ private string ProcessNode(XmlNode node, MtComponentMap availableMtComponents, M if (hasChildren) { subSnippet.AppendLine(1, - ProcessNode(childNode, availableMtComponents, currentContext)); + ProcessNode(childNode, availableMtComponents, currentContext, + parentComponent: parentComponent)); subSnippet.AppendLine(CreateXmlClosingTag(tag)); } @@ -384,8 +388,15 @@ private Dictionary GetSlotContentsBySlotName(XmlNode componentNo continue; } + //Get the name from the slot attribute, or default if not found. var slotName = childNode.Attributes?["slot"]?.Value.ToLower() ?? "default"; + if (slotName.Trim().Length == 0) + { + throw new EmptyNodeAttributeException( + $"There's a template tag with empty slot name in <{componentNode.Name}>."); + } + if (slotName == "default") { //Do not strip contents for default slot from node @@ -420,7 +431,8 @@ private string ProcessComponentNode( MtComponentAttributes attributeList, string componentBody, IReadOnlyDictionary slotContents, - MtComponent? parentComponent = null + MtComponent? parentComponent = null, + bool isRootNode = false ) { foreach (var slotName in component.Slots) @@ -527,7 +539,7 @@ private string ProcessComponentNode( renderComponentCall.Append( $", __slotRenderer_{parentSlotName}: __slotRenderer_{parentSlotName}"); } - + foreach (var propertyName in parentComponent.Properties.Keys) { renderComponentCall.Append($",{propertyName}: {propertyName}"); @@ -537,11 +549,10 @@ private string ProcessComponentNode( { foreach (var parentSlotName in component.Slots) { - renderComponentCall.Append( - $", __slotRenderer_{parentSlotName}: () => DoNothing()"); + renderComponentCall.Append($", __slotRenderer_{parentSlotName}: () => DoNothing()"); } } - + renderComponentCall.Append(')'); i++; @@ -554,7 +565,7 @@ private string ProcessComponentNode( return renderComponentCall.ToString(); } - + /// /// Creates the method which renders the contents of a component. /// @@ -775,7 +786,7 @@ private string CreateManiaScriptDirectivesBlock() } /// - /// Checks the attribute list for if-condition and returns it, else null. + /// Checks the attribute list for if-condition and if found removes & returns it, else null. /// private string? GetIfConditionFromNodeAttributes(MtComponentAttributes attributeList) { @@ -783,7 +794,7 @@ private string CreateManiaScriptDirectivesBlock() } /// - /// Checks the attribute list for name and returns it, else "default". + /// Checks the attribute list for name and if found removes & returns it, else "default". /// private string GetNameFromNodeAttributes(MtComponentAttributes attributeList) { @@ -922,7 +933,7 @@ private static string CreateContextClassProperties(MtDataContext context) /// /// Parses the attributes of a XmlNode to an MtComponentAttributes-instance. /// - private static MtComponentAttributes GetXmlNodeAttributes(XmlNode node) + public static MtComponentAttributes GetXmlNodeAttributes(XmlNode node) { var attributeList = new MtComponentAttributes(); if (node.Attributes == null) return attributeList; @@ -940,6 +951,8 @@ private static MtComponentAttributes GetXmlNodeAttributes(XmlNode node) /// private string BuildManiaScripts(MtComponent rootComponent) { + //TODO: check if method can be removed + var maniaScripts = rootComponent.Scripts.ToDictionary(script => script.ContentHash()); foreach (var (key, value) in _maniaScripts) { @@ -1005,7 +1018,7 @@ private static MtDataContext GetContextFromComponent(MtComponent component, stri /// Returns C# code representation of the type. /// /// The type. - private static string GetFormattedName(Type type) + public static string GetFormattedName(Type type) { if (type.IsSubclassOf(typeof(DynamicObject))) { @@ -1081,7 +1094,7 @@ public static string WrapStringInQuotes(string str) /// name = Shown in in-game debugger. /// version = Version for the markup language of Trackmania. /// - private static string ManiaLinkStart(string name, int version = 3, string? displayLayer = null) + public static string ManiaLinkStart(string name, int version = 3, string? displayLayer = null) { var layer = ""; if (displayLayer != null) @@ -1103,7 +1116,7 @@ private static string ManiaLinkEnd() /// /// Creates a xml opening tag for the given string and attribute list. /// - private string CreateXmlOpeningTag(string tag, MtComponentAttributes attributeList, bool hasChildren) + public string CreateXmlOpeningTag(string tag, MtComponentAttributes attributeList, bool hasChildren) { var output = $"<{tag}"; @@ -1132,7 +1145,7 @@ private static string CreateXmlClosingTag(string tag) /// /// Converts any valid XML-string into an XmlNode-element. /// - private static XmlNode XmlStringToNode(string content) + public static XmlNode XmlStringToNode(string content) { var doc = new XmlDocument(); doc.LoadXml($"{content}"); @@ -1145,6 +1158,9 @@ private static XmlNode XmlStringToNode(string content) /// public static string ReplaceCurlyBraces(string value, Func curlyContentWrapper) { + CheckForCurlyBraceCountMismatch(value); + CheckInterpolationRecursion(value); + var matches = TemplateInterpolationRegex.Match(value); var output = value; @@ -1161,10 +1177,59 @@ public static string ReplaceCurlyBraces(string value, Func curly return output; } + /// + /// Checks whether double interpolation exists ({{ {{ a }} {{ b }} }}) and throws exception if so. + /// + public static void CheckInterpolationRecursion(string value) + { + var openCurlyBraces = 0; + foreach (var character in value.ToCharArray()) + { + if (character == '{') + { + openCurlyBraces++; + + if (openCurlyBraces >= 4) + { + throw new InterpolationRecursionException( + $"Double interpolation found in: {value}. You must not use double curly braces inside other double curly braces."); + } + } + else if (character == '}') + { + openCurlyBraces--; + } + } + } + + /// + /// Checks whether double interpolation exists ({{ {{ a }} {{ b }} }}) and throws exception if so. + /// + public static void CheckForCurlyBraceCountMismatch(string value) + { + var openCurlyBraces = 0; + foreach (var character in value.ToCharArray()) + { + if (character == '{') + { + openCurlyBraces++; + } + else if (character == '}') + { + openCurlyBraces--; + } + } + + if (openCurlyBraces != 0) + { + throw new CurlyBraceCountMismatchException($"Found curly brace count mismatch in: {value}."); + } + } + /// /// Joins consecutive feature blocks to reduce generated code. /// - private static string JoinFeatureBlocks(string manialink) + public static string JoinFeatureBlocks(string manialink) { var match = TemplateFeatureControlRegex.Match(manialink); var output = new Snippet(); diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/ManialinkEngineTest.cs b/tests/ManiaTemplates.Tests/IntegrationTests/ManialinkEngineTest.cs index 6ffa376..486156a 100644 --- a/tests/ManiaTemplates.Tests/IntegrationTests/ManialinkEngineTest.cs +++ b/tests/ManiaTemplates.Tests/IntegrationTests/ManialinkEngineTest.cs @@ -88,17 +88,20 @@ await Assert.ThrowsAsync(() => [Fact] public async void Should_Render_Component_Without_Content_For_Slot() { - var slotRecursionOuterTwoTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-outer-two.mt"); - var slotRecursionOuterTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-outer.mt"); - var slotRecursionInnerTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-inner.mt"); + var slotRecursionOuterTwoTemplate = + await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-outer-two.mt"); + var slotRecursionOuterTemplate = + await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-outer.mt"); + var slotRecursionInnerTemplate = + await File.ReadAllTextAsync("IntegrationTests/templates/slot-recursion-inner.mt"); var expected = await File.ReadAllTextAsync("IntegrationTests/expected/single-slot-unfilled.xml"); var assemblies = new[] { typeof(ManiaTemplateEngine).Assembly, typeof(ComplexDataType).Assembly }; - + _maniaTemplateEngine.AddTemplateFromString("SlotRecursionOuterTwo", slotRecursionOuterTwoTemplate); _maniaTemplateEngine.AddTemplateFromString("SlotRecursionOuter", slotRecursionOuterTemplate); _maniaTemplateEngine.AddTemplateFromString("SlotRecursionInner", slotRecursionInnerTemplate); - - var template = _maniaTemplateEngine.RenderAsync("SlotRecursionInner", new{}, assemblies).Result; + + var template = _maniaTemplateEngine.RenderAsync("SlotRecursionInner", new { }, assemblies).Result; Assert.Equal(expected, template, ignoreLineEndingDifferences: true); } @@ -110,15 +113,32 @@ public async void Should_Pass_Properties_To_Components_And_Slots() var testComponentTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/component.mt"); var expected = await File.ReadAllTextAsync("IntegrationTests/expected/property-test.xml"); var assemblies = new[] { typeof(ManiaTemplateEngine).Assembly, typeof(ComplexDataType).Assembly }; - + _maniaTemplateEngine.AddTemplateFromString("PropertyTest", propertyTestTemplate); _maniaTemplateEngine.AddTemplateFromString("Wrapper", testWrapperTemplate); _maniaTemplateEngine.AddTemplateFromString("TestComponent", testComponentTemplate); - + var template = _maniaTemplateEngine.RenderAsync("PropertyTest", new { testVariable = "integration" }, assemblies).Result; Assert.Equal(expected, template, ignoreLineEndingDifferences: true); } + + [Fact] + public async void Should_Fill_All_Slots() + { + var baseTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slots/base.mt"); + var containerTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slots/container.mt"); + var windowTemplate = await File.ReadAllTextAsync("IntegrationTests/templates/slots/window.mt"); + var expected = await File.ReadAllTextAsync("IntegrationTests/expected/slots/manialink.xml"); + var assemblies = new[] { typeof(ManiaTemplateEngine).Assembly, typeof(ComplexDataType).Assembly }; + + _maniaTemplateEngine.AddTemplateFromString("Base", baseTemplate); + _maniaTemplateEngine.AddTemplateFromString("Container", containerTemplate); + _maniaTemplateEngine.AddTemplateFromString("Window", windowTemplate); + + var template = _maniaTemplateEngine.RenderAsync("Base", new { }, assemblies).Result; + Assert.Equal(expected, template, ignoreLineEndingDifferences: true); + } } \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/expected/slots/manialink.xml b/tests/ManiaTemplates.Tests/IntegrationTests/expected/slots/manialink.xml new file mode 100644 index 0000000..9bb5746 --- /dev/null +++ b/tests/ManiaTemplates.Tests/IntegrationTests/expected/slots/manialink.xml @@ -0,0 +1,10 @@ + + + + diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/base.mt b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/base.mt new file mode 100644 index 0000000..f190a0b --- /dev/null +++ b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/base.mt @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/container.mt b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/container.mt new file mode 100644 index 0000000..005b411 --- /dev/null +++ b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/container.mt @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/window.mt b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/window.mt new file mode 100644 index 0000000..5a9a4c6 --- /dev/null +++ b/tests/ManiaTemplates.Tests/IntegrationTests/templates/slots/window.mt @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/Lib/MtTransformerTest.cs b/tests/ManiaTemplates.Tests/Lib/MtTransformerTest.cs index 445376e..ea61d45 100644 --- a/tests/ManiaTemplates.Tests/Lib/MtTransformerTest.cs +++ b/tests/ManiaTemplates.Tests/Lib/MtTransformerTest.cs @@ -1,6 +1,8 @@ using System.Reflection; using System.Text.RegularExpressions; +using System.Xml; using ManiaTemplates.Components; +using ManiaTemplates.Exceptions; using ManiaTemplates.Languages; using ManiaTemplates.Lib; using Xunit.Abstractions; @@ -13,6 +15,7 @@ public class MtTransformerTest private readonly MtTransformer _transformer; private readonly ManiaTemplateEngine _maniaTemplateEngine = new(); private readonly Regex _hashCodePattern = new("[0-9]{6,10}"); + private readonly MtComponent _testComponent = new() { Namespaces = new() { "namespace" }, @@ -131,7 +134,25 @@ public void Should_Import_Components_Recursively() } [Fact] - public void Should_Replace_Curly_Braces_Correctly() + public void Should_Throw_Interpolation_Recursion_Exception() + { + Assert.Throws(() => + MtTransformer.CheckInterpolationRecursion("{{ {{ a }} {{ b }} }}")); + Assert.Throws(() => + MtTransformer.CheckInterpolationRecursion("{{ {{ b }} }}")); + } + + [Fact] + public void Should_Throw_Curly_Brace_Count_Mismatch_Exception() + { + Assert.Throws(() => MtTransformer.CheckForCurlyBraceCountMismatch("{{ { }}")); + Assert.Throws(() => MtTransformer.CheckForCurlyBraceCountMismatch("{{ } }}")); + Assert.Throws(() => MtTransformer.CheckForCurlyBraceCountMismatch("{")); + Assert.Throws(() => MtTransformer.CheckForCurlyBraceCountMismatch("}}")); + } + + [Fact] + public void Should_Replace_Curly_Braces() { Assert.Equal("abcd", MtTransformer.ReplaceCurlyBraces("{{a}}{{ b }}{{c }}{{ d}}", s => s)); Assert.Equal("x y z", MtTransformer.ReplaceCurlyBraces("{{x}} {{ y }} {{z }}", s => s)); @@ -144,5 +165,69 @@ public void Should_Replace_Curly_Braces_Correctly() public void Should_Wrap_Strings_In_Quotes() { Assert.Equal(@"$""unit test""", MtTransformer.WrapStringInQuotes("unit test")); + Assert.Equal(@"$""""", MtTransformer.WrapStringInQuotes("")); + } + + [Fact] + public void Should_Join_Feature_Blocks() + { + Assert.Equal("<#+\n unittest \n#>", + MtTransformer.JoinFeatureBlocks("<#+#><#+\n #> <#+ unittest \n#><#+ \n\n\n#>")); + } + + [Fact] + public void Should_Convert_String_To_Xml_Node() + { + var node = MtTransformer.XmlStringToNode("test"); + Assert.IsAssignableFrom(node); + Assert.Equal("test", node.InnerText); + Assert.Equal("test", node.InnerXml); + Assert.Equal("test", node.OuterXml); + } + + [Fact] + public void Should_Create_Xml_Opening_Tag() + { + var attributeList = new MtComponentAttributes(); + Assert.Equal("", _transformer.CreateXmlOpeningTag("test", attributeList, false)); + Assert.Equal("", _transformer.CreateXmlOpeningTag("test", attributeList, true)); + + attributeList["prop"] = "value"; + Assert.Equal("""""", _transformer.CreateXmlOpeningTag("test", attributeList, false)); + Assert.Equal("""""", _transformer.CreateXmlOpeningTag("test", attributeList, true)); + } + + [Fact] + public void Should_Create_ManiaLink_Opening_Tag() + { + Assert.Equal("""""", + MtTransformer.ManiaLinkStart("Test", 99)); + Assert.Equal("""""", + MtTransformer.ManiaLinkStart("Test", 99, "SomeLayer")); + } + + [Fact] + public void Should_Convert_Xml_Node_Arguments_To_MtComponentAttributes_Instance() + { + var node = MtTransformer.XmlStringToNode("""testContent"""); + if (node.FirstChild == null) return; + + var attributes = MtTransformer.GetXmlNodeAttributes(node.FirstChild); + Assert.Equal(2, attributes.Count); + Assert.Equal("test1", attributes["arg1"]); + Assert.Equal("test2", attributes["arg2"]); + } + + [Fact] + public void Should_Detect_Correct_Type_String_For_CSharp_Scripting() + { + Assert.Equal("int", MtTransformer.GetFormattedName(0.GetType())); + Assert.Equal("double", MtTransformer.GetFormattedName(0.0.GetType())); + Assert.Equal("string", MtTransformer.GetFormattedName("test".GetType())); + Assert.Equal("System.Collections.Generic.List", + MtTransformer.GetFormattedName(new List().GetType())); + Assert.Equal("System.Collections.Generic.HashSet", + MtTransformer.GetFormattedName(new HashSet().GetType())); + Assert.Equal("dynamic", MtTransformer.GetFormattedName(new TestDynamicObject().GetType())); } } \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/Lib/TestDynamicObject.cs b/tests/ManiaTemplates.Tests/Lib/TestDynamicObject.cs new file mode 100644 index 0000000..471cbe1 --- /dev/null +++ b/tests/ManiaTemplates.Tests/Lib/TestDynamicObject.cs @@ -0,0 +1,7 @@ +using System.Dynamic; + +namespace ManiaTemplates.Tests.Lib; + +public class TestDynamicObject : DynamicObject +{ +} \ No newline at end of file diff --git a/tests/ManiaTemplates.Tests/Lib/expected.tt b/tests/ManiaTemplates.Tests/Lib/expected.tt index f49dc93..596dd8e 100644 --- a/tests/ManiaTemplates.Tests/Lib/expected.tt +++ b/tests/ManiaTemplates.Tests/Lib/expected.tt @@ -32,18 +32,18 @@ i = data.i; } List __insertedOneTimeManiaScripts = new List(); List __maniaScriptRenderMethods = new List(); -string DoNothing(){return "";} +string DoNothing(){ return ""; } void RenderBody() { var __data = new CRoot { numbers = numbers,enabled = enabled }; var __outerIndex1 = 0; foreach (int i in numbers) { var __index = __outerIndex1; if (enabled) { -Render_Component_MtContext2(__data: __data, x: (20 * __index), __slotRenderer_default: () => Render_Slot_3_default(__data: new CRoot_ForEachLoop1(__data){__index = __index, i = i}, __slotRenderer_default: () => DoNothing())); +Render_Component_MtContext2(__data: __data, x: (20 * __index), __slotRenderer_default: () => Render_Slot_3_default(__data: new CRoot_ForEachLoop1(__data){__index = __index, i = i},numbers: numbers,enabled: enabled)); } __outerIndex1++; } -Render_Component_MtContext2(__data: __data, __slotRenderer_default: () => Render_Slot_4_default(__data: __data, __slotRenderer_default: () => DoNothing())); +Render_Component_MtContext2(__data: __data, __slotRenderer_default: () => Render_Slot_4_default(__data: __data,numbers: numbers,enabled: enabled)); foreach(var maniaScriptRenderMethod in __maniaScriptRenderMethods){ maniaScriptRenderMethod(); } #> <#+ } -void Render_Slot_3_default(CRoot_ForEachLoop1 __data,Action __slotRenderer_default) { -var numbers = __data.numbers; -var enabled = __data.enabled; +void Render_Slot_3_default(CRoot_ForEachLoop1 __data,List numbers,boolean enabled = true) { var __index = __data.__index; var i = __data.i; var __outerIndex7 = 0; @@ -115,9 +113,7 @@ Render_Component_MtContext6(__data: __data, arg3: (new test())); <#+ } -void Render_Slot_4_default(CRoot __data,Action __slotRenderer_default) { -var numbers = __data.numbers; -var enabled = __data.enabled; +void Render_Slot_4_default(CRoot __data,List numbers,boolean enabled = true) { Render_Component_MtContext2(__data: __data, __slotRenderer_default: () => Render_Slot_8_default(__data: __data, __slotRenderer_default: () => DoNothing())); } #> \ No newline at end of file