diff options
| author | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:12:05 +0000 |
|---|---|---|
| committer | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:12:05 +0000 |
| commit | b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38 (patch) | |
| tree | ea725abc39def99a755b041c13cba1fe0d594ddc /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | |
| parent | 1bdcaa9f51303bbff682507f31fb3b4414692ca4 (diff) | |
| parent | d33c19b3ac6803de4b5c8d475832efef131c1a45 (diff) | |
| download | Tango-b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38.tar.gz Tango-b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38.zip | |
Merged PR 6: Revert "Hope it is fine"
Revert "Hope it is fine"
Reverted commit `1344a54c`.
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | 1991 |
1 files changed, 1597 insertions, 394 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs index efa1b087a..e65ff671d 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs @@ -1,8 +1,10 @@ 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; @@ -23,7 +25,9 @@ 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; @@ -33,6 +37,7 @@ 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 @@ -42,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; @@ -52,9 +60,48 @@ 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; + 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; } @@ -85,6 +132,8 @@ namespace Tango.Scripting.Editors #region Properties + public static List<String> BlockedUsingsCache { get; set; } + /// <summary> /// Gets or sets a value indicating whether to enable folding. /// </summary> @@ -110,13 +159,13 @@ namespace Tango.Scripting.Editors /// <summary> /// Gets or sets the reference assemblies. /// </summary> - public ObservableCollection<ReferenceAssembly> ReferenceAssemblies + public ObservableCollection<Assembly> ReferenceAssemblies { - get { return (ObservableCollection<ReferenceAssembly>)GetValue(ReferenceAssembliesProperty); } + get { return (ObservableCollection<Assembly>)GetValue(ReferenceAssembliesProperty); } set { SetValue(ReferenceAssembliesProperty, value); } } public static readonly DependencyProperty ReferenceAssembliesProperty = - DependencyProperty.Register("ReferenceAssemblies", typeof(ObservableCollection<ReferenceAssembly>), typeof(ScriptEditor), new PropertyMetadata(null)); + DependencyProperty.Register("ReferenceAssemblies", typeof(ObservableCollection<Assembly>), typeof(ScriptEditor), new PropertyMetadata(null, (d, e) => (d as ScriptEditor).OnReferenceAssembliesChanged())); public Object CurrentPopupContent { @@ -126,6 +175,70 @@ namespace Tango.Scripting.Editors 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 @@ -137,6 +250,87 @@ namespace Tango.Scripting.Editors 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> @@ -148,16 +342,17 @@ namespace Tango.Scripting.Editors _current_usings = new List<string>(); - ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>(); + //ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>(); - //Add basic assemblies... - ReferenceAssemblies.Add(new ReferenceAssembly(typeof(String))); //mscorelib - ReferenceAssemblies.Add(new ReferenceAssembly(typeof(Enumerable))); //System.Core - ReferenceAssemblies.Add(new ReferenceAssembly(typeof(Tango.Core.CoreSettings))); //System.Core + ////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(); @@ -175,6 +370,59 @@ namespace Tango.Scripting.Editors 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 @@ -279,18 +527,18 @@ namespace Tango.Scripting.Editors } else if (e.Key == Key.Oem2) { - int offset = CaretOffset; - var line = Document.GetLineByOffset(offset); + //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; - } + //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) { @@ -304,474 +552,509 @@ namespace Tango.Scripting.Editors private void TextArea_TextEntered(object sender, TextCompositionEventArgs e) { - List<Object> items = new List<object>(); + if (IsReadOnly) return; - HidePopup(); + try + { + List<Object> items = new List<object>(); - var lineText = GetCurrentLineText(); - var previousWords = GetPreviousWords(); - var previousWordsLast = previousWords.LastOrDefault(); - String currentWord = previousWordsLast != null ? previousWordsLast.Replace("\t", "") : String.Empty; - String currentWordIncludingParenthesis = currentWord.Split('(').LastOrDefault(); + HidePopup(); - if (previousWords.Count > 0 && previousWords.First().Trim().StartsWith("//")) return; + 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 (e.Text == " " && previousWords.Count > 2 && previousWords[previousWords.Count - 2] == "=") - { - var expression = previousWords.First(); - var knownType = GetKnownTypeFromExpression(expression + "."); + if (previousWords.Count > 0 && previousWords.First().Trim().StartsWith("//")) return; - if (knownType != null && knownType.Type.IsEnum) + if (e.Text == " " && previousWords.Count > 2 && previousWords[previousWords.Count - 2] == "=") { - completionWindow.HideCompletion(); - IList<ICompletionData> data = new List<ICompletionData>(); + var expression = previousWords.First(); + var knownTypeResult = GetKnownTypeFromExpression(expression + "."); - foreach (var field in knownType.Fields) + if (knownTypeResult != null && knownTypeResult.KnownType.Type.IsEnum) { - data.Add(new FieldCompletionItem() + completionWindow.HideCompletion(); + IList<ICompletionData> data = new List<ICompletionData>(); + + foreach (var field in knownTypeResult.KnownType.Fields) { - Class = knownType.FriendlyName, - Name = knownType.FriendlyName + "." + field.Name, - Type = field.ReturnTypeFriendlyName, - Description = field.Summary, - }); + 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()); } - ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord()); } - - } - else if (e.Text == " " && GetPreviousWord() == "new") - { - var s = _parser.GetExpressionFirst<FieldDeclarationSyntax>(GetCurrentLineText()); - - if (s != null) + else if (e.Text == " " && GetPreviousWord() == "new") { - String type = s.Declaration.Type.ToString(); - - IList<ICompletionData> data = new List<ICompletionData>(); + var s = _parser.GetExpressionFirst<FieldDeclarationSyntax>(GetCurrentLineText()); - data.Add(new ClassCompletionItem() + if (s != null) { - Name = type, - Description = "Auto generate assignment...", - }); + String type = s.Declaration.Type.ToString(); - ShowCompletionWindow(data, type); - } - } - else if (e.Text == ";" || e.Text == " ") - { - HideCompletionWindow(); - } - else if (e.Text == ".") - { - var knownType = GetCurrentKnownType(); + IList<ICompletionData> data = new List<ICompletionData>(); - if (knownType != null) + data.Add(new ClassCompletionItem() + { + Name = type, + Description = "Auto generate assignment...", + }); + + ShowCompletionWindow(data, type); + } + } + else if (e.Text == ";" || e.Text == " ") { - completionWindow.HideCompletion(); + HideCompletionWindow(); + } + else if (e.Text == ".") + { + var knownTypeResult = GetCurrentKnownType(); + IList<ICompletionData> data = new List<ICompletionData>(); - if (!knownType.Type.IsEnum) + if (knownTypeResult != null) { - var typeMembers = knownType.Members.ToList(); + completionWindow.HideCompletion(); - foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().GroupBy(x => x.NameWithTypeArguments)) + if (!knownTypeResult.KnownType.Type.IsEnum) { - var method = methodGroup.First(); + var typeMembers = knownTypeResult.KnownType.Members.ToList(); - data.Add(new MethodCompletionItem() + foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().Where(x => (!knownTypeResult.IsStatic && !x.IsStatic) || (knownTypeResult.IsStatic && x.IsStatic)).GroupBy(x => x.NameWithTypeArguments)) { - Class = knownType.FriendlyName, - Name = method.NameWithTypeArguments, - ReturnType = method.ReturnTypeFriendlyName, - Description = method.Summary, - Parameters = method.Parameters, - Overloads = methodGroup.Count() - 1, - }); - } + var method = methodGroup.First(); - foreach (var methodGroup in typeMembers.Where(x => x.GetType() != typeof(KnownTypeMethod)).GroupBy(x => x.Name)) - { - var member = 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, + }); + } - data.Add(new PropertyCompletionItem() + foreach (var ev in typeMembers.OfType<KnownTypeEvent>()) { - Class = knownType.FriendlyName, - Name = member.Name, - Type = member.ReturnTypeFriendlyName, - Description = member.Summary, - }); + 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 knownType.Fields) + else { - data.Add(new FieldCompletionItem() + foreach (var field in knownTypeResult.KnownType.Fields) { - Class = knownType.FriendlyName, - Name = field.Name, - Type = field.ReturnTypeFriendlyName, - Description = field.Summary, - }); + 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) + ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord()); + } + else { - completionWindow.HideCompletion(); - IList<ICompletionData> data = new List<ICompletionData>(); + var declaredType = GetCurrentDeclaredType(); - var typeMembers = declaredType.Symbols.ToList(); - - foreach (var methodGroup in typeMembers.GroupBy(x => x.Name)) + if (declaredType != null) { - var member = methodGroup.First(); + completionWindow.HideCompletion(); - 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, - }; + var typeMembers = declaredType.Symbols.ToList(); + foreach (var methodGroup in typeMembers.GroupBy(x => x.Name)) + { + var member = methodGroup.First(); - for (int i = 0; i < member.Parameters.Count; i++) + if (member.Kind == SymbolKind.Method) { - var pair = member.Parameters[i]; + var methodCompletion = new MethodCompletionItem() + { + Class = declaredType.Name, + Name = member.Name, + ReturnType = member.Type, + Description = member.Summary, + Overloads = methodGroup.Count() - 1, + }; - methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + + for (int i = 0; i < member.Parameters.Count; i++) { - Type = pair.Key, - Name = pair.Value, - IsLast = (i == member.Parameters.Count - 1) - }); - } + var pair = member.Parameters[i]; - data.Add(methodCompletion); + methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + { + Type = pair.Key, + Name = pair.Value, + IsLast = (i == member.Parameters.Count - 1) + }); + } - } - else if (member.Kind == SymbolKind.Property) - { - data.Add(new PropertyCompletionItem() + data.Add(methodCompletion); + + } + else if (member.Kind == SymbolKind.Property) { - Class = declaredType.Name, - Name = member.Name, - Type = member.Type, - Description = member.Summary, - }); - } - else if (member.Kind == SymbolKind.Field) - { - data.Add(new FieldCompletionItem() + data.Add(new PropertyCompletionItem() + { + Class = declaredType.Name, + Name = member.Name, + Type = member.Type, + Description = member.Summary, + }); + } + else if (member.Kind == SymbolKind.Field) { - Class = declaredType.Name, - Name = member.Name, - Type = member.Type, - Description = member.Summary, - }); + data.Add(new FieldCompletionItem() + { + Class = declaredType.Name, + Name = member.Name, + Type = member.Type, + Description = member.Summary, + }); + } } - } - ShowCompletionWindow(data, GetCurrentWord()); + ShowCompletionWindow(data, GetCurrentWord()); + } } } - } - else if (e.Text == "(" || e.Text == ",") - { - completionWindow.HideCompletion(); - - try + else if (e.Text == "(" || e.Text == ",") { - var session = GetConstructionSession(); + completionWindow.HideCompletion(); - if (session != null) + try { - var content = CreateConstructionSessionPopupContent(session); - if (content.Methods.Count > 0) + var session = GetConstructionSession(); + + if (session != null) { - ShowPopup(content); - return; + var content = CreateConstructionSessionPopupContent(session); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } } - } - var methodSession = GetMethodSession(); + var methodSession = GetMethodSession(); - if (methodSession != null) - { - var content = CreateMethodSessionPopupContent(methodSession); - if (content.Methods.Count > 0) + if (methodSession != null) { - ShowPopup(content); - return; + var content = CreateMethodSessionPopupContent(methodSession); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } } - } - var declaredMethodSession = GetDeclaredMethodSession(); + var declaredMethodSession = GetDeclaredMethodSession(); - if (declaredMethodSession != null) - { - var content = CreateDeclaredMethodSessionPopupContent(declaredMethodSession); - if (content.Methods.Count > 0) + if (declaredMethodSession != null) { - ShowPopup(content); - return; + 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); + } } - catch (Exception ex) - { - Debug.WriteLine(ex); - } - } - else if (lineText.StartsWith("using")) - { - if (completionWindow.IsVisible) + else if (lineText.StartsWith("using") && e.Text != "\n") { - completionWindow.UpdatePositionFix(); - return; - } + if (completionWindow.IsVisible) + { + completionWindow.UpdatePositionFix(); + return; + } - IList<ICompletionData> data = new List<ICompletionData>(); + IList<ICompletionData> data = new List<ICompletionData>(); - foreach (var asm in ReferenceAssemblies) - { - foreach (var ns in asm.Assembly.GetTypes().Select(x => x.Namespace).Distinct().Where(x => x != null)) + foreach (var asm in ReferenceAssemblies) { - data.Add(new NamespaceCompletionItem() + foreach (var ns in asm.GetTypes().Select(x => x.Namespace).Distinct().Where(x => x != null)) { - Name = ns, - Assembly = asm.Assembly.GetName().Name, - }); + data.Add(new NamespaceCompletionItem() + { + Name = ns, + Assembly = asm.GetName().Name, + }); + } } - } - data = data.DistinctBy(x => x.Text).ToList(); + 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--; + ShowCompletionWindow(data, GetCurrentWord()); } - } - else if (e.Text == "}") - { - if (Document.GetText(CaretOffset - 2, 1) == "{" && Document.GetText(CaretOffset, 1) == "}") + else if (e.Text == "{") { - 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"); + int parentesisCount = lineText.TakeWhile(x => x != '{').Count(x => x == '\"'); + + if (parentesisCount % 2 == 0) + { + Document.Insert(CaretOffset, "}"); + CaretOffset--; + } } - } - else if (!currentWordIncludingParenthesis.Contains(".") || currentWord[currentWord.Length - 2] == '<') - { - if (completionWindow.IsVisible) + else if (e.Text == "}") { - completionWindow.UpdatePositionFix(); - return; + if (Document.GetText(CaretOffset - 2, 1) == "{" && Document.GetText(CaretOffset, 1) == "}") + { + Document.Replace(CaretOffset, 1, ""); + } } - - var previous_word = GetPreviousWord(); - var word = GetCurrentWord(); - - if (word.Contains("<")) + else if (e.Text == "\n") { - word = word.Last(x => x != '<').ToString(); + if (Document.GetText(CaretOffset - 3, 1) == "{" && Document.GetText(CaretOffset, 1) == "}") + { + CaretOffset--; + Document.Insert(CaretOffset, "\n\t"); + } } - - if (previous_word != word) + else if (!currentWordIncludingParenthesis.Contains(".") || currentWord[currentWord.Length - 2] == '<') { - if (_knownTypes.Exists(x => x.Name == previous_word)) + if (completionWindow.IsVisible) { + completionWindow.UpdatePositionFix(); return; } - if (_blocking_type_words.Contains(previous_word)) + var previous_word = GetPreviousWord(); + var word = GetCurrentWord(); + + if (word.Contains("<")) { - return; + word = word.Last(x => x != '<').ToString(); } - } - if (!String.IsNullOrWhiteSpace(word)) - { - IList<ICompletionData> data = new List<ICompletionData>(); - - foreach (var type in _declaredTypes.Where(x => x.Name.StartsWith(word))) + if (previous_word != 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) + if (_knownTypes.Exists(x => x.Name == previous_word)) { - 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, - }); + return; } - else + + if (_blocking_type_words.Contains(previous_word)) { - throw new NotImplementedException("Implement generic item here!"); + return; } } - foreach (var type in _knownTypes.ToList().Where(x => x.Name.StartsWith(word))) + if (!String.IsNullOrWhiteSpace(word)) { - if (type.Type.IsEnum) + IList<ICompletionData> data = new List<ICompletionData>(); + + foreach (var snippet in snippets) { - data.Add(new EnumCompletionItem() - { - Namespace = type.Type.Namespace, - Description = type.Summary, - Name = type.FriendlyName, - Priority = 0, - }); + data.Add(snippet); } - else if (type.Type.IsInterface) + + foreach (var type in _declaredTypes.Where(x => x.Name.StartsWith(word))) { - data.Add(new InterfaceCompletionItem() + if (type.Kind == TypeKind.Struct) { - Name = type.FriendlyName, - Description = type.Summary, - Namespace = type.Type.Namespace, - Priority = 0, - }); - } - else if (type.Type.IsValueType) - { - data.Add(new StructCompletionItem() + data.Add(new StructCompletionItem() + { + Name = type.Name, + Description = type.Summary, + Namespace = type.ContainingNamespace, + Priority = 1, + }); + } + else if (type.Kind == TypeKind.Enum) { - Name = type.FriendlyName, - Description = type.Summary, - Namespace = type.Type.Namespace, - Priority = 0, - }); - } - else if (type.Type.IsClass) - { - data.Add(new ClassCompletionItem() + data.Add(new EnumCompletionItem() + { + Name = type.Name, + Description = type.Summary, + Namespace = type.ContainingNamespace, + Priority = 1, + }); + } + else if (type.Kind == TypeKind.Interface) { - Name = type.FriendlyName, - Description = type.Summary, - Namespace = type.Type.Namespace, - Priority = 0, - }); - } - else - { - throw new NotImplementedException("Implement generic item here."); + 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 symbol in _parser.GetContextSymbols(Document.Text, CaretOffset).Where(x => x.Name.StartsWith(GetCurrentWord()))) - { - if (symbol.Kind == SymbolKind.Property) + foreach (var type in _knownTypes.ToList().Where(x => x.Name.StartsWith(word))) { - data.Add(new PropertyCompletionItem() + if (type.Type.IsEnum) { - 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() + data.Add(new EnumCompletionItem() + { + Namespace = type.Type.Namespace, + Description = type.Summary, + Name = type.FriendlyName, + Priority = 0, + }); + } + else if (type.Type.IsInterface) { - Class = symbol.Class, - Description = symbol.Summary, - Name = symbol.Name, - Type = symbol.Type, - Priority = 2, - }); + 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."); + } } - else if (symbol.Kind == SymbolKind.Method) + + foreach (var symbol in _parser.GetContextSymbols(Document.Text, CaretOffset).Where(x => x.Name.StartsWith(GetCurrentWord()))) { - var methodCompletion = new MethodCompletionItem() + if (symbol.Kind == SymbolKind.Property) { - Class = symbol.Class, - Description = symbol.Summary, - Name = symbol.Name, - ReturnType = symbol.Type, - Priority = 2, - }; - - for (int i = 0; i < symbol.Parameters.Count; i++) + 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) { - var pair = symbol.Parameters[i]; - - methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + data.Add(new FieldCompletionItem() { - Type = pair.Key, - Name = pair.Value, - IsLast = (i == symbol.Parameters.Count - 1) + 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]; - data.Add(methodCompletion); + methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + { + Type = pair.Key, + Name = pair.Value, + IsLast = (i == symbol.Parameters.Count - 1) + }); + } + + data.Add(methodCompletion); + } } - } - ShowCompletionWindow(data, word); + ShowCompletionWindow(data, word); + } } } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } } #endregion @@ -792,7 +1075,7 @@ namespace Tango.Scripting.Editors IList<ICompletionData> data = completionWindow.CompletionList.CompletionData; data.Clear(); - foreach (var item in suggestions) + foreach (var item in suggestions.DistinctBy(x => x.Text)) { data.Add(item); } @@ -864,16 +1147,23 @@ 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) { + 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(); @@ -884,7 +1174,7 @@ namespace Tango.Scripting.Editors if (enumType != null) { - return enumType; + return new KnownTypeResult(enumType); } tree.RemoveAt(0); @@ -893,7 +1183,8 @@ namespace Tango.Scripting.Editors if (variable != null) { - var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable.Type, "<.+>", "<T>")); + var name = Regex.Replace(variable.Type, "<.+>", "<T>"); + var knownType = _knownTypes.FirstOrDefault(x => name == x.FriendlyName || name == x.Alias); if (knownType != null) { @@ -911,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; + } } } @@ -926,6 +1245,13 @@ namespace Tango.Scripting.Editors 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(); @@ -958,6 +1284,11 @@ namespace Tango.Scripting.Editors return declaredType; } } + else + { + //Maybe static... + return _declaredTypes.FirstOrDefault(x => x.Name == variableName); + } } } @@ -1047,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; @@ -1140,33 +1471,364 @@ namespace Tango.Scripting.Editors return popup; } - private void InvalidateHighlighting() + 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; - _knownTypes.Clear(); var assemblies = ReferenceAssemblies.ToList(); + KnownType.ExtensionMethodsAssemblies = assemblies.ToList(); var usings = _current_usings.ToList(); Thread t = new Thread(() => { - foreach (var asm in assemblies.Select(x => x.Assembly)) + LoadUsingsSymbols(assemblies, usings); + + if (loadKnownTypes) { - Parallel.ForEach(asm.GetTypes().Where(x => x.IsVisible && x.IsPublic && !x.IsPrimitive), (type) => + _knownTypes.Clear(); + + foreach (var knownType in _knownTypesCache.ToList().Select(x => x.Value).ToList()) { - if (usings.Exists(x => type.Namespace == x)) + if (usings.Exists(x => knownType.Type.Namespace == x) && assemblies.Exists(x => x == knownType.Type.Assembly)) { lock (_knownTypes) { - if (!_knownTypes.Exists(x => x.Type.FullName == type.FullName)) - { - _knownTypes.Add(new KnownType(type)); - } + _knownTypes.Add(knownType); } } - }); + } } if (_knownTypes.Count > 0 || _declaredTypes.Count > 0) @@ -1239,10 +1901,10 @@ namespace Tango.Scripting.Editors })); - foreach (var knownType in _knownTypes) - { - knownType.LoadDocumentation(); - } + //foreach (var knownType in _knownTypes) + //{ + // knownType.LoadDocumentation(); + //} } _isLoadingTypes = false; @@ -1256,10 +1918,19 @@ namespace Tango.Scripting.Editors { 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(); + InvalidateHighlighting(false); } _declaredTypes = declaredTypes; @@ -1311,7 +1982,8 @@ namespace Tango.Scripting.Editors private void IndentCode() { - Text = Indentation.CSharp.CSharpIndentationHelper.IndentCSharpCode(Text); + Text = CodeFormatter.Format(Text); + //Text = Indentation.CSharp.CSharpIndentationHelper.IndentCSharpCode(Text); //Text = _parser.IndentCSharpCode(Text); } @@ -1389,7 +2061,11 @@ namespace Tango.Scripting.Editors private ConstructionSession GetConstructionSession() { - var expression = _parser.GetCurrentConstructionExpression(GetCurrentLineText()); + var currentLine = GetCurrentLineText(); + + //if (currentLine.Count(x => x == '(') > 1) return null; + + var expression = _parser.GetCurrentConstructionExpression(currentLine); if (expression != null) { @@ -1445,13 +2121,64 @@ namespace Tango.Scripting.Editors } } + 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 words = GetCurrentLineText().Split(' '); + 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")) { @@ -1463,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(); @@ -1508,7 +2237,7 @@ namespace Tango.Scripting.Editors return null; } - private DeclaredMethodSession GetDeclaredMethodSession() + private MethodSession GetStaticMethodSession() { var words = GetCurrentLineText().Split(' '); @@ -1517,7 +2246,73 @@ namespace Tango.Scripting.Editors return null; } - var expression = GetPreviousWords().LastOrDefault(); + 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) { @@ -1532,7 +2327,7 @@ namespace Tango.Scripting.Editors if (variable != null) { - var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Class, "<.+>", "<T>")); + var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Type, "<.+>", "<T>")); if (declaredType != null) { @@ -1591,5 +2386,413 @@ namespace Tango.Scripting.Editors } #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 } } |
