aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.IDE/ProjectItemsViews/CSharpScriptItemView.xaml
blob: 682956205b29cd95bbf6dc3e3a1f4d9d3b111a12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
<UserControl x:Class="Tango.Scripting.IDE.ProjectItemsViews.CSharpScriptItemView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Tango.Scripting.IDE.ProjectItemsViews"
             xmlns:editors="clr-namespace:Tango.Scripting.Editors;assembly=Tango.Scripting.Editors"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <editors:ScriptEditor />
    </Grid>
</UserControl>
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml;
using Tango.Core;
using Tango.Core.Commands;
using Tango.Scripting.Core;
using Tango.Scripting.Editors.CodeCompletion;
using Tango.Scripting.Editors.Document;
using Tango.Scripting.Editors.Editing;
using Tango.Scripting.Editors.Folding;
using Tango.Scripting.Editors.Highlighting;
using Tango.Scripting.Editors.Highlighting.Xshd;
using Tango.Scripting.Editors.Intellisense;
using Tango.Scripting.Editors.Popups;
using Tango.Scripting.Editors.Rendering;
using Tango.Scripting.Formatting;
using Tango.Scripting.Parsing;

namespace Tango.Scripting.Editors
{
    public class ScriptEditor : TextEditor
    {
        private char[] word_separators = { ' ', '\t', '\n', '.', '(', ',', '-', '*', '/', '+', '$', '=', '<', '>' };
        private string[] _blocking_type_words = { "class", "void" };

        public event EventHandler<BreakPointSymbolPressedEventArgs> BreakPointSymbolPressed;

        private DispatcherTimer _update_timer;
        private BreakPointMargin breakPointMargin;
        private Popup _popup;
        private FoldingManager foldingManager;
        private BraceFoldingStrategy foldingStrategy;
        private CompletionWindow completionWindow;
        private ScriptParser _parser;
        private List<String> _current_usings;
        private List<KnownType> _knownTypes;
        private List<ScriptType> _declaredTypes;
        private bool _isLoadingTypes;
        private TextMarkerService errorMarkerService;
        private List<ScriptBreakPointSymbol> _breakPointSymbols;
        private int _breakPointLineNumber;
        private ScriptBreakPointSymbol _currentBreakPointSymbol;
        private Point _currentBreakPointSymbolPosition;

        private static JsonSerializerSettings _jsonSettings;
        private static Dictionary<Type, KnownType> _knownTypesCache;
        private static String KNOWN_TYPES_CACHE_FOLDER;
        private static List<CachedAssembly> _cachedAssemblies;
        private static List<CachedUsing> _cachedUsings;
        private static bool _isLoadingCachedAssemblies;
        private static bool _isCacheAssembliesLoaded;
        private static bool _isUsingsLoadingStarted;
        private static object _loadUsingsLock = new object();
        private static List<SnippetCompletionItem> snippets;

        public static event EventHandler<TangoProgressChangedEventArgs<int>> LoadingSymbolsProgress;
        public static event EventHandler LoadingSymbolsStarted;
        public static event EventHandler LoadingSymbolsCompleted;
        private static event EventHandler KnownTypesAvailable;
        public static event EventHandler UsingsLoadingStarted;
        public static event EventHandler UsingsLoadingCompleted;

        #region Mini Classes

        private class KnownTypeResult
        {
            public KnownType KnownType { get; set; }
            public bool IsStatic { get; set; }

            public KnownTypeResult()
            {

            }

            public KnownTypeResult(KnownType knownType)
            {
                KnownType = knownType;
            }
        }

        private class ScriptClass
        {
            public String Name { get; set; }
            public int Index { get; set; }
        }

        private class ConstructionSession
        {
            public KnownType Type { get; set; }
            public int ParameterIndex { get; set; }
            public List<String> TypeArguments { get; set; }
        }

        private class MethodSession
        {
            public KnownType Type { get; set; }
            public String MethodName { get; set; }
            public int ParameterIndex { get; set; }
        }

        private class DeclaredMethodSession
        {
            public ScriptType Type { get; set; }
            public ScriptSymbol Method { get; set; }
        }

        #endregion

        #region Properties

        public static List<String> BlockedUsingsCache { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether to enable folding.
        /// </summary>
        public bool EnableFolding
        {
            get { return (bool)GetValue(EnableFoldingProperty); }
            set { SetValue(EnableFoldingProperty, value); }
        }
        public static readonly DependencyProperty EnableFoldingProperty =
            DependencyProperty.Register("EnableFolding", typeof(bool), typeof(ScriptEditor), new PropertyMetadata(true, (d, e) => (d as ScriptEditor).OnEnableFoldingChanged()));

        /// <summary>
        /// 
        /// </summary>
        public RelayCommand IndentCommand
        {
            get { return (RelayCommand)GetValue(IndentCommandProperty); }
            set { SetValue(IndentCommandProperty, value); }
        }
        public static readonly DependencyProperty IndentCommandProperty =
            DependencyProperty.Register("IndentCommand", typeof(RelayCommand), typeof(ScriptEditor), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the reference assemblies.
        /// </summary>
        public ObservableCollection<Assembly> ReferenceAssemblies
        {
            get { return (ObservableCollection<Assembly>)GetValue(ReferenceAssembliesProperty); }
            set { SetValue(ReferenceAssembliesProperty, value); }
        }
        public static readonly DependencyProperty ReferenceAssembliesProperty =
            DependencyProperty.Register("ReferenceAssemblies", typeof(ObservableCollection<Assembly>), typeof(ScriptEditor), new PropertyMetadata(null, (d, e) => (d as ScriptEditor).OnReferenceAssembliesChanged()));

        public Object CurrentPopupContent
        {
            get { return (Object)GetValue(CurrentPopupContentProperty); }
            set { SetValue(CurrentPopupContentProperty, value); }
        }
        public static readonly DependencyProperty CurrentPopupContentProperty =
            DependencyProperty.Register("CurrentPopupContent", typeof(Object), typeof(ScriptEditor), new PropertyMetadata(null));

        public String Code
        {
            get { return (String)GetValue(CodeProperty); }
            set { SetValue(CodeProperty, value); }
        }
        public static readonly DependencyProperty CodeProperty =
            DependencyProperty.Register("Code", typeof(String), typeof(ScriptEditor), new PropertyMetadata(null, (d, e) => (d as ScriptEditor).OnCodeChanged()));

        public ObservableCollection<IScriptSource> AdditionalScripts
        {
            get { return (ObservableCollection<IScriptSource>)GetValue(AdditionalScriptsProperty); }
            set { SetValue(AdditionalScriptsProperty, value); }
        }
        public static readonly DependencyProperty AdditionalScriptsProperty =
            DependencyProperty.Register("AdditionalScripts", typeof(ObservableCollection<IScriptSource>), typeof(ScriptEditor), new PropertyMetadata(null));

        public Brush ColorizeBrush
        {
            get { return (Brush)GetValue(ColorizeBrushProperty); }
            set { SetValue(ColorizeBrushProperty, value); }
        }
        public static readonly DependencyProperty ColorizeBrushProperty =
            DependencyProperty.Register("ColorizeBrush", typeof(Brush), typeof(ScriptEditor), new PropertyMetadata(new SolidColorBrush(Colors.YellowGreen) { Opacity = 0.5 }));

        public Brush ErrorLineBrush
        {
            get { return (Brush)GetValue(ErrorLineBrushProperty); }
            set { SetValue(ErrorLineBrushProperty, value); }
        }
        public static readonly DependencyProperty ErrorLineBrushProperty =
            DependencyProperty.Register("ErrorLineBrush", typeof(Brush), typeof(ScriptEditor), new PropertyMetadata(new SolidColorBrush(Colors.Red) { Opacity = 0.5 }));

        public Brush DebugLineBrush
        {
            get { return (Brush)GetValue(DebugLineBrushProperty); }
            set { SetValue(DebugLineBrushProperty, value); }
        }
        public static readonly DependencyProperty DebugLineBrushProperty =
            DependencyProperty.Register("DebugLineBrush", typeof(Brush), typeof(ScriptEditor), new PropertyMetadata(new SolidColorBrush(Colors.Gray) { Opacity = 0.5 }));

        public Brush BreakPointLineBrush
        {
            get { return (Brush)GetValue(BreakPointLineBrushProperty); }
            set { SetValue(BreakPointLineBrushProperty, value); }
        }
        public static readonly DependencyProperty BreakPointLineBrushProperty =
            DependencyProperty.Register("BreakPointLineBrush", typeof(Brush), typeof(ScriptEditor), new PropertyMetadata(new SolidColorBrush(Colors.Yellow) { Opacity = 0.5 }));

        public IScriptSource ScriptSource
        {
            get { return (IScriptSource)GetValue(ScriptSourceProperty); }
            set { SetValue(ScriptSourceProperty, value); }
        }
        public static readonly DependencyProperty ScriptSourceProperty =
            DependencyProperty.Register("ScriptSource", typeof(IScriptSource), typeof(ScriptEditor), new PropertyMetadata(null));

        public bool DisableBreakPoints
        {
            get { return (bool)GetValue(DisableBreakPointsProperty); }
            set { SetValue(DisableBreakPointsProperty, value); }
        }
        public static readonly DependencyProperty DisableBreakPointsProperty =
            DependencyProperty.Register("DisableBreakPoints", typeof(bool), typeof(ScriptEditor), new PropertyMetadata(false));


        #endregion

        #region Constructors

        /// <summary>
        /// Initializes the <see cref="ScriptEditor"/> class.
        /// </summary>
        static ScriptEditor()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptEditor), new FrameworkPropertyMetadata(typeof(ScriptEditor)));

            snippets = new List<SnippetCompletionItem>();

            BlockedUsingsCache = new List<string>();

