diff options
| author | Shlomo Hecht <shlomo@twine-s.com> | 2020-10-29 15:55:21 +0200 |
|---|---|---|
| committer | Shlomo Hecht <shlomo@twine-s.com> | 2020-10-29 15:55:21 +0200 |
| commit | 4b789f33eadfc5cc1d937a80ce03ea8425955ffe (patch) | |
| tree | 7dbbd0529a24f9ca064cab688a0d6d2b8b762ea1 /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | |
| parent | 8f3baa0d9097aa6ed800863a4680608e867c809a (diff) | |
| parent | 11fb700fcbc4627162a9c3f84b03b5016248bd97 (diff) | |
| download | Tango-4b789f33eadfc5cc1d937a80ce03ea8425955ffe.tar.gz Tango-4b789f33eadfc5cc1d937a80ce03ea8425955ffe.zip | |
Merge branch 'master' of https://twinetfs.visualstudio.com/Tango/_git/Tango
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | 761 |
1 files changed, 711 insertions, 50 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs index f63cedcdc..e65ff671d 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs @@ -4,6 +4,7 @@ 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; @@ -46,7 +47,10 @@ namespace Tango.Scripting.Editors 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; @@ -56,6 +60,12 @@ namespace Tango.Scripting.Editors 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; @@ -63,14 +73,35 @@ namespace Tango.Scripting.Editors 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; } @@ -160,6 +191,55 @@ namespace Tango.Scripting.Editors 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 @@ -171,6 +251,8 @@ namespace Tango.Scripting.Editors { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptEditor), new FrameworkPropertyMetadata(typeof(ScriptEditor))); + snippets = new List<SnippetCompletionItem>(); + BlockedUsingsCache = new List<string>(); if (KNOWN_TYPES_CACHE_FOLDER == null) @@ -188,6 +270,67 @@ namespace Tango.Scripting.Editors _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> @@ -208,6 +351,8 @@ namespace Tango.Scripting.Editors _knownTypes = new List<KnownType>(); _parser = new ScriptParser(); + KnownTypesAvailable += ScriptEditor_KnownTypesAvailable; + TextArea.IndentationStrategy = new Indentation.CSharp.CSharpIndentationStrategy(Options); foldingStrategy = new BraceFoldingStrategy(); @@ -227,6 +372,36 @@ namespace Tango.Scripting.Editors 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; @@ -377,6 +552,8 @@ namespace Tango.Scripting.Editors private void TextArea_TextEntered(object sender, TextCompositionEventArgs e) { + if (IsReadOnly) return; + try { List<Object> items = new List<object>(); @@ -394,19 +571,19 @@ namespace Tango.Scripting.Editors if (e.Text == " " && previousWords.Count > 2 && previousWords[previousWords.Count - 2] == "=") { var expression = previousWords.First(); - var knownType = GetKnownTypeFromExpression(expression + "."); + var knownTypeResult = GetKnownTypeFromExpression(expression + "."); - if (knownType != null && knownType.Type.IsEnum) + if (knownTypeResult != null && knownTypeResult.KnownType.Type.IsEnum) { completionWindow.HideCompletion(); IList<ICompletionData> data = new List<ICompletionData>(); - foreach (var field in knownType.Fields) + foreach (var field in knownTypeResult.KnownType.Fields) { data.Add(new FieldCompletionItem() { - Class = knownType.FriendlyName, - Name = knownType.FriendlyName + "." + field.Name, + Class = knownTypeResult.KnownType.FriendlyName, + Name = knownTypeResult.KnownType.FriendlyName + "." + field.Name, Type = field.ReturnTypeFriendlyName, Description = field.Summary, }); @@ -441,25 +618,25 @@ namespace Tango.Scripting.Editors } else if (e.Text == ".") { - var knownType = GetCurrentKnownType(); + var knownTypeResult = GetCurrentKnownType(); IList<ICompletionData> data = new List<ICompletionData>(); - if (knownType != null) + if (knownTypeResult != null) { completionWindow.HideCompletion(); - if (!knownType.Type.IsEnum) + if (!knownTypeResult.KnownType.Type.IsEnum) { - var typeMembers = knownType.Members.ToList(); + var typeMembers = knownTypeResult.KnownType.Members.ToList(); - foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().Where(x => !x.IsStatic).GroupBy(x => x.NameWithTypeArguments)) + 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 = knownType.FriendlyName, + Class = knownTypeResult.KnownType.FriendlyName, Name = method.NameWithTypeArguments, ReturnType = method.ReturnTypeFriendlyName, Description = method.Summary, @@ -472,7 +649,7 @@ namespace Tango.Scripting.Editors { data.Add(new EventCompletionItem() { - Class = knownType.FriendlyName, + Class = knownTypeResult.KnownType.FriendlyName, Name = ev.Name, Description = ev.Summary, }); @@ -484,7 +661,7 @@ namespace Tango.Scripting.Editors data.Add(new PropertyCompletionItem() { - Class = knownType.FriendlyName, + Class = knownTypeResult.KnownType.FriendlyName, Name = member.Name, Type = member.ReturnTypeFriendlyName, Description = member.Summary, @@ -493,11 +670,11 @@ namespace Tango.Scripting.Editors } else { - foreach (var field in knownType.Fields) + foreach (var field in knownTypeResult.KnownType.Fields) { data.Add(new FieldCompletionItem() { - Class = knownType.FriendlyName, + Class = knownTypeResult.KnownType.FriendlyName, Name = field.Name, Type = field.ReturnTypeFriendlyName, Description = field.Summary, @@ -573,34 +750,6 @@ namespace Tango.Scripting.Editors ShowCompletionWindow(data, GetCurrentWord()); } - else - { - //Maybe static ... - var typeText = GetPreviousWord(); - knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == typeText || x.Alias == typeText); - - if (knownType != null) - { - var typeMembers = knownType.Members.ToList(); - - foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().Where(x => x.IsStatic).GroupBy(x => x.NameWithTypeArguments)) - { - var method = methodGroup.First(); - - data.Add(new MethodCompletionItem() - { - Class = knownType.FriendlyName, - Name = method.NameWithTypeArguments, - ReturnType = method.ReturnTypeFriendlyName, - Description = method.Summary, - Parameters = method.Parameters, - Overloads = methodGroup.Count() - 1, - }); - } - - ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord()); - } - } } } else if (e.Text == "(" || e.Text == ",") @@ -746,6 +895,11 @@ namespace Tango.Scripting.Editors { 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) @@ -993,13 +1147,13 @@ namespace Tango.Scripting.Editors return list; } - private KnownType GetCurrentKnownType() + private KnownTypeResult GetCurrentKnownType() { var expression = GetPreviousWords().LastOrDefault(); return GetKnownTypeFromExpression(expression); } - private KnownType GetKnownTypeFromExpression(String expression) + private KnownTypeResult GetKnownTypeFromExpression(String expression) { if (expression != null) { @@ -1020,7 +1174,7 @@ namespace Tango.Scripting.Editors if (enumType != null) { - return enumType; + return new KnownTypeResult(enumType); } tree.RemoveAt(0); @@ -1048,9 +1202,37 @@ namespace Tango.Scripting.Editors knownType = _knownTypes.FirstOrDefault(x => x.Type.Namespace + "." + x.Type.Name == member.ReturnType.Namespace + "." + member.ReturnType.Name); } - return knownType; + 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; + } } } @@ -1102,6 +1284,11 @@ namespace Tango.Scripting.Editors return declaredType; } } + else + { + //Maybe static... + return _declaredTypes.FirstOrDefault(x => x.Name == variableName); + } } } @@ -1191,7 +1378,7 @@ namespace Tango.Scripting.Editors foreach (var m in session.Type.Methods.Where(x => x.Name == session.MethodName)) { MethodDescription method = new MethodDescription(); - method.ReturnType = session.Type.Name; + method.ReturnType = m.ReturnTypeFriendlyName; method.Description = m.Summary; method.Name = m.NameWithTypeArguments; method.Class = session.Type.FriendlyName; @@ -1284,7 +1471,7 @@ namespace Tango.Scripting.Editors return popup; } - public static void LoadUsingsSymbols(List<Assembly> assemblies, List<String> usings) + public void LoadUsingsSymbols(List<Assembly> assemblies, List<String> usings) { lock (_loadUsingsLock) { @@ -1296,6 +1483,12 @@ namespace Tango.Scripting.Editors { 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)) @@ -1317,6 +1510,9 @@ namespace Tango.Scripting.Editors _knownTypesCache.Add(knownType.Type, knownType); } + KnownTypesAvailable?.Invoke(this, new EventArgs()); + InvalidateHighlightingPartial(); + continue; } @@ -1370,13 +1566,17 @@ namespace Tango.Scripting.Editors 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 => x == use)) + if (!BlockedUsingsCache.Exists(x => use.StartsWith(x))) { Task.Factory.StartNew(() => { @@ -1387,6 +1587,11 @@ namespace Tango.Scripting.Editors } } + if (_isUsingsLoadingStarted) + { + UsingsLoadingCompleted?.Invoke(this, new EventArgs()); + } + LoadingSymbolsCompleted?.Invoke(null, new EventArgs()); } } @@ -1502,6 +1707,100 @@ namespace Tango.Scripting.Editors // _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) @@ -1509,6 +1808,7 @@ namespace Tango.Scripting.Editors _isLoadingTypes = true; var assemblies = ReferenceAssemblies.ToList(); + KnownType.ExtensionMethodsAssemblies = assemblies.ToList(); var usings = _current_usings.ToList(); Thread t = new Thread(() => @@ -1876,6 +2176,8 @@ namespace Tango.Scripting.Editors 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")) @@ -1888,6 +2190,8 @@ namespace Tango.Scripting.Editors 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(); @@ -2132,6 +2436,363 @@ namespace Tango.Scripting.Editors 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 } } |
