aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs761
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
}
}