public void TestCustomCode()
        {
            var visualScript = new VisualScriptAsset();

            // Build blocks
            var functionStart = new FunctionStartBlock();
            var writeTrue     = new CustomCodeBlock {
                Code = "System.Console.Write(true);"
            };
            var method = new Method {
                Name = "Test"
            };

            method.Blocks.Add(functionStart);
            method.Blocks.Add(writeTrue);

            // Generate slots
            foreach (var block in method.Blocks)
            {
                block.Value.GenerateSlots(block.Value.Slots, new SlotGeneratorContext());
            }

            // Build links
            method.Links.Add(new Link(functionStart, writeTrue));

            visualScript.Methods.Add(method);

            // Test
            TestAndCompareOutput(visualScript, "True", testInstance => testInstance.Test());
        }
        public void TestVariableGet()
        {
            var visualScript = new VisualScriptAsset();

            var condition = new Property("bool", "Condition");

            visualScript.Properties.Add(condition);

            // Build blocks
            // TODO: Switch to a simple Write(variable) later, so that we don't depend on ConditionalBranchBlock for this test?
            var functionStart = new FunctionStartBlock();
            var conditionGet  = new VariableGet {
                Name = condition.Name
            };
            var conditionalBranch = new ConditionalBranchBlock();
            var writeTrue         = new CustomCodeBlock {
                Code = "System.Console.Write(true);"
            };
            var writeFalse = new CustomCodeBlock {
                Code = "System.Console.Write(false);"
            };
            var method = new Method {
                Name = "Test"
            };

            method.Blocks.Add(functionStart);
            method.Blocks.Add(conditionGet);
            method.Blocks.Add(conditionalBranch);
            method.Blocks.Add(writeTrue);
            method.Blocks.Add(writeFalse);

            // Generate slots
            foreach (var block in method.Blocks)
            {
                block.Value.GenerateSlots(block.Value.Slots, new SlotGeneratorContext());
            }

            // Build links
            method.Links.Add(new Link(functionStart, conditionalBranch));
            method.Links.Add(new Link(conditionGet.ValueSlot, conditionalBranch.ConditionSlot));
            method.Links.Add(new Link(conditionalBranch.TrueSlot, writeTrue));
            method.Links.Add(new Link(conditionalBranch.FalseSlot, writeFalse));

            visualScript.Methods.Add(method);

            // Test
            TestAndCompareOutput(visualScript, "True", testInstance =>
            {
                testInstance.Condition = true;
                testInstance.Test();
            });

            TestAndCompareOutput(visualScript, "False", testInstance =>
            {
                testInstance.Condition = false;
                testInstance.Test();
            });
        }
        /// <summary>
        /// Adds extra adornments to Code Block, if it contains a Language.
        /// </summary>
        /// <param name="element">CodeBlock Element</param>
        /// <param name="context">Render Context</param>
        protected override void RenderCode(CodeBlock element, IRenderContext context)
        {
            // Renders the Code Block in the standard fashion.
            base.RenderCode(element, context);

            // Don't do any manipulations if the CodeLanguage isn't specified.
            if (string.IsNullOrWhiteSpace(element.CodeLanguage))
            {
                return;
            }

            // Unify all Code Language headers for C#.
            var language = element.CodeLanguage.ToUpper();

            switch (language)
            {
            case "CSHARP":
            case "CS":
                language = "C#";
                break;
            }

            // Grab the Local context and cast it.
            var localContext = context as UIElementCollectionRenderContext;
            var collection   = localContext?.BlockUIElementCollection;

            // Don't go through with it, if there is an issue with the context or collection.
            if (localContext == null || collection?.Any() != true)
            {
                return;
            }

            var lastIndex = collection.Count() - 1;
            var prevIndex = lastIndex - 1;

            // Removes the current Code Block UI from the UI Collection, and wraps it in additional UI.
            if (collection[lastIndex] is ScrollViewer viewer)
            {
                collection.RemoveAt(lastIndex);

                // Combine Code Blocks if a Different Language.
                if (language != "XAML" &&
                    prevIndex >= 0 &&
                    collection[prevIndex] is StackPanel prevPanel &&
                    prevPanel.Tag is CustomCodeBlock block &&
                    !block.Languages.ContainsKey("XAML") && // Prevent combining of XAML Code Blocks.
                    !block.Languages.ContainsKey(language))
                {
                    // Add New Lang to Existing Block
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly
                    block.Languages.Add(language, (viewer, element.Text));
#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly

                    if (prevPanel.Children.FirstOrDefault() is Grid headerGrid)
                    {
                        var langHead = headerGrid.Children.FirstOrDefault();
                        if (langHead is TextBlock textLangHead)
                        {
                            // Replace TextBlock with ComboBox
                            headerGrid.Children.Remove(textLangHead);
                            var combLangHead = new ComboBox
                            {
                                Items =
                                {
                                    textLangHead.Text,
                                    language
                                },
                                SelectedIndex = 0,
                                MinWidth      = 80
                            };

                            headerGrid.Children.Add(combLangHead);

                            combLangHead.SelectionChanged += (s, e) =>
                            {
                                var newLang = combLangHead.SelectedItem as string;
                                block.CurrentLanguage = newLang;
                                LanguageRequested?.Invoke(combLangHead, newLang);

                                var newViewer = block.Languages[newLang].viewer;

                                // Remove old Viewer.
                                var lastItem = prevPanel.Children.Count - 1;
                                if (lastItem >= 0)
                                {
                                    prevPanel.Children.RemoveAt(lastItem);
                                }

                                prevPanel.Children.Add(newViewer);
                            };

                            LanguageRequested += (s, e) =>
                            {
                                if (s != combLangHead)
                                {
                                    if (combLangHead.Items.Contains(e))
                                    {
                                        combLangHead.SelectedItem = e;
                                        block.CurrentLanguage     = e;
                                    }
                                }
                            };

                            if (DesiredLang == language)
                            {
                                combLangHead.SelectedItem = language;
                                block.CurrentLanguage     = language;
                            }
                        }
                        else if (langHead is ComboBox combLangHead)
                        {
                            // Add Lang to ComboBox
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly
                            block.Languages.Add(language, (viewer, element.Text));
#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly
                            combLangHead.Items.Add(language);

                            if (DesiredLang == language)
                            {
                                combLangHead.SelectedItem = language;
                                block.CurrentLanguage     = language;
                            }
                        }
                    }
                }
                else
                {
                    block = new CustomCodeBlock();
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly
                    block.Languages.Add(language, (viewer, element.Text));
#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly
                    block.CurrentLanguage = language;

                    // Creates a Header to specify Language and provide a copy button.
                    var headerGrid = new Grid
                    {
                        Background = new SolidColorBrush(Color.FromArgb(50, 0, 0, 0))
                    };
                    headerGrid.ColumnDefinitions.Add(new ColumnDefinition());
                    headerGrid.ColumnDefinitions.Add(new ColumnDefinition {
                        Width = GridLength.Auto
                    });

                    var languageBlock = new TextBlock
                    {
                        Text = language,
                        VerticalAlignment = VerticalAlignment.Center,
                        Margin            = new Thickness(10, 0, 0, 0)
                    };
                    headerGrid.Children.Add(languageBlock);

                    var copyButton = new Button
                    {
                        Content             = "Copy",
                        HorizontalAlignment = HorizontalAlignment.Stretch,
                        VerticalAlignment   = VerticalAlignment.Stretch
                    };

                    copyButton.Click += (s, e) =>
                    {
                        var text = block.Languages[block.CurrentLanguage].text;

                        var content = new DataPackage();
                        content.SetText(text);
                        Clipboard.SetContent(content);
                    };

                    headerGrid.Children.Add(copyButton);
                    Grid.SetColumn(copyButton, 1);

                    // Collection the adornment and the standard UI, add them to a StackPanel, and add it back to the collection.
                    var panel = new StackPanel
                    {
                        Background = viewer.Background,
                        Margin     = viewer.Margin,
                        Tag        = block
                    };

                    panel.Children.Add(headerGrid);
                    panel.Children.Add(viewer);

                    collection.Add(panel);
                }
            }