diff options
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | 348 |
1 files changed, 321 insertions, 27 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs index d06862cef..a4493d2c4 100644 --- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -23,7 +24,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; @@ -52,6 +55,14 @@ namespace Tango.Scripting.Editors private List<KnownType> _knownTypes; private List<ScriptType> _declaredTypes; private bool _isLoadingTypes; + private static JsonSerializerSettings _jsonSettings; + private static Dictionary<Type, KnownType> _knownTypesCache; + private static String KNOWN_TYPES_CACHE_FOLDER; + private static List<CachedAssembly> _cachedAssemblies; + private static bool _isLoadingCachedAssemblies; + private static bool _isCacheAssembliesLoaded; + + public static event EventHandler<TangoProgressChangedEventArgs<int>> AssemblyCacheProgress; #region Mini Classes @@ -110,13 +121,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)); public Object CurrentPopupContent { @@ -126,6 +137,21 @@ 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)); #endregion @@ -137,6 +163,21 @@ namespace Tango.Scripting.Editors static ScriptEditor() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptEditor), new FrameworkPropertyMetadata(typeof(ScriptEditor))); + + 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>(); } /// <summary> @@ -148,12 +189,11 @@ 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(); @@ -175,6 +215,29 @@ namespace Tango.Scripting.Editors completionWindow.AllowsTransparency = true; completionWindow.ResizeMode = ResizeMode.NoResize; completionWindow.InsertionRequest += CompletionWindow_InsertionRequest; + + TextChanged += ScriptEditor_TextChanged; + } + + 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 @@ -370,16 +433,17 @@ namespace Tango.Scripting.Editors { var knownType = GetCurrentKnownType(); + IList<ICompletionData> data = new List<ICompletionData>(); + if (knownType != null) { completionWindow.HideCompletion(); - IList<ICompletionData> data = new List<ICompletionData>(); if (!knownType.Type.IsEnum) { var typeMembers = knownType.Members.ToList(); - foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().GroupBy(x => x.NameWithTypeArguments)) + foreach (var methodGroup in typeMembers.OfType<KnownTypeMethod>().Where(x => !x.IsStatic).GroupBy(x => x.NameWithTypeArguments)) { var method = methodGroup.First(); @@ -432,7 +496,6 @@ namespace Tango.Scripting.Editors if (declaredType != null) { completionWindow.HideCompletion(); - IList<ICompletionData> data = new List<ICompletionData>(); var typeMembers = declaredType.Symbols.ToList(); @@ -491,6 +554,34 @@ namespace Tango.Scripting.Editors ShowCompletionWindow(data, GetCurrentWord()); } + else + { + //Maybe static ... + var typeText = GetPreviousWord(); + knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == 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 == ",") @@ -534,6 +625,18 @@ namespace Tango.Scripting.Editors return; } } + + var staticMethodSession = GetStaticMethodSession(); + + if (staticMethodSession != null) + { + var content = CreateMethodSessionPopupContent(staticMethodSession); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } + } } catch (Exception ex) { @@ -552,12 +655,12 @@ namespace Tango.Scripting.Editors foreach (var asm in ReferenceAssemblies) { - foreach (var ns in asm.Assembly.GetTypes().Select(x => x.Namespace).Distinct().Where(x => x != null)) + foreach (var ns in asm.GetTypes().Select(x => x.Namespace).Distinct().Where(x => x != null)) { data.Add(new NamespaceCompletionItem() { Name = ns, - Assembly = asm.Assembly.GetName().Name, + Assembly = asm.GetName().Name, }); } } @@ -1147,33 +1250,114 @@ namespace Tango.Scripting.Editors return popup; } - private void InvalidateHighlighting() + public static void LoadCachedAssemblies(List<Assembly> assemblies) + { + if (_isLoadingCachedAssemblies) return; + + _isLoadingCachedAssemblies = true; + + if (!_isCacheAssembliesLoaded) + { + foreach (var file in System.IO.Directory.GetFiles(KNOWN_TYPES_CACHE_FOLDER)) + { + try + { + AssemblyCacheProgress?.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 && !x.IsPrimitive).ToList(); + + int i = 0; + + foreach (var type in types) + { + AssemblyCacheProgress?.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); + _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)); + } + } + + _isLoadingCachedAssemblies = false; + } + + private void InvalidateHighlighting(bool loadKnownTypes = true) { if (!_isLoadingTypes) { _isLoadingTypes = true; - _knownTypes.Clear(); var assemblies = ReferenceAssemblies.ToList(); var usings = _current_usings.ToList(); Thread t = new Thread(() => { - foreach (var asm in assemblies.Select(x => x.Assembly)) + LoadCachedAssemblies(assemblies); + + if (loadKnownTypes) { - Parallel.ForEach(asm.GetTypes().Where(x => x.IsVisible && x.IsPublic && !x.IsPrimitive), (type) => + _knownTypes.Clear(); + + foreach (var asm in assemblies) { - if (usings.Exists(x => type.Namespace == x)) + foreach (var knownType in _knownTypesCache.ToList().Select(x => x.Value).ToList()) { - lock (_knownTypes) + if (usings.Exists(x => knownType.Type.Namespace == x)) { - if (!_knownTypes.Exists(x => x.Type.FullName == type.FullName)) + lock (_knownTypes) { - _knownTypes.Add(new KnownType(type)); + _knownTypes.Add(knownType); } } } - }); + } } if (_knownTypes.Count > 0 || _declaredTypes.Count > 0) @@ -1246,10 +1430,10 @@ namespace Tango.Scripting.Editors })); - foreach (var knownType in _knownTypes) - { - knownType.LoadDocumentation(); - } + //foreach (var knownType in _knownTypes) + //{ + // knownType.LoadDocumentation(); + //} } _isLoadingTypes = false; @@ -1263,10 +1447,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; @@ -1452,6 +1645,48 @@ 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; } @@ -1515,6 +1750,65 @@ namespace Tango.Scripting.Editors return null; } + private MethodSession GetStaticMethodSession() + { + var words = GetCurrentLineText().Split(' '); + + if (words.Count() > 0 && (words.First() == "private" || words.First() == "public" || words.First() == "void")) + { + return null; + } + + var expression = words.LastOrDefault(); + + if (expression != null) + { + int parameterIndex = expression.Count(x => x == ','); + expression = new string(expression.TakeWhile(x => x != '(').ToArray()); + + var tree = expression.Split('.').Select(x => x.Remove(@"\n|\r|\s|\t|\(|\)|\[|\]|<.*>")).ToList(); + var variableName = tree.FirstOrDefault(); + + if (variableName != null && tree.Count > 1) + { + tree.RemoveAt(0); + var variables = _parser.GetContextSymbols(Document.Text, CaretOffset); + var variable = variableName; + + if (variable != null) + { + var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable, "<.+>", "<T>")); + + if (knownType != null) + { + while (tree.Count > 1) + { + var memberName = tree.First(); + tree.RemoveAt(0); + var member = knownType.Members.FirstOrDefault(x => x.Name == memberName); + + if (member == null) + { + return null; + } + + knownType = _knownTypes.FirstOrDefault(x => x.Type.Namespace + "." + x.Type.Name == member.ReturnType.Namespace + "." + member.ReturnType.Name); + } + + return new MethodSession() + { + Type = knownType, + MethodName = tree.Last(), + ParameterIndex = parameterIndex, + }; + } + } + } + } + + return null; + } + private DeclaredMethodSession GetDeclaredMethodSession() { var words = GetCurrentLineText().Split(' '); |
