aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Synchronization/ViewModels/DirectSynchronizationViewVM.cs
blob: a09e698e3d77cfde2230c09bcce413ef2c74c7bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity.Validation;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Core.Commands;
using Tango.Core.Helpers;
using Tango.BL.Entities;
using Tango.Integration.ExternalBridge;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Synchronization.Navigation;
using Tango.PMR.Integration;
using Tango.SharedUI;
using Tango.Synchronization;
using Tango.Synchronization.Local;
using Tango.Synchronization.Remote;
using Tango.Core.DI;

namespace Tango.MachineStudio.Synchronization.ViewModels
{
    /// <summary>
    /// Represents the 'Direct Synchronization' view model.
    /// </summary>
    /// <seealso cref="Tango.SharedUI.ViewModel" />
    public class DirectSynchronizationViewVM : ViewModel
    {
        private SyncNavigationManager _navigation;
        private String _slaveDBFile;
        private String _masterDBFile;
        private LocalDBComparer _comparer;
        private INotificationProvider _notification;
        private String _comparedSerialNumber;
        private MainViewVM _mainView;

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DirectSynchronizationViewVM"/> class.
        /// </summary>
        /// <param name="applicationManager">The application manager.</param>
        /// <param name="navigation">The navigation.</param>
        /// <param name="notification">The notification.</param>
        public DirectSynchronizationViewVM(IStudioApplicationManager applicationManager, SyncNavigationManager navigation, INotificationProvider notification)
        {
            ApplicationManager = applicationManager;

            _navigation = navigation;
            _notification = notification;

            BackCommand = new RelayCommand(() => _navigation.NavigateTo(NavigationView.MenuView));

            Differences = new ObservableCollection<Diff>();

            CompareCommand = new RelayCommand(Compare, (x) => !IsWorking && SelectedMachine != null);
            CommitAllCommand = new RelayCommand(Synchronize, (x) => Differences.Count > 0 && !IsWorking && SelectedMachine != null);
        }

        #endregion

        #region Commands

        /// <summary>
        /// Gets or sets the back command.
        /// </summary>
        public RelayCommand BackCommand { get; set; }

        /// <summary>
        /// Gets or sets the browse master database command.
        /// </summary>
        public RelayCommand BrowseMasterDBCommand { get; set; }

        /// <summary>
        /// Gets or sets the browse slave database command.
        /// </summary>
        public RelayCommand BrowseSlaveDBCommand { get; set; }

        /// <summary>
        /// Gets or sets the compare command.
        /// </summary>
        public RelayCommand CompareCommand { get; set; }

        /// <summary>
        /// Gets or sets the commit command.
        /// </summary>
        public RelayCommand CommitCommand { get; set; }

        /// <summary>
        /// Gets or sets the commit all command.
        /// </summary>
        public RelayCommand CommitAllCommand { get; set; }

        /// <summary>
        /// Gets or sets the clean command.
        /// </summary>
        public RelayCommand CleanCommand { get; set; }

        #endregion

        #region Properties

        public IStudioApplicationManager ApplicationManager { get; set; }

        private bool _isWorking;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is working.
        /// </summary>
        public bool IsWorking
        {
            get { return _isWorking; }
            set { _isWorking = value; RaisePropertyChangedAuto(); }
        }

        private bool _isClearMachine;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is clear machine.
        /// </summary>
        public bool IsClearMachine
        {
            get { return _isClearMachine; }
            set
            {
                _isClearMachine = value;
                RaisePropertyChangedAuto();
            }
        }

        private ObservableCollection<Diff> _differences;
        /// <summary>
        /// Gets or sets the differences.
        /// </summary>
        public ObservableCollection<Diff> Differences
        {
            get { return _differences; }
            set { _differences = value; RaisePropertyChanged(nameof(Differences)); }
        }

        private Diff _selectedDifference;
        /// <summary>
        /// Gets or sets the selected difference.
        /// </summary>
        public Diff SelectedDifference
        {
            get { return _selectedDifference; }
            set { _selectedDifference = value; RaisePropertyChanged(nameof(SelectedDifference)); InvalidateRelayCommands(); }
        }