            if (KNOWN_TYPES_CACHE_FOLDER == null)
            {
                KNOWN_TYPES_CACHE_FOLDER = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "Scripting", "Cache");
                Directory.CreateDirectory(KNOWN_TYPES_CACHE_FOLDER);
            }

            _jsonSettings = new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.Auto,
                PreserveReferencesHandling = PreserveReferencesHandling.All
            };

            _knownTypesCache = new Dictionary<Type, KnownType>();
            _cachedAssemblies = new List<CachedAssembly>();
            _cachedUsings = new List<CachedUsing>();

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "for",
                Code = @"for (int i = 0; i < 10; i++)
        {
            
        }"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "while",
                Code = @"while (true)
        {
            
        }"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "foreach",
                Code = @"foreach (var item in items)
        {
            
        }"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "prop",
                Code = @"public int MyProperty { get; set; }"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "do",
                Code = @"do
        {
            
        } while (true)"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "try",
                Code = @"try
        {
                
        }
        catch (Exception ex)
        {
            
        }"
            });

            snippets.Add(new SnippetCompletionItem()
            {
                Name = "cw",
                Code = "context.WriteLine(\"\");"
            });
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ScriptEditor"/> class.
        /// </summary>
        public ScriptEditor()
        {
            _declaredTypes = new List<ScriptType>();

            _current_usings = new List<string>();

            //ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>();

            ////Add basic assemblies...
            //ReferenceAssemblies.Add(new ReferenceAssembly(typeof(String))); //mscorelib
            //ReferenceAssemblies.Add(new ReferenceAssembly(typeof(Enumerable))); //System.Core

            _knownTypes = new List<KnownType>();
            _parser = new ScriptParser();

            KnownTypesAvailable += ScriptEditor_KnownTypesAvailable;

            TextArea.IndentationStrategy = new Indentation.CSharp.CSharpIndentationStrategy(Options);
            foldingStrategy = new BraceFoldingStrategy();

            _update_timer = new DispatcherTimer();
            _update_timer.Interval = TimeSpan.FromSeconds(2);
            _update_timer.Tick += UpdateTimer_Tick;
            _update_timer.Start();

            IndentCommand = new RelayCommand(IndentCode);

            TextArea.TextEntered += TextArea_TextEntered;

            completionWindow = new CompletionWindow(TextArea);
            completionWindow.WindowStyle = WindowStyle.None;
            completionWindow.AllowsTransparency = true;
            completionWindow.ResizeMode = ResizeMode.NoResize;
            completionWindow.InsertionRequest += CompletionWindow_InsertionRequest;

            TextChanged += ScriptEditor_TextChanged;

            errorMarkerService = new TextMarkerService(Document);
            TextArea.TextView.BackgroundRenderers.Add(errorMarkerService);
            TextArea.TextView.LineTransformers.Add(errorMarkerService);

            Unloaded += ScriptEditor_Unloaded;

            breakPointMargin = new BreakPointMargin(this);
            _breakPointSymbols = new List<ScriptBreakPointSymbol>();
            Loaded += ScriptEditor_Loaded;

            MouseMove += ScriptEditor_MouseMove;
        }

        private void ScriptEditor_Loaded(object sender, RoutedEventArgs e)
        {
            TextArea.LeftMargins.Insert(0, breakPointMargin);
        }

        private void ScriptEditor_Unloaded(object sender, RoutedEventArgs e)
        {
            _update_timer.Stop();
        }

        private void ScriptEditor_KnownTypesAvailable(object sender, EventArgs e)
        {
            if (sender != this)
            {
                InvalidateHighlightingPartial();
            }
        }

        private bool preventCodeUpdate;
        private void ScriptEditor_TextChanged(object sender, EventArgs e)
        {
            if (!preventCodeUpdate)
            {
                preventCodeUpdate = true;
                Code = Text;
                preventCodeUpdate = false;
            }
        }

        private void OnCodeChanged()
        {
            if (!preventCodeUpdate)
            {
                preventCodeUpdate = true;
                Text = Code;
                preventCodeUpdate = false;
            }
        }

        #endregion

        #region Update Time Tick

        /// <summary>
        /// Handles the Tick event of the UpdateTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void UpdateTimer_Tick(object sender, EventArgs e)
        {
            InvalidateFolding();
            InvalidateUsings();
            InvalidateScriptTypesHighlightings();
        }

        #endregion

        #region Properties Changes

        private void OnEnableFoldingChanged()
        {
            if (EnableFolding)
            {
                foldingManager = FoldingManager.Install(TextArea);
            }
            else
            {
                FoldingManager.Uninstall(foldingManager);
            }
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// Invoked when an unhandled <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Windows.Input.KeyEventArgs" /> that contains the event data.</param>
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (CurrentPopupContent is MethodPopup && (e.Key == Key.Down || e.Key == Key.Up))
            {
                e.Handled = true;

                if (e.Key == Key.Down)
                {
                    (CurrentPopupContent as MethodPopup).IncrementMethod();
                }
                else if (e.Key == Key.Up)
                {
                    (CurrentPopupContent as MethodPopup).DecrementMethod();
                }

                return;
            }

            base.OnPreviewKeyDown(e);
            HidePopup();
            HandleKeyCombinations(e);
        }

        #endregion

        #region Apply Template

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _popup = GetTemplateChild("PART_popup") as Popup;
        }

        #endregion

        #region Key Combination Handling

        /// <summary>
        /// Handles the key combinations.
        /// </summary>
        /// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param>
        private void HandleKeyCombinations(KeyEventArgs e)
        {
            if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.K) && Keyboard.IsKeyDown(Key.D))
            {
                try
                {
                    int index = CaretOffset;
                    Document.BeginUpdate();
                    IndentCode();
                    Document.EndUpdate();
                    e.Handled = true;
                    CaretOffset = index;
                }
                catch
                {
                    Debug.WriteLine("Error indenting code.");
                }
            }
            else if (e.Key == Key.Oem2)
            {
                //int offset = CaretOffset;
                //var line = Document.GetLineByOffset(offset);

                //String text = GetCurrentLineText();
                //if (text.TrimStart('\t', ' ').StartsWith("//"))
                //{
                //    Document.BeginUpdate();
                //    Document.Replace(line, "/// <summary>\n/// \n/// </summary>");
                //    Document.EndUpdate();
                //    e.Handled = true;
                //    CaretOffset = Document.GetLineByNumber(line.LineNumber + 1).EndOffset;
                //}
            }
            else if (e.Key == Key.End || e.Key == Key.Home)
            {
                HideCompletionWindow();
            }
        }

        #endregion

        #region Text Entered

        private void TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            if (IsReadOnly) return;

            try
            {
                List<Object> items = new List<object>();

                HidePopup();

                var lineText = GetCurrentLineText();
                var previousWords = GetPreviousWords();
                var previousWordsLast = previousWords.LastOrDefault();
                String currentWord = previousWordsLast != null ? previousWordsLast.Replace("\t", "") : String.Empty;
                String currentWordIncludingParenthesis = currentWord.Split('(').LastOrDefault();

                if (previousWords.Count > 0 && previousWords.First().Trim().StartsWith("//")) return;

                if (e.Text == " " && previousWords.Count > 2 && previousWords[previousWords.Count - 2] == "=")
                {
                    var expression = previousWords.First();
                    var knownTypeResult = GetKnownTypeFromExpression(expression + ".");

                    if (knownTypeResult != null && knownTypeResult.KnownType.Type.IsEnum)
                    {
                        completionWindow.HideCompletion();
                        IList<ICompletionData> data = new List<ICompletionData>();

                        foreach (var field in knownTypeResult.KnownType.Fields)
                        {
                            data.Add(new FieldCompletionItem()
                            {
                                Class = knownTypeResult.KnownType.FriendlyName,
                                Name = knownTypeResult.KnownType.FriendlyName + "." + field.Name,
                                Type = field.ReturnTypeFriendlyName,
                                Description = field.Summary,
                            });
                        }

                        ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord());
                    }

                }
                else if (e.Text == " " && GetPreviousWord() == "new")
                {
                    var s = _parser.GetExpressionFirst<FieldDeclarationSyntax>(GetCurrentLineText());

                    if (s != null)
                    {
                        String type = s.Declaration.Type.ToString();

                        IList<ICompletionData> data = new List<ICompletionData>();

                        data.Add(new ClassCompletionItem()
                        {
                            Name = type,
                            Description = "Auto generate assignment...",
                        });

                        ShowCompletionWindow(data, type);
                    }
                }
                else if (e.Text == ";" || e.Text == " ")
                {
                    HideCompletionWindow();
                }
                else if (e.Text == ".")
                {
                    var knownTypeResult = GetCurrentKnownType();

                    IList<ICompletionData> data = new List<ICompletionData>();

                    if (knownTypeResult != null)
                    {
                        completionWindow.HideCompletion();

                        if (!knownTypeResult.KnownType.Type.IsEnum)
                        {
                            var typeMembers = knownTypeResult.KnownType.Members.ToList();

                            foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().Where(x => (!knownTypeResult.IsStatic && !x.IsStatic) || (knownTypeResult.IsStatic && x.IsStatic)).GroupBy(x => x.NameWithTypeArguments))
                            {
                                var method = methodGroup.First();

                                data.Add(new MethodCompletionItem()
                                {
                                    Class = knownTypeResult.KnownType.FriendlyName,
                                    Name = method.NameWithTypeArguments,
                                    ReturnType = method.ReturnTypeFriendlyName,
                                    Description = method.Summary,
                                    Parameters = method.Parameters,
                                    Overloads = methodGroup.Count() - 1,
                                });
                            }

                            foreach (var ev in typeMembers.OfType<KnownTypeEvent>())
                            {
                                data.Add(new EventCompletionItem()
                                {
                                    Class = knownTypeResult.KnownType.FriendlyName,
                                    Name = ev.Name,
                                    Description = ev.Summary,
                                });
                            }

                            foreach (var methodGroup in typeMembers.Where(x => x.GetType() != typeof(KnownTypeMethod) && x.GetType() != typeof(KnownTypeEvent)).GroupBy(x => x.Name))
                            {
                                var member = methodGroup.First();

                                data.Add(new PropertyCompletionItem()
                                {
                                    Class = knownTypeResult.KnownType.FriendlyName,
                                    Name = member.Name,
                                    Type = member.ReturnTypeFriendlyName,
                                    Description = member.Summary,
                                });
                            }
                        }
                        else
                        {
                            foreach (var field in knownTypeResult.KnownType.Fields)
                            {
                                data.Add(new FieldCompletionItem()
                                {
                                    Class = knownTypeResult.KnownType.FriendlyName,
                                    Name = field.Name,
                                    Type = field.ReturnTypeFriendlyName,
                                    Description = field.Summary,
                                });

                            }
                        }

                        ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord());
                    }
                    else
                    {
                        var declaredType = GetCurrentDeclaredType();

                        if (declaredType != null)
                        {
                            completionWindow.HideCompletion();

                            var typeMembers = declaredType.Symbols.ToList();

                            foreach (var methodGroup in typeMembers.GroupBy(x => x.Name))
                            {
                                var member = methodGroup.First();

                                if (member.Kind == SymbolKind.Method)
                                {
                                    var methodCompletion = new MethodCompletionItem()
                                    {
                                        Class = declaredType.Name,
                                        Name = member.Name,
                                        ReturnType = member.Type,
                                        Description = member.Summary,
                                        Overloads = methodGroup.Count() - 1,
                                    };


                                    for (int i = 0; i < member.Parameters.Count; i++)
                                    {
                                        var pair = member.Parameters[i];

                                        methodCompletion.Parameters.Add(new KnownTypeMethodParameter()
                                        {
                                            Type = pair.Key,
                                            Name = pair.Value,
                                            IsLast = (i == member.Parameters.Count - 1)
                                        });
                                    }

                                    data.Add(methodCompletion);

                                }
                                else if (member.Kind == SymbolKind.Property)
                                {
                                    data.Add(new PropertyCompletionItem()
                                    {
                                        Class = declaredType.Name,
                                        Name = member.Name,
                                        Type = member.Type,
                                        Description = member.Summary,
                                    });
                                }
                                else if (member.Kind == SymbolKind.Field)
                                {
                                    data.Add(new FieldCompletionItem()
                                    {
                                        Class = declaredType.Name,
                                        Name = member.Name,
                                        Type = member.Type,
                                        Description = member.Summary,
                                    });
                                }
                            }

                            ShowCompletionWindow(data, GetCurrentWord());
                        }
                    }
                }
                else if (e.Text == "(" || e.Text == ",")
                {
                    completionWindow.HideCompletion();

                    try
                    {
                        var session = GetConstructionSession();

                        if (session != null)
                        {
                            var content = CreateConstructionSessionPopupContent(session);
                            if (content.Methods.Count > 0)
                            {
                                ShowPopup(content);
                                return;
                            }
                        }

                        var methodSession = GetMethodSession();

                        if (methodSession != null)
                        {
                            var content = CreateMethodSessionPopupContent(methodSession);
                            if (content.Methods.Count > 0)
                            {
                                ShowPopup(content);
                                return;
                            }
                        }

                        var declaredMethodSession = GetDeclaredMethodSession();

                        if (declaredMethodSession != null)
                        {
                            var content = CreateDeclaredMethodSessionPopupContent(declaredMethodSession);
                            if (content.Methods.Count > 0)
                            {
                                ShowPopup(content);
                                return;
                            }
                        }

                        var staticMethodSession = GetStaticMethodSession();

                        if (staticMethodSession != null)
                        {
                            var content = CreateMethodSessionPopupContent(staticMethodSession);
                            if (content.Methods.Count > 0)
                            {
                                ShowPopup(content);
                                return;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                    }
                }
                else if (lineText.StartsWith("using") && e.Text != "\n")
                {
                    if (completionWindow.IsVisible)
                    {
                        completionWindow.UpdatePositionFix();
                        return;
                    }

                    IList<ICompletionData> data = new List<ICompletionData>();

                    foreach (var asm in ReferenceAssemblies)
                    {
                        foreach (var ns in asm.GetTypes().Select(x => x.Namespace).Distinct().Where(x => x != null))
                        {
                            data.Add(new NamespaceCompletionItem()
                            {
                                Name = ns,
                                Assembly = asm.GetName().Name,
                            });
                        }
                    }

                    data = data.DistinctBy(x => x.Text).ToList();

                    ShowCompletionWindow(data, GetCurrentWord());
                }
                else if (e.Text == "{")
                {
                    int parentesisCount = lineText.TakeWhile(x => x != '{').Count(x => x == '\"');

                    if (parentesisCount % 2 == 0)
                    {
                        Document.Insert(CaretOffset, "}");
                        CaretOffset--;
                    }
                }
                else if (e.Text == "}")
                {
                    if (Document.GetText(CaretOffset - 2, 1) == "{" && Document.GetText(CaretOffset, 1) == "}")
                    {
                        Document.Replace(CaretOffset, 1, "");
                    }
                }
                else if (e.Text == "\n")
                {
                    if (Document.GetText(CaretOffset - 3, 1) == "{" && Document.GetText(CaretOffset, 1) == "}")
                    {
                        CaretOffset--;
                        Document.Insert(CaretOffset, "\n\t");
                    }
                }
                else if (!currentWordIncludingParenthesis.Contains(".") || currentWord[currentWord.Length - 2] == '<')
                {
                    if (completionWindow.IsVisible)
                    {
                        completionWindow.UpdatePositionFix();
                        return;
                    }

                    var previous_word = GetPreviousWord();
                    var word = GetCurrentWord();

                    if (word.Contains("<"))
                    {
                        word = word.Last(x => x != '<').ToString();
                    }

                    if (previous_word != word)
                    {
                        if (_knownTypes.Exists(x => x.Name == previous_word))
                        {
                            return;
                        }

                        if (_blocking_type_words.Contains(previous_word))
                        {
                            return;
                        }
                    }

                    if (!String.IsNullOrWhiteSpace(word))
                    {
                        IList<ICompletionData> data = new List<ICompletionData>();

                        foreach (var snippet in snippets)
                        {
                            data.Add(snippet);
                        }

                        foreach (var type in _declaredTypes.Where(x => x.Name.StartsWith(word)))
                        {
                            if (type.Kind == TypeKind.Struct)
                            {
                                data.Add(new StructCompletionItem()
                                {
                                    Name = type.Name,
                                    Description = type.Summary,
                                    Namespace = type.ContainingNamespace,
                                    Priority = 1,
                                });
                            }
                            else if (type.Kind == TypeKind.Enum)
                            {
                                data.Add(new EnumCompletionItem()
                                {
                                    Name = type.Name,
                                    Description = type.Summary,
                                    Namespace = type.ContainingNamespace,
                                    Priority = 1,
                                });
                            }
                            else if (type.Kind == TypeKind.Interface)
                            {
                                data.Add(new InterfaceCompletionItem()
                                {
                                    Name = type.Name,
                                    Description = type.Summary,
                                    Namespace = type.ContainingNamespace,
                                    Priority = 1,
                                });
                            }
                            else if (type.Kind == TypeKind.Class)
                            {
                                data.Add(new ClassCompletionItem()
                                {
                                    Name = type.Name,
                                    Description = type.Summary,
                                    Namespace = type.ContainingNamespace,
                                    Priority = 1,
                                });
                            }
                            else
                            {
                                throw new NotImplementedException("Implement generic item here!");
                            }
                        }

                        foreach (var type in _knownTypes.ToList().Where(x => x.Name.StartsWith(word)))
                        {
                            if (type.Type.IsEnum)
                            {
                                data.Add(new EnumCompletionItem()
                                {
                                    Namespace = type.Type.Namespace,
                                    Description = type.Summary,
                                    Name = type.FriendlyName,
                                    Priority = 0,
                                });
                            }
                            else if (type.Type.IsInterface)
                            {
                                data.Add(new InterfaceCompletionItem()
                                {
                                    Name = type.FriendlyName,
                                    Description = type.Summary,
                                    Namespace = type.Type.Namespace,
                                    Priority = 0,
                                });
                            }
                            else if (type.Type.IsValueType)
                            {
                                data.Add(new StructCompletionItem()
                                {
                                    Name = type.FriendlyName,
                                    Description = type.Summary,
                                    Namespace = type.Type.Namespace,
                                    Priority = 0,
                                });
                            }
                            else if (type.Type.IsClass)
                            {
                                data.Add(new ClassCompletionItem()
                                {
                                    Name = type.FriendlyName,
                                    Description = type.Summary,
                                    Namespace = type.Type.Namespace,
                                    Priority = 0,
                                });
                            }
                            else
                            {
                                throw new NotImplementedException("Implement generic item here.");
                            }
                        }

                        foreach (var symbol in _parser.GetContextSymbols(Document.Text, CaretOffset).Where(x => x.Name.StartsWith(GetCurrentWord())))
                        {
                            if (symbol.Kind == SymbolKind.Property)
                            {
                                data.Add(new PropertyCompletionItem()
                                {
                                    Class = symbol.Class,
                                    Description = symbol.Summary,
                                    Name = symbol.Name,
                                    Type = symbol.Type,
                                    Priority = 2,
                                });
                            }
                            else if (symbol.Kind == SymbolKind.Field || symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
                            {
                                data.Add(new FieldCompletionItem()
                                {
                                    Class = symbol.Class,
                                    Description = symbol.Summary,
                                    Name = symbol.Name,
                                    Type = symbol.Type,
                                    Priority = 2,
                                });
                            }
                            else if (symbol.Kind == SymbolKind.Method)
                            {
                                var methodCompletion = new MethodCompletionItem()
                                {
                                    Class = symbol.Class,
                                    Description = symbol.Summary,
                                    Name = symbol.Name,
                                    ReturnType = symbol.Type,
                                    Priority = 2,
                                };

                                for (int i = 0; i < symbol.Parameters.Count; i++)
                                {
                                    var pair = symbol.Parameters[i];

                                    methodCompletion.Parameters.Add(new KnownTypeMethodParameter()
                                    {
                                        Type = pair.Key,
                                        Name = pair.Value,
                                        IsLast = (i == symbol.Parameters.Count - 1)
                                    });
                                }

                                data.Add(methodCompletion);
                            }
                        }

                        ShowCompletionWindow(data, word);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
        }

        #endregion

        #region Completion Window Insertion

        private void CompletionWindow_InsertionRequest(ICompletionData item)
        {
            item.Complete(this);
        }

        #endregion

        #region Private/Internal Methods

        private void ShowCompletionWindow(IList<ICompletionData> suggestions, String filter)
        {
            IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
            data.Clear();

            foreach (var item in suggestions.DistinctBy(x => x.Text))
            {
                data.Add(item);
            }

            if (data.Count > 0)
            {
                completionWindow.ShowCompletion();

                if (completionWindow.CompletionList.ListBox != null)
                {
                    completionWindow.CompletionList.SelectItemFiltering(filter);
                }
            }
            else
            {
                completionWindow.HideCompletion();
            }
        }

        private void HideCompletionWindow()
        {
            completionWindow.HideCompletion();
        }

        private List<String> GetScriptClassNames()
        {
            List<String> list = new List<String>();

            Regex r = new Regex(@"(class\s+)(\w+)");
            var matches = r.Matches(Text);

            foreach (var m in matches.OfType<Match>())
            {
                var g = m.Groups.OfType<Group>().Last();
                list.Add(g.Value);
            }

            return list.Distinct().ToList();
        }

        private List<String> GetScriptInterfaceOfEnumNames()
        {
            List<String> list = new List<String>();

            Regex r = new Regex(@"((enum|interface)\s+)(\w+)");
            var matches = r.Matches(Text);

            foreach (var m in matches.OfType<Match>())
            {
                var g = m.Groups.OfType<Group>().Last();
                list.Add(g.Value);
            }

            return list.Distinct().ToList();
        }

        private List<String> GetScriptEnumsAndInterfaces()
        {
            List<String> list = new List<string>();

            Regex r = new Regex(@"((public|private|internal)\s+(enum|interface)\s+\w+)");
            var matches = r.Matches(Text);

            foreach (var m in matches)
            {

            }

            return list;
        }

        private KnownTypeResult GetCurrentKnownType()
        {
            var expression = GetPreviousWords().LastOrDefault();
            return GetKnownTypeFromExpression(expression);
        }

        private KnownTypeResult GetKnownTypeFromExpression(String expression)
        {
            if (expression != null)
            {
                var insideMethodExp = expression.Split('(').LastOrDefault();

                if (insideMethodExp != null)
                {
                    expression = insideMethodExp;
                }

                var tree = expression.Split('.').Select(x => x.Remove(@"\n|\t|\r|\(.*\)|\[.*\]|\s")).ToList();
                var variableName = tree.FirstOrDefault();

                if (variableName != null)
                {
                    //Search for enum type first
                    var enumType = _knownTypes.FirstOrDefault(x => x.Name == variableName && x.Type.IsEnum);

                    if (enumType != null)
                    {
                        return new KnownTypeResult(enumType);
                    }

                    tree.RemoveAt(0);
                    var variables = _parser.GetContextSymbols(Document.Text, CaretOffset);
                    var variable = variables.FirstOrDefault(x => x.Name == variableName);

                    if (variable != null)
                    {
                        var name = Regex.Replace(variable.Type, "<.+>", "<T>");
                        var knownType = _knownTypes.FirstOrDefault(x => name == x.FriendlyName || name == x.Alias);

                        if (knownType != null)
                        {
                            while (tree.Count > 1)
                            {
                                var memberName = tree.First();
                                tree.RemoveAt(0);
                                var member = knownType.Members.FirstOrDefault(x => x.Name == memberName);

                                if (member == null)
                                {
                                    return null;
                                }

                                knownType = _knownTypes.FirstOrDefault(x => x.Type.Namespace + "." + x.Type.Name == member.ReturnType.Namespace + "." + member.ReturnType.Name);
                            }

                            return new KnownTypeResult(knownType);
                        }
                        else //Maybe a variable of a declared type...
                        {
                            if (tree.Count > 0)
                            {
                                var memberName = tree[0];
                                var declaredType = _declaredTypes.SingleOrDefault(x => x.Name == name);

                                if (declaredType != null)
                                {
                                    var member = declaredType.Symbols.FirstOrDefault(x => x.Name == memberName);
                                    if (member != null)
                                    {
                                        knownType = _knownTypes.SingleOrDefault(x => x.Name.ToLower() == member.Type.ToLower());

                                        if (knownType != null)
                                        {
                                            return new KnownTypeResult(knownType);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        //Maybe static...
                        var type = _knownTypes.FirstOrDefault(x => x.Name == variableName || x.Alias == variableName);
                        return type != null ? new KnownTypeResult(type) { IsStatic = true } : null;
                    }
                }
            }

            return null;
        }

        private ScriptType GetCurrentDeclaredType()
        {
            var expression = GetPreviousWords().LastOrDefault();

            if (expression != null)
            {
                var insideMethodExp = expression.Split('(').LastOrDefault();

                if (insideMethodExp != null)
                {
                    expression = insideMethodExp;
                }

                var tree = expression.Split('.').Select(x => x.Remove(@"\n|\t|\r|\(.*\)|\[.*\]|\s")).ToList();
                var variableName = tree.FirstOrDefault();

                if (variableName != null)
                {
                    tree.RemoveAt(0);
                    var variables = _parser.GetContextSymbols(Document.Text, CaretOffset);
                    var variable = variables.FirstOrDefault(x => x.Name == variableName);

                    if (variable != null)
                    {
                        var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Type, "<.+>", "<T>"));

                        if (declaredType != null)
                        {
                            while (tree.Count > 1)
                            {
                                var memberName = tree.First();
                                tree.RemoveAt(0);
                                var member = declaredType.Symbols.FirstOrDefault(x => x.Name == memberName);

                                if (member == null)
                                {
                                    return null;
                                }

                                declaredType = _declaredTypes.FirstOrDefault(x => x.ContainingNamespace + "." + x.Name == member.ContainingNamespace + "." + member.Type);
                            }

                            return declaredType;
                        }
                    }
                    else
                    {
                        //Maybe static...
                        return _declaredTypes.FirstOrDefault(x => x.Name == variableName);
                    }
                }
            }

            return null;
        }

        private void ShowPopup(Object content)
        {
            var position = TextArea.Caret.Position;
            var textView = TextArea.TextView;

            var visualLocation = textView.GetVisualPosition(position, VisualYPosition.LineBottom);
            var visualLocationTop = textView.GetVisualPosition(position, VisualYPosition.LineTop);

            Point location = textView.PointToScreen(visualLocation - textView.ScrollOffset);
            Point locationTop = textView.PointToScreen(visualLocationTop - textView.ScrollOffset);

            _popup.Placement = PlacementMode.Absolute;
            _popup.PlacementRectangle = new Rect(location, new Size(200, 100));

            CurrentPopupContent = content;

            _popup.IsOpen = true;
        }

        private void HidePopup()
        {
            _popup.IsOpen = false;
            CurrentPopupContent = null;
        }

        private MethodPopup CreateConstructionSessionPopupContent(ConstructionSession session)
        {
            MethodPopup popup = new MethodPopup();

            foreach (var c in session.Type.Constructors)
            {
                MethodDescription method = new MethodDescription();
                method.ReturnType = session.Type.Name;
                method.Description = c.Summary;

                if (session.Type.Type.IsGenericType && session.TypeArguments != null)
                {
                    method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>";
                }

                var parameters = c.Parameters;

                foreach (var p in parameters)
                {
                    ParameterDescription pDescription = new ParameterDescription(method);
                    pDescription.Name = p.Name;
                    pDescription.Type = p.Type;
                    pDescription.Description = p.Description;
                    method.Parameters.Add(pDescription);
                }

                popup.Methods.Add(method);
            }

            if (session.ParameterIndex > 0)
            {
                popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1);

                if (popup.CurrentMethod == null)
                {
                    popup.CurrentMethod = popup.Methods.FirstOrDefault();
                }
            }
            else
            {
                popup.CurrentMethod = popup.Methods.FirstOrDefault();
            }

            if (popup.CurrentMethod != null)
            {
                popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1;
            }

            return popup;
        }

        private MethodPopup CreateMethodSessionPopupContent(MethodSession session)
        {
            MethodPopup popup = new MethodPopup();

            foreach (var m in session.Type.Methods.Where(x => x.Name == session.MethodName))
            {
                MethodDescription method = new MethodDescription();
                method.ReturnType = m.ReturnTypeFriendlyName;
                method.Description = m.Summary;
                method.Name = m.NameWithTypeArguments;
                method.Class = session.Type.FriendlyName;

                //if (session.Type.Type.IsGenericType && session.TypeArguments != null)
                //{
                //    method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>";
                //}

                var parameters = m.Parameters;

                foreach (var p in parameters)
                {
                    ParameterDescription pDescription = new ParameterDescription(method);
                    pDescription.Name = p.Name;
                    pDescription.Type = p.Type;
                    pDescription.Description = p.Description;
                    method.Parameters.Add(pDescription);
                }

                popup.Methods.Add(method);
            }

            if (session.ParameterIndex > 0)
            {
                popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1);

                if (popup.CurrentMethod == null)
                {
                    popup.CurrentMethod = popup.Methods.FirstOrDefault();
                }
            }
            else
            {
                popup.CurrentMethod = popup.Methods.FirstOrDefault();
            }

            if (popup.CurrentMethod != null)
            {
                popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1;
            }

            return popup;
        }

        private MethodPopup CreateDeclaredMethodSessionPopupContent(DeclaredMethodSession session)
        {
            MethodPopup popup = new MethodPopup();

            MethodDescription method = new MethodDescription();
            method.ReturnType = session.Method.Type;
            method.Description = session.Method.Summary;
            method.Name = session.Method.Name;
            method.Class = session.Method.Class;

            //if (session.Type.Type.IsGenericType && session.TypeArguments != null)
            //{
            //    method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>";
            //}

            foreach (var p in session.Method.Parameters)
            {
                ParameterDescription pDescription = new ParameterDescription(method);
                pDescription.Type = p.Key;
                pDescription.Name = p.Value;
                method.Parameters.Add(pDescription);
            }

            popup.Methods.Add(method);

            //if (false)
            //{
            //    popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1);

            //    if (popup.CurrentMethod == null)
            //    {
            //        popup.CurrentMethod = popup.Methods.FirstOrDefault();
            //    }
            //}
            //else
            //{
            popup.CurrentMethod = popup.Methods.FirstOrDefault();
            //}

            if (popup.CurrentMethod != null)
            {
                popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1;
            }

            return popup;
        }

        public void LoadUsingsSymbols(List<Assembly> assemblies, List<String> usings)
        {
            lock (_loadUsingsLock)
            {
                LoadingSymbolsStarted?.Invoke(null, new EventArgs());

                var allTypes = assemblies.SelectMany(x => x.GetTypes());

                foreach (var use in usings)
                {
                    if (!_cachedUsings.Exists(x => x.Namespace == use))
                    {
                        if (!_isUsingsLoadingStarted)
                        {
                            _isUsingsLoadingStarted = true;
                            UsingsLoadingStarted?.Invoke(this, new EventArgs());
                        }

                        var useFileName = System.IO.Path.Combine(KNOWN_TYPES_CACHE_FOLDER, use + ".json");

                        if (File.Exists(useFileName))
                        {
                            LoadingSymbolsProgress?.Invoke(null, new TangoProgressChangedEventArgs<int>()
                            {
                                Progress = new TangoProgress<int>()
                                {
                                    IsIndeterminate = true,
                                    Maximum = 100,
                                    Message = $"Loading symbols for '{use}'..."
                                }
                            });

                            CachedUsing cached = JsonConvert.DeserializeObject<CachedUsing>(File.ReadAllText(useFileName), _jsonSettings);
                            _cachedUsings.Add(cached);
                            foreach (var knownType in cached.KnownTypes)
                            {
                                _knownTypesCache.Add(knownType.Type, knownType);
                            }

                            KnownTypesAvailable?.Invoke(this, new EventArgs());
                            InvalidateHighlightingPartial();

                            continue;
                        }

                        var useTypes = allTypes.Where(x => x.IsVisible && x.IsPublic && x.Namespace == use).ToList();

                        CachedUsing cachedUsing = new CachedUsing();
                        cachedUsing.Namespace = use;
                        _cachedUsings.Add(cachedUsing);

                        int i = 1;

                        foreach (var type in useTypes)
                        {
                            LoadingSymbolsProgress?.Invoke(null, new TangoProgressChangedEventArgs<int>()
                            {
                                Progress = new TangoProgress<int>()
                                {
                                    IsIndeterminate = false,
                                    Maximum = useTypes.Count,
                                    Value = i++,
                                    Message = $"Loading symbols for '{use}'..."
                                }
                            });

                            KnownType knownType = new KnownType(type);

                            if (type.IsPrimitive)
                            {
                                if (type == typeof(Int32))
                                {
                                    knownType.Alias = "int";
                                }
                                else if (type == typeof(float))
                                {
                                    knownType.Alias = "float";
                                }
                                else if (type == typeof(Double))
                                {
                                    knownType.Alias = "double";
                                }
                                else if (type == typeof(long))
                                {
                                    knownType.Alias = "long";
                                }
                                else if (type == typeof(bool))
                                {
                                    knownType.Alias = "bool";
                                }
                                else if (type == typeof(uint))
                                {
                                    knownType.Alias = "uint";
                                }
                            }
                            else if (type == typeof(String))
                            {
                                knownType.Alias = "string";
                            }

                            _knownTypesCache.Add(type, knownType);
                            cachedUsing.KnownTypes.Add(knownType);
                            knownType.LoadDocumentation();
                        }

                        if (!BlockedUsingsCache.Exists(x => use.StartsWith(x)))
                        {
                            Task.Factory.StartNew(() =>
                            {
                                var json = JsonConvert.SerializeObject(cachedUsing, _jsonSettings);
                                File.WriteAllText(useFileName, json);
                            });
                        }
                    }
                }

                if (_isUsingsLoadingStarted)
                {
                    UsingsLoadingCompleted?.Invoke(this, new EventArgs());
                }

                LoadingSymbolsCompleted?.Invoke(null, new EventArgs());
            }
        }

        //public static void LoadCachedAssemblies(List<Assembly> assemblies, List<String> usings = null)
        //{
        //    if (_isLoadingCachedAssemblies) return;

        //    _isLoadingCachedAssemblies = true;

        //    LoadingSymbolsStarted?.Invoke(null, new EventArgs());

        //    if (!_isCacheAssembliesLoaded)
        //    {
        //        foreach (var file in System.IO.Directory.GetFiles(KNOWN_TYPES_CACHE_FOLDER))
        //        {
        //            try
        //            {
        //                LoadingSymbolsProgress?.Invoke(null, new TangoProgressChangedEventArgs<int>()
        //                {
        //                    Progress = new TangoProgress<int>()
        //                    {
        //                        IsIndeterminate = true,
        //                        Maximum = 100,
        //                        Message = $"Loading metadata cache for '{System.IO.Path.GetFileName(file)}'..."
        //                    }
        //                });

        //                var cachedAssembly = JsonConvert.DeserializeObject<CachedAssembly>(System.IO.File.ReadAllText(file), _jsonSettings);

        //                foreach (var knownType in cachedAssembly.KnownTypes)
        //                {
        //                    _knownTypesCache.Add(knownType.Type, knownType);
        //                }

        //                _cachedAssemblies.Add(cachedAssembly);
        //            }
        //            catch { }
        //        }

        //        _isCacheAssembliesLoaded = true;
        //    }

        //    foreach (var asm in assemblies)
        //    {
        //        if (!_cachedAssemblies.Exists(x => x.Name == asm.FullName))
        //        {
        //            String asmFileName = System.IO.Path.GetFileName(asm.Location);

        //            CachedAssembly cachedAssembly = new CachedAssembly();
        //            cachedAssembly.Name = asm.FullName;
        //            _cachedAssemblies.Add(cachedAssembly);

        //            var types = asm.GetTypes().Where(x => x.IsVisible && x.IsPublic).ToList();

        //            int i = 0;

        //            foreach (var type in types)
        //            {
        //                LoadingSymbolsProgress?.Invoke(null, new TangoProgressChangedEventArgs<int>()
        //                {
        //                    Progress = new TangoProgress<int>()
        //                    {
        //                        IsIndeterminate = false,
        //                        Maximum = types.Count,
        //                        Value = i++,
        //                        Message = $"Caching metadata for '{asmFileName}'..."
        //                    }
        //                });

        //                KnownType knownType = new KnownType(type);

        //                if (type.IsPrimitive)
        //                {
        //                    if (type == typeof(Int32))
        //                    {
        //                        knownType.Alias = "int";
        //                    }
        //                    else if (type == typeof(float))
        //                    {
        //                        knownType.Alias = "float";
        //                    }
        //                    else if (type == typeof(Double))
        //                    {
        //                        knownType.Alias = "double";
        //                    }
        //                    else if (type == typeof(long))
        //                    {
        //                        knownType.Alias = "long";
        //                    }
        //                    else if (type == typeof(bool))
        //                    {
        //                        knownType.Alias = "bool";
        //                    }
        //                    else if (type == typeof(uint))
        //                    {
        //                        knownType.Alias = "uint";
        //                    }
        //                }

        //                _knownTypesCache.Add(type, knownType);
        //                cachedAssembly.KnownTypes.Add(knownType);
        //                //knownType.LoadDocumentation();
        //            }

        //            String cachedAssemblyFile = System.IO.Path.Combine(KNOWN_TYPES_CACHE_FOLDER, asmFileName);
        //            File.WriteAllText(cachedAssemblyFile, JsonConvert.SerializeObject(cachedAssembly, _jsonSettings));
        //        }
        //    }

        //    LoadingSymbolsCompleted?.Invoke(null, new EventArgs());

        //    _isLoadingCachedAssemblies = false;
        //}

        private void InvalidateHighlightingPartial()
        {
            List<Assembly> assemblies = new List<Assembly>();
            Dispatcher.Invoke(() =>
            {
                assemblies = ReferenceAssemblies.ToList();
            });

            var usings = _current_usings.ToList();

            _knownTypes.Clear();

            foreach (var knownType in _knownTypesCache.ToList().Select(x => x.Value).ToList())
            {
                if (usings.Exists(x => knownType.Type.Namespace == x) && assemblies.Exists(x => x == knownType.Type.Assembly))
                {
                    lock (_knownTypes)
                    {
                        _knownTypes.Add(knownType);
                    }
                }
            }

            if (_knownTypes.Count > 0 || _declaredTypes.Count > 0)
            {
                String text = String.Empty;

                Stream xshd_stream = typeof(ScriptEditor).Assembly.GetManifestResourceStream("Tango.Scripting.Editors.Highlighting.Resources.CSharp-Mode.xshd");

                using (StreamReader reader = new StreamReader(xshd_stream))
                {
                    text = reader.ReadToEnd();
                }

                List<String> referenceTypes = new List<string>();
                List<String> interfaceTypes = new List<string>();

                lock (_knownTypes)
                {
                    foreach (var type in _knownTypes.ToList().Where(x => x != null))
                    {
                        String name = type.Name;

                        if (type.Type.ContainsGenericParameters)
                        {
                            name = new String(name.TakeWhile(x => x != '`').ToArray());
                        }

                        if (type.Type.IsInterface || type.Type.IsEnum)
                        {
                            interfaceTypes.Add(String.Format("<Word>{0}</Word>", name));
                        }
                        else if (type.Type.IsClass || (type.Type.IsValueType))
                        {
                            referenceTypes.Add(String.Format("<Word>{0}</Word>", name));
                        }
                    }
                }

                foreach (var type in _declaredTypes)
                {
                    if (type.Kind == TypeKind.Interface || type.Kind == TypeKind.Enum)
                    {
                        interfaceTypes.Add(String.Format("<Word>{0}</Word>", type.Name));
                    }
                    else if (type.Kind == TypeKind.Class)
                    {
                        referenceTypes.Add(String.Format("<Word>{0}</Word>", type.Name));
                    }
                }

                if (referenceTypes.Count > 0)
                {
                    text = text.Replace("<Word>@ReferenceTypes@</Word>", String.Join(Environment.NewLine, referenceTypes.Distinct()));
                }

                if (interfaceTypes.Count > 0)
                {
                    text = text.Replace("<Word>@InterfaceTypes@</Word>", String.Join(Environment.NewLine, interfaceTypes.Distinct()));
                }

                MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(text));

                XmlTextReader xshd_reader = new XmlTextReader(ms);

                Dispatcher.Invoke(new Action(() =>
                {
                    SyntaxHighlighting = HighlightingLoader.Load(xshd_reader, HighlightingManager.Instance);
                    xshd_reader.Close();
                    ms.Dispose();
                }));
            }
        }

        public void InvalidateHighlighting(bool loadKnownTypes = true)
        {
            if (!_isLoadingTypes)
            {
                _isLoadingTypes = true;

                var assemblies = ReferenceAssemblies.ToList();
                KnownType.ExtensionMethodsAssemblies = assemblies.ToList();
                var usings = _current_usings.ToList();

                Thread t = new Thread(() =>
                {
                    LoadUsingsSymbols(assemblies, usings);

                    if (loadKnownTypes)
                    {
                        _knownTypes.Clear();

                        foreach (var knownType in _knownTypesCache.ToList().Select(x => x.Value).ToList())
                        {
                            if (usings.Exists(x => knownType.Type.Namespace == x) && assemblies.Exists(x => x == knownType.Type.Assembly))
                            {
                                lock (_knownTypes)
                                {
                                    _knownTypes.Add(knownType);
                                }
                            }
                        }
                    }

                    if (_knownTypes.Count > 0 || _declaredTypes.Count > 0)
                    {
                        String text = String.Empty;

                        Stream xshd_stream = typeof(ScriptEditor).Assembly.GetManifestResourceStream("Tango.Scripting.Editors.Highlighting.Resources.CSharp-Mode.xshd");

                        using (StreamReader reader = new StreamReader(xshd_stream))
                        {
                            text = reader.ReadToEnd();
                        }

                        List<String> referenceTypes = new List<string>();
                        List<String> interfaceTypes = new List<string>();

                        lock (_knownTypes)
                        {
                            foreach (var type in _knownTypes.ToList().Where(x => x != null))
                            {
                                String name = type.Name;

                                if (type.Type.ContainsGenericParameters)
                                {
                                    name = new String(name.TakeWhile(x => x != '`').ToArray());
                                }

                                if (type.Type.IsInterface || type.Type.IsEnum)
                                {
                                    interfaceTypes.Add(String.Format("<Word>{0}</Word>", name));
                                }
                                else if (type.Type.IsClass || (type.Type.IsValueType))
                                {
                                    referenceTypes.Add(String.Format("<Word>{0}</Word>", name));
                                }
                            }
                        }

                        foreach (var type in _declaredTypes)
                        {
                            if (type.Kind == TypeKind.Interface || type.Kind == TypeKind.Enum)
                            {
                                interfaceTypes.Add(String.Format("<Word>{0}</Word>", type.Name));
                            }
                            else if (type.Kind == TypeKind.Class)
                            {
                                referenceTypes.Add(String.Format("<Word>{0}</Word>", type.Name));
                            }
                        }

                        if (referenceTypes.Count > 0)
                        {
                            text = text.Replace("<Word>@ReferenceTypes@</Word>", String.Join(Environment.NewLine, referenceTypes.Distinct()));
                        }

                        if (interfaceTypes.Count > 0)
                        {
                            text = text.Replace("<Word>@InterfaceTypes@</Word>", String.Join(Environment.NewLine, interfaceTypes.Distinct()));
                        }

                        MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(text));

                        XmlTextReader xshd_reader = new XmlTextReader(ms);

                        Dispatcher.BeginInvoke(new Action(() =>
                        {
                            SyntaxHighlighting = HighlightingLoader.Load(xshd_reader, HighlightingManager.Instance);
                            xshd_reader.Close();
                            ms.Dispose();
                        }));


                        //foreach (var knownType in _knownTypes)
                        //{
                        //    knownType.LoadDocumentation();
                        //}
                    }

                    _isLoadingTypes = false;
                });
                t.IsBackground = true;
                t.Start();
            }
        }

        private void InvalidateScriptTypesHighlightings()
        {
            var declaredTypes = _parser.GetDeclaredTypes(Text);

            if (AdditionalScripts != null)
            {
                foreach (var script in AdditionalScripts)
                {
                    declaredTypes.AddRange(_parser.GetDeclaredTypes(script.Code));
                }
            }


            if (declaredTypes.Exists(x => !_declaredTypes.Exists(y => y.Name == x.Name)) || _declaredTypes.Exists(x => !declaredTypes.Exists(y => y.Name == x.Name)))
            {
                _declaredTypes = declaredTypes;
                InvalidateHighlighting(false);
            }

            _declaredTypes = declaredTypes;

            //for (int i = 0; i < TextArea.TextView.LineTransformers.Count; i++)
            //{
            //    if (TextArea.TextView.LineTransformers[i] is OffsetColorizer)
            //    {
            //        TextArea.TextView.LineTransformers.RemoveAt(i);
            //    }
            //}

            //foreach (var cls in scriptClasses)
            //{
            //    Document.BeginUpdate();

            //    var line = Document.GetLineByOffset(cls.Index);

            //    OffsetColorizer colorizer = new OffsetColorizer(line, cls.Index, cls.Index + cls.Name.Length, Brushes.Red);
            //    TextArea.TextView.LineTransformers.Add(colorizer);

            //    Document.EndUpdate();
            //}
        }

        private void InvalidateUsings()
        {
            var oldUsings = _current_usings.ToList();
            _current_usings = _parser.GetUsings(Text);

            if (_current_usings.Exists(x => !oldUsings.Exists(y => y == x)) || oldUsings.Exists(x => !_current_usings.Exists(y => y == x)))
            {
                InvalidateHighlighting();
            }
        }

        private void InvalidateFolding()
        {
            if (EnableFolding)
            {
                if (foldingManager == null)
                {
                    foldingManager = FoldingManager.Install(TextArea);
                }

                foldingStrategy.UpdateFoldings(foldingManager, Document);
            }
        }

        private void IndentCode()
        {
            Text = CodeFormatter.Format(Text);
            //Text = Indentation.CSharp.CSharpIndentationHelper.IndentCSharpCode(Text);
            //Text = _parser.IndentCSharpCode(Text);
        }

        internal DocumentLine GetCurrentLine()
        {
            int offset = CaretOffset;
            var line = Document.GetLineByOffset(offset);
            return line;
        }

        private String GetCurrentLineText()
        {
            var text = Document.GetText(GetCurrentLine());
            return text;
        }

        internal String GetCurrentWord()
        {
            return GetWordByEndIndex(CaretOffset);
        }

        private String GetWordByEndIndex(int index)
        {
            String word = String.Empty;
            var line = GetCurrentLine();

            int position = index;

            for (int i = position - 1; i >= line.Offset; i--)
            {
                char c = Document.GetText(i, 1).First();

                if (word_separators.Contains(c))
                {
                    break;
                }

                word += c;
            }

            word = new string(word.Reverse().ToArray());

            if (word.Length > 0)
            {
                word = word.Replace(".", "");
            }

            return word;
        }

        internal int GetCurrentWordStartIndex()
        {
            var line = GetCurrentLine();

            int position = CaretOffset;

            for (int i = position - 1; i >= line.Offset; i--)
            {
                char c = Document.GetText(i, 1).First();

                if (word_separators.Contains(c))
                {
                    return i + 1;
                }
            }

            return line.Offset;
        }

        private String GetPreviousWord()
        {
            int index = GetCurrentWordStartIndex() - 1;
            return GetWordByEndIndex(index);
        }

        private ConstructionSession GetConstructionSession()
        {
            var currentLine = GetCurrentLineText();

            //if (currentLine.Count(x => x == '(') > 1) return null;

            var expression = _parser.GetCurrentConstructionExpression(currentLine);

            if (expression != null)
            {
                ConstructionSession session = new ConstructionSession();

                var line = GetCurrentLine();
                int parameterIndex = 0;
                for (int i = CaretOffset; i > line.Offset; i--)
                {
                    String c = Document.GetText(i, 1);

                    if (c == "(")
                    {
                        var typeDeclaration = expression.Type as GenericNameSyntax;

                        KnownType type = null;

                        if (typeDeclaration != null)
                        {
                            var typeName = typeDeclaration.Identifier.ToString();
                            var argumentsCount = typeDeclaration.TypeArgumentList.Arguments.Count;
                            session.TypeArguments = typeDeclaration.TypeArgumentList.Arguments.Select(x => x.ToString()).ToList();

                            if (argumentsCount == 0)
                            {
                                type = _knownTypes.FirstOrDefault(x => x.Type.Name == expression.Type.ToString());
                            }
                            else
                            {
                                type = _knownTypes.FirstOrDefault(x => x.Type.Name == typeName + "`" + argumentsCount);
                            }
                        }
                        else
                        {
                            type = _knownTypes.FirstOrDefault(x => x.Name == expression.Type.ToString());
                        }

                        if (type != null)
                        {
                            session.Type = type;
                            session.ParameterIndex = parameterIndex;
                            return session;
                        }
                        else
                        {
                            return null;
                        }
                    }
                    else if (c == ",")
                    {
                        parameterIndex++;
                    }

                }
            }
            else
            {
                var expression2 = _parser.GetCurrentConstructionExpressionAlt(GetCurrentLineText());

                if (expression2 != null && expression2.Identifier != null)
                {
                    ConstructionSession session = new ConstructionSession();

                    var line = GetCurrentLine();
                    int parameterIndex = 0;
                    for (int i = CaretOffset; i > line.Offset; i--)
                    {
                        String c = Document.GetText(i, 1);

                        if (c == "(")
                        {
                            KnownType type = null;

                            if (expression2.Identifier != null)
                            {
                                var typeName = expression2.Identifier.ToString();
                                type = _knownTypes.FirstOrDefault(x => x.Type.Name == typeName);

                                if (type != null)
                                {
                                    session.Type = type;
                                    session.ParameterIndex = parameterIndex;
                                    return session;
                                }
                                else
                                {
                                    return null;
                                }
                            }
                        }
                        else if (c == ",")
                        {
                            parameterIndex++;
                        }
                    }
                }
            }

            return null;
        }

        private MethodSession GetMethodSession()
        {
            var currentLine = GetCurrentLineText();

            if (currentLine.Count(x => x == '(') > 1)
            {
                currentLine = currentLine.Split('(')[currentLine.Split('(').Length - 2];
            }

            currentLine = Regex.Replace(currentLine, "(?<=\")(.*?)(?=\")", string.Empty);

            var words = currentLine.Split(' ');

            if (words.Count() > 0 && (words.First() == "private" || words.First() == "public" || words.First() == "void"))
            {
                return null;
            }

            var expression = words.LastOrDefault();

            if (expression != null)
            {
                int parameterIndex = expression.Count(x => x == ',');


                expression = new string(expression.TakeWhile(x => x != '(').ToArray());

                var tree = expression.Split('.').Select(x => x.Remove(@"\n|\r|\s|\t|\(|\)|\[|\]|<.*>")).ToList();
                var variableName = tree.FirstOrDefault();

                if (variableName != null && tree.Count > 1)
                {
                    tree.RemoveAt(0);
                    var variables = _parser.GetContextSymbols(Document.Text, CaretOffset);
                    var variable = variables.FirstOrDefault(x => x.Name == variableName);

                    if (variable != null)
                    {
                        var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable.Type, "<.+>", "<T>"));

                        if (knownType != null)
                        {
                            while (tree.Count > 1)
                            {
                                var memberName = tree.First();
                                tree.RemoveAt(0);
                                var member = knownType.Members.FirstOrDefault(x => x.Name == memberName);

                                if (member == null)
                                {
                                    return null;
                                }

                                knownType = _knownTypes.FirstOrDefault(x => x.Type.Namespace + "." + x.Type.Name == member.ReturnType.Namespace + "." + member.ReturnType.Name);
                            }

                            return new MethodSession()
                            {
                                Type = knownType,
                                MethodName = tree.Last(),
                                ParameterIndex = parameterIndex,
                            };
                        }
                    }
                }
            }

            return null;
        }

        private MethodSession GetStaticMethodSession()
        {
            var words = GetCurrentLineText().Split(' ');

            if (words.Count() > 0 && (words.First() == "private" || words.First() == "public" || words.First() == "void"))
            {
                return null;
            }

            var expression = words.LastOrDefault();

            if (expression != null)
            {
                int parameterIndex = expression.Count(x => x == ',');
                expression = new string(expression.TakeWhile(x => x != '(').ToArray());

                var tree = expression.Split('.').Select(x => x.Remove(@"\n|\r|\s|\t|\(|\)|\[|\]|<.*>")).ToList();
                var variableName = tree.FirstOrDefault();

                if (variableName != null && tree.Count > 1)
                {
                    tree.RemoveAt(0);
                    var variables = _parser.GetContextSymbols(Document.Text, CaretOffset);
                    var variable = variableName;

                    if (variable != null)
                    {
                        var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable, "<.+>", "<T>"));

                        if (knownType != null)
                        {
                            while (tree.Count > 1)
                            {
                                var memberName = tree.First();
                                tree.RemoveAt(0);
                                var member = knownType.Members.FirstOrDefault(x => x.Name == memberName);

                                if (member == null)
                                {
                                    return null;
                                }

                                knownType = _knownTypes.FirstOrDefault(x => x.Type.Namespace + "." + x.Type.Name == member.ReturnType.Namespace + "." + member.ReturnType.Name);
                            }

                            return new MethodSession()
                            {
                                Type = knownType,
                                MethodName = tree.Last(),
                                ParameterIndex = parameterIndex,
                            };
                        }
                    }
                }
            }

            return null;
        }

        private DeclaredMethodSession GetDeclaredMethodSession()
        {
            var currentLine = GetCurrentLineText();

            if (currentLine.Count(x => x == '(') > 1)
            {
                currentLine = currentLine.Split('(')[currentLine.Split('(').Length - 2];
            }

            var words = currentLine.Split(' ');

            if (words.Count() > 0 && (words.First() == "private" || words.First() == "public" || words.First() == "void"))
            {
                return null;
            }

            var expression = currentLine;

            if (expression != null)
            {
                var tree = expression.Split('.').Select(x => x.Replace("\n", "").Replace("\r", "").Replace(" ", "").Replace("\t", "").Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "")).ToList();
                var variableName = tree.FirstOrDefault();

                if (variableName != null && tree.Count > 0)
                {
                    tree.RemoveAt(0);
                    var variables = _parser.GetContextSymbols(Document.Text, CaretOffset);
                    var variable = variables.FirstOrDefault(x => x.Name == variableName);

                    if (variable != null)
                    {
                        var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Type, "<.+>", "<T>"));

                        if (declaredType != null)
                        {
                            while (tree.Count > 1)
                            {
                                var memberName = tree.First();
                                tree.RemoveAt(0);
                                var member = declaredType.Symbols.FirstOrDefault(x => x.Name == memberName);

                                if (member == null)
                                {
                                    return null;
                                }

                                declaredType = _declaredTypes.FirstOrDefault(x => x.ContainingNamespace + "." + x.Name == member.ContainingNamespace + "." + member.Type);
                            }

                            var methodName = tree.Count > 0 ? tree.Last() : variableName;

                            var method = declaredType.Symbols.FirstOrDefault(x => x.Kind == SymbolKind.Method && x.Name == methodName);

                            if (method != null)
                            {
                                return new DeclaredMethodSession()
                                {
                                    Type = declaredType,
                                    Method = method,
                                };
                            }
                        }
                        else if (tree.Count == 0)
                        {
                            var method = variable;

                            if (method != null)
                            {
                                return new DeclaredMethodSession()
                                {
                                    Type = declaredType,
                                    Method = method,
                                };
                            }
                        }
                    }
                }
            }

            return null;
        }

        private List<String> GetPreviousWords()
        {
            var currentLine = GetCurrentLine();
            var currentText = Document.GetText(currentLine.Offset, CaretOffset - currentLine.Offset);
            return currentText.Split(' ', ',').ToList();
        }

        #endregion

        #region Reference Assemblies Changed

        private void OnReferenceAssembliesChanged()
        {
            if (ReferenceAssemblies != null)
            {
                ReferenceAssemblies.CollectionChanged -= ReferenceAssemblies_CollectionChanged;
                ReferenceAssemblies.CollectionChanged += ReferenceAssemblies_CollectionChanged;

                InvalidateHighlighting();
            }
        }

        private void ReferenceAssemblies_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            InvalidateHighlighting();
        }

        #endregion

        #region Public Methods

        public void FormatCode()
        {
            try
            {
                int index = CaretOffset;
                Document.BeginUpdate();
                IndentCode();
                Document.EndUpdate();
                CaretOffset = index;
            }
            catch
            {
                Debug.WriteLine("Error formatting code.");
            }
        }

        public void Highlight(int position, int length, int line)
        {
            Select(position, Math.Max(length, 1));
            ScrollToLine(line);
        }

        public void InsertCode(String code)
        {
            Document.Insert(TextArea.Caret.Offset, code);
        }

        public int Find(String text)
        {
            if (String.IsNullOrEmpty(text)) return -1;

            string txt = Document.Text;
            int index = txt.IndexOf(text, TextArea.Caret.Offset);

            if (index > -1)
            {
                Select(index, text.Length);
                ScrollToLine(TextArea.Selection.StartPosition.Line);
            }
            else
            {
                index = txt.IndexOf(text, 0);

                if (index > -1)
                {
                    Select(index, text.Length);
                    ScrollToLine(TextArea.Selection.StartPosition.Line);
                }
                else
                {
                    Select(0, 0);
                    System.Media.SystemSounds.Beep.Play();
                }
            }

            return index;
        }

        public int ReplaceNext(String text, String replace)
        {
            if (String.IsNullOrEmpty(text)) return -1;

            String selectedText = TextArea.Selection.GetText();

            if (selectedText == text)
            {
                TextArea.Selection.ReplaceSelectionWithText(replace);
            }

            return Find(text);
        }

        public int ReplaceAll(String text, String replace)
        {
            int counter = 0;

            Select(0, 0);

            while (ReplaceNext(text, replace) > -1)
            {
                counter++;
            };

            return counter;
        }

        public void ColorizeByKeyword(String text)
        {
            ResetColorizationByKeyword();

            if (String.IsNullOrEmpty(text)) return;

            var txt = Document.Text;

            var indexes = txt.AllIndexesOf(text).ToList();

            foreach (var index in indexes)
            {
                Document.BeginUpdate();

                var line = Document.GetLineByOffset(index);

                OffsetColorizer colorizer = new OffsetColorizer(line, index, index + text.Length, ColorizeBrush);
                TextArea.TextView.LineTransformers.Add(colorizer);

                Document.EndUpdate();
            }
        }

        public void ResetColorizationByKeyword()
        {
            Document.BeginUpdate();

            for (int i = 0; i < TextArea.TextView.LineTransformers.Count; i++)
            {
                if (TextArea.TextView.LineTransformers[i] is OffsetColorizer)
                {
                    TextArea.TextView.LineTransformers.RemoveAt(i);
                    i--;
                }
            }

            Document.EndUpdate();
        }

        public void HighlighError(int position, int length)
        {
            try
            {
                ITextMarker marker = errorMarkerService.Create(position, length);
                marker.MarkerTypes = TextMarkerTypes.SquigglyUnderline;
                marker.MarkerColor = Colors.Red;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error highlighting script error.\n{ex.ToString()}");
            }
        }

        public void ClearErrors()
        {
            errorMarkerService.RemoveAll(m => true);
        }

        public void HighlightErrorLine(int lineNumber)
        {
            Document.BeginUpdate();

            var line = Document.GetLineByNumber(lineNumber);
            OffsetColorizer errorLineColrizer = new OffsetColorizer(line, line.Offset, line.EndOffset, ErrorLineBrush);
            TextArea.TextView.LineTransformers.Add(errorLineColrizer);

            Document.EndUpdate();
        }

        public void HighlightDebugLine(int lineNumber)
        {
            Document.BeginUpdate();

            var line = Document.GetLineByNumber(lineNumber);
            OffsetColorizer errorLineColrizer = new OffsetColorizer(line, line.Offset, line.EndOffset, DebugLineBrush);
            TextArea.TextView.LineTransformers.Add(errorLineColrizer);

            Document.EndUpdate();
        }

        public void HighlightBreakPoint(int lineNumber, List<ScriptBreakPointSymbol> symbols)
        {
            _breakPointLineNumber = lineNumber;
            _breakPointSymbols = symbols.ToList();
            _currentBreakPointSymbol = null;

            Document.BeginUpdate();

            var line = Document.GetLineByNumber(lineNumber);
            OffsetColorizer errorLineColrizer = new OffsetColorizer(line, line.Offset, line.EndOffset, BreakPointLineBrush);
            TextArea.TextView.LineTransformers.Add(errorLineColrizer);

            var breakPoint = breakPointMargin.BreakPoints.FirstOrDefault(x => x.LineNumber == lineNumber);
            breakPoint.IsActive = true;
            breakPointMargin.InvalidateVisual();

            Document.EndUpdate();
        }

        public void ResetBreakPointLine()
        {
            _breakPointSymbols = new List<ScriptBreakPointSymbol>();
            _currentBreakPointSymbol = null;
            ResetColorizationByKeyword();
            breakPointMargin.BreakPoints.ToList().ForEach(x => x.IsActive = false);
            Mouse.OverrideCursor = null;
            ClearErrors();
            breakPointMargin.InvalidateVisual();
        }

        public Point? GetLineVisualPosition(int lineNumber)
        {
            double top = TextArea.TextView.GetVisualTopByDocumentLine(lineNumber);
            var visualLine = TextArea.TextView.GetVisualLine(lineNumber);

            if (visualLine != null)
            {
                var textLine = visualLine.GetTextLine(0);
                var x = visualLine.GetTextLineVisualXPosition(textLine, visualLine.VisualLengthWithEndOfLineMarker);
                var left = visualLine.VisualLengthWithEndOfLineMarker;
                return new Point(x, top);
            }

            return null;
        }

        public List<ScriptBreakPoint> GetBreakPoints()
        {
            List<ScriptBreakPoint> breakPoints = new List<ScriptBreakPoint>();

            foreach (var b in breakPointMargin.BreakPoints)
            {
                ScriptBreakPoint breakPoint = new ScriptBreakPoint();
                breakPoint.Script = ScriptSource;
                breakPoint.LineNumber = b.LineNumber;

                var line = Document.GetLineByNumber(b.LineNumber);
                breakPoint.LineStartOffset = line.Offset;
                breakPoint.LineEndOffset = line.EndOffset;

                var symbols = _parser.GetContextSymbols(Document.Text, line.Offset);

                foreach (var symbol in symbols.Where(x => (x.Kind == SymbolKind.Property || x.Kind == SymbolKind.Field || x.Kind == SymbolKind.Local || x.Kind == SymbolKind.Parameter) && !x.IsUnassigned))
                {
                    if (symbol.Offset < line.Offset)
                    {
                        breakPoint.ContextSymbols.Add(new ScriptBreakPointSymbol()
                        {
                            Name = symbol.Name,
                            Offset = symbol.Offset,
                            Length = symbol.Length,
                        });
                    }
                }

                breakPoints.Add(breakPoint);
            }

            return breakPoints;
        }

        #endregion

        #region BreakPoint Symbols Search

        private void ScriptEditor_MouseMove(object sender, MouseEventArgs e)
        {
            if (IsReadOnly && _breakPointSymbols.Count > 0)
            {
                try
                {
                    var word_separators_plus = word_separators.ToList();
                    word_separators_plus.Add(')');
                    word_separators_plus.Add(';');

                    var textView = TextArea.TextView;
                    Point position = e.GetPosition(textView);
                    position.Y += textView.VerticalOffset;
                    VisualLine visualLine = textView.GetVisualLineFromVisualTop(position.Y);
                    int columnIndex = visualLine.GetVisualColumnFloor(position, false);
                    String line = Document.GetText(visualLine.FirstDocumentLine.Offset, visualLine.FirstDocumentLine.Length);
                    if (columnIndex < line.Length)
                    {
                        int wordStartIndex = columnIndex;
                        int wordEndIndex = columnIndex;

                        while (wordStartIndex > 0)
                        {
                            if (word_separators_plus.Contains(line[wordStartIndex])) break;
                            wordStartIndex--;
                        }

                        while (wordEndIndex < line.Length)
                        {
                            if (word_separators_plus.Contains(line[wordEndIndex])) break;
                            wordEndIndex++;
                        }

                        if (wordStartIndex > 0)
                        {
                            wordStartIndex++;
                        }

                        String word = line.Substring(wordStartIndex, wordEndIndex - wordStartIndex);

                        var breakPointSymbol = _breakPointSymbols.FirstOrDefault(x => x.Name == word);

                        if (breakPointSymbol != null)
                        {
                            int wordStartOffset = visualLine.FirstDocumentLine.Offset + wordStartIndex;

                            ClearErrors();
                            ITextMarker marker = errorMarkerService.Create(wordStartOffset, word.Length);
                            marker.MarkerTypes = TextMarkerTypes.NormalUnderline;
                            marker.MarkerColor = Colors.Yellow;
                            Mouse.OverrideCursor = Cursors.Hand;

                            _currentBreakPointSymbol = breakPointSymbol;
                            _currentBreakPointSymbolPosition = visualLine.GetVisualPosition(wordEndIndex, VisualYPosition.LineTop);
                        }
                        else
                        {
                            _currentBreakPointSymbol = null;
                            Mouse.OverrideCursor = null;
                            ClearErrors();
                        }
                    }
                    else
                    {
                        _currentBreakPointSymbol = null;
                        Mouse.OverrideCursor = null;
                        ClearErrors();
                    }
                }
                catch (Exception ex)
                {
                    _currentBreakPointSymbol = null;
                    Mouse.OverrideCursor = null;
                    ClearErrors();
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonUp(e);

            if (_currentBreakPointSymbol != null)
            {
                Mouse.OverrideCursor = null;
                Debug.WriteLine($"Pressed on break point symbol: {_currentBreakPointSymbol.Name}");
                BreakPointSymbolPressed?.Invoke(this, new BreakPointSymbolPressedEventArgs()
                {
                    BreakPointSymbol = _currentBreakPointSymbol,
                    Position = _currentBreakPointSymbolPosition
                });
            }
        }

        public String GetCaretWord()
        {
            try
            {
                var word_separators_plus = word_separators.ToList();
                word_separators_plus.Add(')');
                word_separators_plus.Add(';');

                int wordStartOffset = CaretOffset;
                int wordEndOffset = CaretOffset;

                while (wordStartOffset > 0)
                {
                    if (word_separators_plus.Contains(Document.Text[wordStartOffset])) break;
                    wordStartOffset--;
                }

                while (wordEndOffset < Document.Text.Length)
                {
                    if (word_separators_plus.Contains(Document.Text[wordEndOffset])) break;
                    wordEndOffset++;
                }

                if (wordStartOffset > 0)
                {
                    wordStartOffset++;
                }

                String word = Document.Text.Substring(wordStartOffset, wordEndOffset - wordStartOffset);

                return word;
            }
            catch
            {
                return null;
            }
        }

        #endregion
    }
}