aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-04-19 23:12:56 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-04-19 23:12:56 +0300
commit9b7875d904456c34c3698d7fc569078f99ed5187 (patch)
treecb6ef38d0f2bde6b78d241e6a9a1ec4ba49ec82f /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs
parent6e172dd3bc3e2388e532fd43381896f15abaed70 (diff)
downloadTango-9b7875d904456c34c3698d7fc569078f99ed5187.tar.gz
Tango-9b7875d904456c34c3698d7fc569078f99ed5187.zip
Scripting refactoring.
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs348
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(' ');