        private Machine _selectedMachine;
        /// <summary>
        /// Gets or sets the selected machine.
        /// </summary>
        public Machine SelectedMachine
        {
            get { return _selectedMachine; }
            set { _selectedMachine = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        #endregion

        #region Event Handlers

        private void Comparer_Progress(object sender, string e)
        {
            if (_mainView == null)
            {
                _mainView = TangoIOC.Default.GetInstance<MainViewVM>();
                _mainView.Log = String.Empty;
            }

            _mainView.Log += ("[" + DateTime.Now.ToTimeString() + "]  " + e + Environment.NewLine);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Compares the selected machine against the remote database.
        /// </summary>
        private void Compare()
        {
            if (SelectedMachine.SerialNumber != ApplicationManager.ConnectedMachine.As<ExternalBridgeTcpClient>().SerialNumber)
            {
                if (!_notification.ShowQuestion("The selected machine serial number does not match the connected machine. Are you sure you want to continue?"))
                {
                    return;
                }
            }

            Task.Factory.StartNew(async () =>
            {
                using (_notification.PushTaskItem("Downloading machine database..."))
                {
                    try
                    {
                        IsWorking = true;
                        InvalidateRelayCommands();
                        Thread.Sleep(1500);

                        ApplicationManager.ConnectedMachine.UseKeepAlive = false;

                        var response = await ApplicationManager.ConnectedMachine.SendRequest<DirectSynchronizationRequest, DirectSynchronizationResponse>(new DirectSynchronizationRequest(), TimeSpan.FromSeconds(60));

                        using (_notification.PushTaskItem("Generating temporary files..."))
                        {
                            var tempFolder = TemporaryManager.CreateFolder();

                            //File path for the reflected remote data base SQLite.
                            _masterDBFile = Path.Combine(tempFolder, "Remote.db");
                            //File path for the received machine SQLite db.
                            _slaveDBFile = Path.Combine(tempFolder, "Local.db");

                            //Save the machine db to file.
                            File.WriteAllBytes(_slaveDBFile, response.Message.LocalDB.ToByteArray());

                            //Copy the SQLite db template.
                            File.Copy(Path.Combine(PathHelper.GetStartupPath(), "Tango.db"), _masterDBFile);

                            //Synchronize the SQL Server db with the new SQLite template. (Overwrite basically)
                            RemoteDBSynchronizer.Synchronize(_masterDBFile, SelectedMachine.SerialNumber, true);

                            _comparedSerialNumber = SelectedMachine.SerialNumber;

                            using (_notification.PushTaskItem("Comparing database..."))
                            {
                                _comparer = new LocalDBComparer(new SQLiteDataBase(_masterDBFile), new SQLiteDataBase(_slaveDBFile));
                                _comparer.Progress += Comparer_Progress;

                                var diffs = _comparer.Compare();
                                Differences = new ObservableCollection<Diff>(diffs);

                                if (diffs.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() > 0)
                                {
                                    ShowInfo("Found " + Differences.Where(x => x.Action != DiffAction.ReplaceTableDataInSlave).Count() + " differences.");
                                }
                                else
                                {
                                    ShowInfo("The machine database is synchronized.");
                                }
                            }
                        }
                    }
                    catch (DbEntityValidationException ex)
                    {
                        String message = "The following validation errors occurred." + Environment.NewLine + Environment.NewLine;

                        foreach (var error in ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).ToList())
                        {
                            message += error.ErrorMessage + Environment.NewLine;
                        }

                        ShowError(message);
                    }
                    catch (Exception ex)
                    {
                        ShowError(ex.Message);
                    }
                    finally
                    {
                        IsWorking = false;
                        SelectedDifference = null;
                        InvalidateRelayCommands();
                        ApplicationManager.ConnectedMachine.UseKeepAlive = true;
                    }
                }
            });
        }


        /// <summary>
        /// Synchronizes the selected machine with the remote database.
        /// </summary>
        private void Synchronize()
        {
            if (IsClearMachine)
            {
                if (!_notification.ShowQuestion("This will erase and override the existing machine database. Do you want to proceed?"))
                {
                    return;
                }
            }

            if (SelectedMachine.SerialNumber != _comparedSerialNumber)
            {
                _notification.ShowError("You have selected a different machine serial number after comparing. Please compare again if you wish to synchronize a different machine.");
                return;
            }

            Task.Factory.StartNew(async () =>
            {
                using (_notification.PushTaskItem("Synchronizing..."))
                {
                    try
                    {
                        ApplicationManager.ConnectedMachine.UseKeepAlive = false;

                        IsWorking = true;
                        InvalidateRelayCommands();
                        Thread.Sleep(1500);

                        for (int i = 0; i < Differences.Count; i++)
                        {
                            var diff = Differences[i];

                            diff.Commit();
                            InvokeUINow(() => Differences.Remove(diff));
                            i--;
                        }

                        _comparer.Dispose();

                        byte[] remoteDbBytes = IsClearMachine ? File.ReadAllBytes(_masterDBFile) : File.ReadAllBytes(_slaveDBFile);

                        var response = await ApplicationManager.ConnectedMachine.SendRequest<OverrideDataBaseRequest, OverrideDataBaseResponse>(new OverrideDataBaseRequest()
                        {
                            RemoteDB = ByteString.CopyFrom(remoteDbBytes)
                        }, TimeSpan.FromSeconds(30));

                        if (!response.Message.Successful)
                        {
                            ShowError("The remote machine has reported some error while trying to override the database.");
                        }
                        else
                        {
                            //Synchronize the SQL Server db with the synchronized master DB.
                            RemoteDBSynchronizer.Synchronize(_masterDBFile, _comparedSerialNumber, false);

                            PathHelper.TryDeleteFile(_slaveDBFile);
                            PathHelper.TryDeleteFile(_masterDBFile);
                            _slaveDBFile = null;
                            _masterDBFile = null;
                            InvalidateRelayCommands();
                        }
                    }
                    catch (Exception ex)
                    {
                        ShowError(ex.Message);
                    }
                    finally
                    {
                        IsWorking = false;
                        SelectedDifference = null;
                        InvalidateRelayCommands();
                        ApplicationManager.ConnectedMachine.UseKeepAlive = true;
                    }
                }
            });
        }

        /// <summary>
        /// Displays an error message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ShowError(String message)
        {
            InvokeUINow(() => _notification.ShowError(message));
        }

        /// <summary>
        /// Displays an information message.
        /// </summary>
        /// <param name="message">The message.</param>
        private void ShowInfo(String message)
        {
            InvokeUINow(() => _notification.ShowInfo(message));
        }

        #endregion
    }
}
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
    }
}