diff options
| author | Victoria Plitt <Victoria.Plitt@twine-s.com> | 2019-04-08 13:49:55 +0300 |
|---|---|---|
| committer | Victoria Plitt <Victoria.Plitt@twine-s.com> | 2019-04-08 13:49:55 +0300 |
| commit | fc8a05358a92cc3c77c5f1e30d536807ef0614fd (patch) | |
| tree | c65f696ebd60f3790145721307c255e5a339923f /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | |
| parent | b4a71931ea52636c6b36376aa9d71697ccf73524 (diff) | |
| download | Tango-fc8a05358a92cc3c77c5f1e30d536807ef0614fd.tar.gz Tango-fc8a05358a92cc3c77c5f1e30d536807ef0614fd.zip | |
were added scripting projects
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs | 1595 |
1 files changed, 1595 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs new file mode 100644 index 000000000..efa1b087a --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/ScriptEditor.cs @@ -0,0 +1,1595 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Windows.Threading; +using System.Xml; +using Tango.Core.Commands; +using Tango.Scripting.Editors.CodeCompletion; +using Tango.Scripting.Editors.Document; +using Tango.Scripting.Editors.Editing; +using Tango.Scripting.Editors.Folding; +using Tango.Scripting.Editors.Highlighting; +using Tango.Scripting.Editors.Highlighting.Xshd; +using Tango.Scripting.Editors.Intellisense; +using Tango.Scripting.Editors.Popups; +using Tango.Scripting.Editors.Rendering; +using Tango.Scripting.Parsing; + +namespace Tango.Scripting.Editors +{ + public class ScriptEditor : TextEditor + { + private char[] word_separators = { ' ', '\t', '\n', '.', '(', ',', '-', '*', '/', '+', '$', '=', '<', '>' }; + private string[] _blocking_type_words = { "class", "void" }; + + private DispatcherTimer _update_timer; + private Popup _popup; + private FoldingManager foldingManager; + private BraceFoldingStrategy foldingStrategy; + private CompletionWindow completionWindow; + private ScriptParser _parser; + private List<String> _current_usings; + private List<KnownType> _knownTypes; + private List<ScriptType> _declaredTypes; + private bool _isLoadingTypes; + + #region Mini Classes + + private class ScriptClass + { + public String Name { get; set; } + public int Index { get; set; } + } + + private class ConstructionSession + { + public KnownType Type { get; set; } + public int ParameterIndex { get; set; } + public List<String> TypeArguments { get; set; } + } + + private class MethodSession + { + public KnownType Type { get; set; } + public String MethodName { get; set; } + public int ParameterIndex { get; set; } + } + + private class DeclaredMethodSession + { + public ScriptType Type { get; set; } + public ScriptSymbol Method { get; set; } + } + + #endregion + + #region Properties + + /// <summary> + /// Gets or sets a value indicating whether to enable folding. + /// </summary> + public bool EnableFolding + { + get { return (bool)GetValue(EnableFoldingProperty); } + set { SetValue(EnableFoldingProperty, value); } + } + public static readonly DependencyProperty EnableFoldingProperty = + DependencyProperty.Register("EnableFolding", typeof(bool), typeof(ScriptEditor), new PropertyMetadata(true, (d, e) => (d as ScriptEditor).OnEnableFoldingChanged())); + + /// <summary> + /// + /// </summary> + public RelayCommand IndentCommand + { + get { return (RelayCommand)GetValue(IndentCommandProperty); } + set { SetValue(IndentCommandProperty, value); } + } + public static readonly DependencyProperty IndentCommandProperty = + DependencyProperty.Register("IndentCommand", typeof(RelayCommand), typeof(ScriptEditor), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the reference assemblies. + /// </summary> + public ObservableCollection<ReferenceAssembly> ReferenceAssemblies + { + get { return (ObservableCollection<ReferenceAssembly>)GetValue(ReferenceAssembliesProperty); } + set { SetValue(ReferenceAssembliesProperty, value); } + } + public static readonly DependencyProperty ReferenceAssembliesProperty = + DependencyProperty.Register("ReferenceAssemblies", typeof(ObservableCollection<ReferenceAssembly>), typeof(ScriptEditor), new PropertyMetadata(null)); + + public Object CurrentPopupContent + { + get { return (Object)GetValue(CurrentPopupContentProperty); } + set { SetValue(CurrentPopupContentProperty, value); } + } + public static readonly DependencyProperty CurrentPopupContentProperty = + DependencyProperty.Register("CurrentPopupContent", typeof(Object), typeof(ScriptEditor), new PropertyMetadata(null)); + + + #endregion + + #region Constructors + + /// <summary> + /// Initializes the <see cref="ScriptEditor"/> class. + /// </summary> + static ScriptEditor() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptEditor), new FrameworkPropertyMetadata(typeof(ScriptEditor))); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ScriptEditor"/> class. + /// </summary> + public ScriptEditor() + { + _declaredTypes = new List<ScriptType>(); + + _current_usings = new List<string>(); + + 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 + + _knownTypes = new List<KnownType>(); + _parser = new ScriptParser(); + + TextArea.IndentationStrategy = new Indentation.CSharp.CSharpIndentationStrategy(Options); + foldingStrategy = new BraceFoldingStrategy(); + + _update_timer = new DispatcherTimer(); + _update_timer.Interval = TimeSpan.FromSeconds(2); + _update_timer.Tick += UpdateTimer_Tick; + _update_timer.Start(); + + IndentCommand = new RelayCommand(IndentCode); + + TextArea.TextEntered += TextArea_TextEntered; + + completionWindow = new CompletionWindow(TextArea); + completionWindow.WindowStyle = WindowStyle.None; + completionWindow.AllowsTransparency = true; + completionWindow.ResizeMode = ResizeMode.NoResize; + completionWindow.InsertionRequest += CompletionWindow_InsertionRequest; + } + + #endregion + + #region Update Time Tick + + /// <summary> + /// Handles the Tick event of the UpdateTimer control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> + private void UpdateTimer_Tick(object sender, EventArgs e) + { + InvalidateFolding(); + InvalidateUsings(); + InvalidateScriptTypesHighlightings(); + } + + #endregion + + #region Properties Changes + + private void OnEnableFoldingChanged() + { + if (EnableFolding) + { + foldingManager = FoldingManager.Install(TextArea); + } + else + { + FoldingManager.Uninstall(foldingManager); + } + } + + #endregion + + #region Override Methods + + /// <summary> + /// Invoked when an unhandled <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// </summary> + /// <param name="e">The <see cref="T:System.Windows.Input.KeyEventArgs" /> that contains the event data.</param> + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if (CurrentPopupContent is MethodPopup && (e.Key == Key.Down || e.Key == Key.Up)) + { + e.Handled = true; + + if (e.Key == Key.Down) + { + (CurrentPopupContent as MethodPopup).IncrementMethod(); + } + else if (e.Key == Key.Up) + { + (CurrentPopupContent as MethodPopup).DecrementMethod(); + } + + return; + } + + base.OnPreviewKeyDown(e); + HidePopup(); + HandleKeyCombinations(e); + } + + #endregion + + #region Apply Template + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _popup = GetTemplateChild("PART_popup") as Popup; + } + + #endregion + + #region Key Combination Handling + + /// <summary> + /// Handles the key combinations. + /// </summary> + /// <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param> + private void HandleKeyCombinations(KeyEventArgs e) + { + if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.K) && Keyboard.IsKeyDown(Key.D)) + { + try + { + int index = CaretOffset; + Document.BeginUpdate(); + IndentCode(); + Document.EndUpdate(); + e.Handled = true; + CaretOffset = index; + } + catch + { + Debug.WriteLine("Error indenting code."); + } + } + else if (e.Key == Key.Oem2) + { + 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; + } + } + else if (e.Key == Key.End || e.Key == Key.Home) + { + HideCompletionWindow(); + } + } + + #endregion + + #region Text Entered + + private void TextArea_TextEntered(object sender, TextCompositionEventArgs e) + { + List<Object> items = new List<object>(); + + HidePopup(); + + 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 (previousWords.Count > 0 && previousWords.First().Trim().StartsWith("//")) return; + + if (e.Text == " " && previousWords.Count > 2 && previousWords[previousWords.Count - 2] == "=") + { + var expression = previousWords.First(); + var knownType = GetKnownTypeFromExpression(expression + "."); + + if (knownType != null && knownType.Type.IsEnum) + { + completionWindow.HideCompletion(); + IList<ICompletionData> data = new List<ICompletionData>(); + + foreach (var field in knownType.Fields) + { + data.Add(new FieldCompletionItem() + { + Class = knownType.FriendlyName, + Name = knownType.FriendlyName + "." + field.Name, + Type = field.ReturnTypeFriendlyName, + Description = field.Summary, + }); + } + + ShowCompletionWindow(data.OrderBy(x => x.Text).ToList(), GetCurrentWord()); + } + + } + else if (e.Text == " " && GetPreviousWord() == "new") + { + var s = _parser.GetExpressionFirst<FieldDeclarationSyntax>(GetCurrentLineText()); + + if (s != null) + { + String type = s.Declaration.Type.ToString(); + + IList<ICompletionData> data = new List<ICompletionData>(); + + data.Add(new ClassCompletionItem() + { + Name = type, + Description = "Auto generate assignment...", + }); + + ShowCompletionWindow(data, type); + } + } + else if (e.Text == ";" || e.Text == " ") + { + HideCompletionWindow(); + } + else if (e.Text == ".") + { + var knownType = GetCurrentKnownType(); + + 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)) + { + 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, + }); + } + + foreach (var methodGroup in typeMembers.Where(x => x.GetType() != typeof(KnownTypeMethod)).GroupBy(x => x.Name)) + { + var member = methodGroup.First(); + + data.Add(new PropertyCompletionItem() + { + Class = knownType.FriendlyName, + Name = member.Name, + Type = member.ReturnTypeFriendlyName, + Description = member.Summary, + }); + + } + } + else + { + foreach (var field in knownType.Fields) + { + data.Add(new FieldCompletionItem() + { + Class = 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) + { + completionWindow.HideCompletion(); + IList<ICompletionData> data = new List<ICompletionData>(); + + var typeMembers = declaredType.Symbols.ToList(); + + foreach (var methodGroup in typeMembers.GroupBy(x => x.Name)) + { + var member = methodGroup.First(); + + 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, + }; + + + for (int i = 0; i < member.Parameters.Count; i++) + { + var pair = member.Parameters[i]; + + methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + { + Type = pair.Key, + Name = pair.Value, + IsLast = (i == member.Parameters.Count - 1) + }); + } + + data.Add(methodCompletion); + + } + else if (member.Kind == SymbolKind.Property) + { + data.Add(new PropertyCompletionItem() + { + Class = declaredType.Name, + Name = member.Name, + Type = member.Type, + Description = member.Summary, + }); + } + else if (member.Kind == SymbolKind.Field) + { + data.Add(new FieldCompletionItem() + { + Class = declaredType.Name, + Name = member.Name, + Type = member.Type, + Description = member.Summary, + }); + } + } + + ShowCompletionWindow(data, GetCurrentWord()); + } + } + } + else if (e.Text == "(" || e.Text == ",") + { + completionWindow.HideCompletion(); + + try + { + var session = GetConstructionSession(); + + if (session != null) + { + var content = CreateConstructionSessionPopupContent(session); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } + } + + var methodSession = GetMethodSession(); + + if (methodSession != null) + { + var content = CreateMethodSessionPopupContent(methodSession); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } + } + + var declaredMethodSession = GetDeclaredMethodSession(); + + if (declaredMethodSession != null) + { + var content = CreateDeclaredMethodSessionPopupContent(declaredMethodSession); + if (content.Methods.Count > 0) + { + ShowPopup(content); + return; + } + } + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + else if (lineText.StartsWith("using")) + { + if (completionWindow.IsVisible) + { + completionWindow.UpdatePositionFix(); + return; + } + + 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)) + { + data.Add(new NamespaceCompletionItem() + { + Name = ns, + Assembly = asm.Assembly.GetName().Name, + }); + } + } + + 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--; + } + } + else if (e.Text == "}") + { + if (Document.GetText(CaretOffset - 2, 1) == "{" && Document.GetText(CaretOffset, 1) == "}") + { + 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"); + } + } + else if (!currentWordIncludingParenthesis.Contains(".") || currentWord[currentWord.Length - 2] == '<') + { + if (completionWindow.IsVisible) + { + completionWindow.UpdatePositionFix(); + return; + } + + var previous_word = GetPreviousWord(); + var word = GetCurrentWord(); + + if (word.Contains("<")) + { + word = word.Last(x => x != '<').ToString(); + } + + if (previous_word != word) + { + if (_knownTypes.Exists(x => x.Name == previous_word)) + { + return; + } + + if (_blocking_type_words.Contains(previous_word)) + { + return; + } + } + + if (!String.IsNullOrWhiteSpace(word)) + { + IList<ICompletionData> data = new List<ICompletionData>(); + + foreach (var type in _declaredTypes.Where(x => x.Name.StartsWith(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) + { + 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 type in _knownTypes.ToList().Where(x => x.Name.StartsWith(word))) + { + if (type.Type.IsEnum) + { + data.Add(new EnumCompletionItem() + { + Namespace = type.Type.Namespace, + Description = type.Summary, + Name = type.FriendlyName, + Priority = 0, + }); + } + else if (type.Type.IsInterface) + { + 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."); + } + } + + foreach (var symbol in _parser.GetContextSymbols(Document.Text, CaretOffset).Where(x => x.Name.StartsWith(GetCurrentWord()))) + { + if (symbol.Kind == SymbolKind.Property) + { + 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) + { + data.Add(new FieldCompletionItem() + { + 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]; + + methodCompletion.Parameters.Add(new KnownTypeMethodParameter() + { + Type = pair.Key, + Name = pair.Value, + IsLast = (i == symbol.Parameters.Count - 1) + }); + } + + data.Add(methodCompletion); + } + } + + ShowCompletionWindow(data, word); + } + } + } + + #endregion + + #region Completion Window Insertion + + private void CompletionWindow_InsertionRequest(ICompletionData item) + { + item.Complete(this); + } + + #endregion + + #region Private/Internal Methods + + private void ShowCompletionWindow(IList<ICompletionData> suggestions, String filter) + { + IList<ICompletionData> data = completionWindow.CompletionList.CompletionData; + data.Clear(); + + foreach (var item in suggestions) + { + data.Add(item); + } + + if (data.Count > 0) + { + completionWindow.ShowCompletion(); + + if (completionWindow.CompletionList.ListBox != null) + { + completionWindow.CompletionList.SelectItemFiltering(filter); + } + } + else + { + completionWindow.HideCompletion(); + } + } + + private void HideCompletionWindow() + { + completionWindow.HideCompletion(); + } + + private List<String> GetScriptClassNames() + { + List<String> list = new List<String>(); + + Regex r = new Regex(@"(class\s+)(\w+)"); + var matches = r.Matches(Text); + + foreach (var m in matches.OfType<Match>()) + { + var g = m.Groups.OfType<Group>().Last(); + list.Add(g.Value); + } + + return list.Distinct().ToList(); + } + + private List<String> GetScriptInterfaceOfEnumNames() + { + List<String> list = new List<String>(); + + Regex r = new Regex(@"((enum|interface)\s+)(\w+)"); + var matches = r.Matches(Text); + + foreach (var m in matches.OfType<Match>()) + { + var g = m.Groups.OfType<Group>().Last(); + list.Add(g.Value); + } + + return list.Distinct().ToList(); + } + + private List<String> GetScriptEnumsAndInterfaces() + { + List<String> list = new List<string>(); + + Regex r = new Regex(@"((public|private|internal)\s+(enum|interface)\s+\w+)"); + var matches = r.Matches(Text); + + foreach (var m in matches) + { + + } + + return list; + } + + private KnownType GetCurrentKnownType() + { + var expression = GetPreviousWords().LastOrDefault(); + return GetKnownTypeFromExpression(expression); + } + + private KnownType GetKnownTypeFromExpression(String expression) + { + if (expression != null) + { + var tree = expression.Split('.').Select(x => x.Remove(@"\n|\t|\r|\(.*\)|\[.*\]|\s")).ToList(); + var variableName = tree.FirstOrDefault(); + + if (variableName != null) + { + //Search for enum type first + var enumType = _knownTypes.FirstOrDefault(x => x.Name == variableName && x.Type.IsEnum); + + if (enumType != null) + { + return enumType; + } + + tree.RemoveAt(0); + var variables = _parser.GetContextSymbols(Document.Text, CaretOffset); + var variable = variables.FirstOrDefault(x => x.Name == variableName); + + if (variable != null) + { + var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable.Type, "<.+>", "<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 knownType; + } + } + } + } + + return null; + } + + private ScriptType GetCurrentDeclaredType() + { + var expression = GetPreviousWords().LastOrDefault(); + + if (expression != null) + { + var tree = expression.Split('.').Select(x => x.Remove(@"\n|\t|\r|\(.*\)|\[.*\]|\s")).ToList(); + var variableName = tree.FirstOrDefault(); + + if (variableName != null) + { + tree.RemoveAt(0); + var variables = _parser.GetContextSymbols(Document.Text, CaretOffset); + var variable = variables.FirstOrDefault(x => x.Name == variableName); + + if (variable != null) + { + var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Type, "<.+>", "<T>")); + + if (declaredType != null) + { + while (tree.Count > 1) + { + var memberName = tree.First(); + tree.RemoveAt(0); + var member = declaredType.Symbols.FirstOrDefault(x => x.Name == memberName); + + if (member == null) + { + return null; + } + + declaredType = _declaredTypes.FirstOrDefault(x => x.ContainingNamespace + "." + x.Name == member.ContainingNamespace + "." + member.Type); + } + + return declaredType; + } + } + } + } + + return null; + } + + private void ShowPopup(Object content) + { + var position = TextArea.Caret.Position; + var textView = TextArea.TextView; + + var visualLocation = textView.GetVisualPosition(position, VisualYPosition.LineBottom); + var visualLocationTop = textView.GetVisualPosition(position, VisualYPosition.LineTop); + + Point location = textView.PointToScreen(visualLocation - textView.ScrollOffset); + Point locationTop = textView.PointToScreen(visualLocationTop - textView.ScrollOffset); + + _popup.Placement = PlacementMode.Absolute; + _popup.PlacementRectangle = new Rect(location, new Size(200, 100)); + + CurrentPopupContent = content; + + _popup.IsOpen = true; + } + + private void HidePopup() + { + _popup.IsOpen = false; + CurrentPopupContent = null; + } + + private MethodPopup CreateConstructionSessionPopupContent(ConstructionSession session) + { + MethodPopup popup = new MethodPopup(); + + foreach (var c in session.Type.Constructors) + { + MethodDescription method = new MethodDescription(); + method.ReturnType = session.Type.Name; + method.Description = c.Summary; + + if (session.Type.Type.IsGenericType && session.TypeArguments != null) + { + method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>"; + } + + var parameters = c.Parameters; + + foreach (var p in parameters) + { + ParameterDescription pDescription = new ParameterDescription(method); + pDescription.Name = p.Name; + pDescription.Type = p.Type; + pDescription.Description = p.Description; + method.Parameters.Add(pDescription); + } + + popup.Methods.Add(method); + } + + if (session.ParameterIndex > 0) + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1); + + if (popup.CurrentMethod == null) + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(); + } + } + else + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(); + } + + if (popup.CurrentMethod != null) + { + popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1; + } + + return popup; + } + + private MethodPopup CreateMethodSessionPopupContent(MethodSession session) + { + MethodPopup popup = new MethodPopup(); + + foreach (var m in session.Type.Methods.Where(x => x.Name == session.MethodName)) + { + MethodDescription method = new MethodDescription(); + method.ReturnType = session.Type.Name; + method.Description = m.Summary; + method.Name = m.NameWithTypeArguments; + method.Class = session.Type.FriendlyName; + + //if (session.Type.Type.IsGenericType && session.TypeArguments != null) + //{ + // method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>"; + //} + + var parameters = m.Parameters; + + foreach (var p in parameters) + { + ParameterDescription pDescription = new ParameterDescription(method); + pDescription.Name = p.Name; + pDescription.Type = p.Type; + pDescription.Description = p.Description; + method.Parameters.Add(pDescription); + } + + popup.Methods.Add(method); + } + + if (session.ParameterIndex > 0) + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1); + + if (popup.CurrentMethod == null) + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(); + } + } + else + { + popup.CurrentMethod = popup.Methods.FirstOrDefault(); + } + + if (popup.CurrentMethod != null) + { + popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1; + } + + return popup; + } + + private MethodPopup CreateDeclaredMethodSessionPopupContent(DeclaredMethodSession session) + { + MethodPopup popup = new MethodPopup(); + + MethodDescription method = new MethodDescription(); + method.ReturnType = session.Method.Type; + method.Description = session.Method.Summary; + method.Name = session.Method.Name; + method.Class = session.Method.Class; + + //if (session.Type.Type.IsGenericType && session.TypeArguments != null) + //{ + // method.ReturnType = new String(session.Type.Name.TakeWhile(x => x != '`').ToArray()) + $"<{String.Join(",", session.TypeArguments)}>"; + //} + + foreach (var p in session.Method.Parameters) + { + ParameterDescription pDescription = new ParameterDescription(method); + pDescription.Type = p.Key; + pDescription.Name = p.Value; + method.Parameters.Add(pDescription); + } + + popup.Methods.Add(method); + + //if (false) + //{ + // popup.CurrentMethod = popup.Methods.FirstOrDefault(x => x.Parameters.Count == session.ParameterIndex + 1); + + // if (popup.CurrentMethod == null) + // { + // popup.CurrentMethod = popup.Methods.FirstOrDefault(); + // } + //} + //else + //{ + popup.CurrentMethod = popup.Methods.FirstOrDefault(); + //} + + if (popup.CurrentMethod != null) + { + popup.CurrentMethodIndex = popup.Methods.IndexOf(popup.CurrentMethod) + 1; + } + + return popup; + } + + private void InvalidateHighlighting() + { + 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)) + { + Parallel.ForEach(asm.GetTypes().Where(x => x.IsVisible && x.IsPublic && !x.IsPrimitive), (type) => + { + if (usings.Exists(x => type.Namespace == x)) + { + lock (_knownTypes) + { + if (!_knownTypes.Exists(x => x.Type.FullName == type.FullName)) + { + _knownTypes.Add(new KnownType(type)); + } + } + } + }); + } + + 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.BeginInvoke(new Action(() => + { + SyntaxHighlighting = HighlightingLoader.Load(xshd_reader, HighlightingManager.Instance); + xshd_reader.Close(); + ms.Dispose(); + })); + + + foreach (var knownType in _knownTypes) + { + knownType.LoadDocumentation(); + } + } + + _isLoadingTypes = false; + }); + t.IsBackground = true; + t.Start(); + } + } + + private void InvalidateScriptTypesHighlightings() + { + var declaredTypes = _parser.GetDeclaredTypes(Text); + + if (declaredTypes.Exists(x => !_declaredTypes.Exists(y => y.Name == x.Name)) || _declaredTypes.Exists(x => !declaredTypes.Exists(y => y.Name == x.Name))) + { + _declaredTypes = declaredTypes; + InvalidateHighlighting(); + } + + _declaredTypes = declaredTypes; + + //for (int i = 0; i < TextArea.TextView.LineTransformers.Count; i++) + //{ + // if (TextArea.TextView.LineTransformers[i] is OffsetColorizer) + // { + // TextArea.TextView.LineTransformers.RemoveAt(i); + // } + //} + + //foreach (var cls in scriptClasses) + //{ + // Document.BeginUpdate(); + + // var line = Document.GetLineByOffset(cls.Index); + + // OffsetColorizer colorizer = new OffsetColorizer(line, cls.Index, cls.Index + cls.Name.Length, Brushes.Red); + // TextArea.TextView.LineTransformers.Add(colorizer); + + // Document.EndUpdate(); + //} + } + + private void InvalidateUsings() + { + var oldUsings = _current_usings.ToList(); + _current_usings = _parser.GetUsings(Text); + + if (_current_usings.Exists(x => !oldUsings.Exists(y => y == x)) || oldUsings.Exists(x => !_current_usings.Exists(y => y == x))) + { + InvalidateHighlighting(); + } + } + + private void InvalidateFolding() + { + if (EnableFolding) + { + if (foldingManager == null) + { + foldingManager = FoldingManager.Install(TextArea); + } + + foldingStrategy.UpdateFoldings(foldingManager, Document); + } + } + + private void IndentCode() + { + Text = Indentation.CSharp.CSharpIndentationHelper.IndentCSharpCode(Text); + //Text = _parser.IndentCSharpCode(Text); + } + + internal DocumentLine GetCurrentLine() + { + int offset = CaretOffset; + var line = Document.GetLineByOffset(offset); + return line; + } + + private String GetCurrentLineText() + { + var text = Document.GetText(GetCurrentLine()); + return text; + } + + internal String GetCurrentWord() + { + return GetWordByEndIndex(CaretOffset); + } + + private String GetWordByEndIndex(int index) + { + String word = String.Empty; + var line = GetCurrentLine(); + + int position = index; + + for (int i = position - 1; i >= line.Offset; i--) + { + char c = Document.GetText(i, 1).First(); + + if (word_separators.Contains(c)) + { + break; + } + + word += c; + } + + word = new string(word.Reverse().ToArray()); + + if (word.Length > 0) + { + word = word.Replace(".", ""); + } + + return word; + } + + internal int GetCurrentWordStartIndex() + { + var line = GetCurrentLine(); + + int position = CaretOffset; + + for (int i = position - 1; i >= line.Offset; i--) + { + char c = Document.GetText(i, 1).First(); + + if (word_separators.Contains(c)) + { + return i + 1; + } + } + + return line.Offset; + } + + private String GetPreviousWord() + { + int index = GetCurrentWordStartIndex() - 1; + return GetWordByEndIndex(index); + } + + private ConstructionSession GetConstructionSession() + { + var expression = _parser.GetCurrentConstructionExpression(GetCurrentLineText()); + + if (expression != 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 == "(") + { + var typeDeclaration = expression.Type as GenericNameSyntax; + + KnownType type = null; + + if (typeDeclaration != null) + { + var typeName = typeDeclaration.Identifier.ToString(); + var argumentsCount = typeDeclaration.TypeArgumentList.Arguments.Count; + session.TypeArguments = typeDeclaration.TypeArgumentList.Arguments.Select(x => x.ToString()).ToList(); + + if (argumentsCount == 0) + { + type = _knownTypes.FirstOrDefault(x => x.Type.Name == expression.Type.ToString()); + } + else + { + type = _knownTypes.FirstOrDefault(x => x.Type.Name == typeName + "`" + argumentsCount); + } + } + else + { + type = _knownTypes.FirstOrDefault(x => x.Name == expression.Type.ToString()); + } + + 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(' '); + + 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 = variables.FirstOrDefault(x => x.Name == variableName); + + if (variable != null) + { + var knownType = _knownTypes.FirstOrDefault(x => x.FriendlyName == Regex.Replace(variable.Type, "<.+>", "<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(' '); + + if (words.Count() > 0 && (words.First() == "private" || words.First() == "public" || words.First() == "void")) + { + return null; + } + + var expression = GetPreviousWords().LastOrDefault(); + + if (expression != null) + { + var tree = expression.Split('.').Select(x => x.Replace("\n", "").Replace("\r", "").Replace(" ", "").Replace("\t", "").Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "")).ToList(); + var variableName = tree.FirstOrDefault(); + + if (variableName != null && tree.Count > 0) + { + tree.RemoveAt(0); + var variables = _parser.GetContextSymbols(Document.Text, CaretOffset); + var variable = variables.FirstOrDefault(x => x.Name == variableName); + + if (variable != null) + { + var declaredType = _declaredTypes.FirstOrDefault(x => x.Name == Regex.Replace(variable.Class, "<.+>", "<T>")); + + if (declaredType != null) + { + while (tree.Count > 1) + { + var memberName = tree.First(); + tree.RemoveAt(0); + var member = declaredType.Symbols.FirstOrDefault(x => x.Name == memberName); + + if (member == null) + { + return null; + } + + declaredType = _declaredTypes.FirstOrDefault(x => x.ContainingNamespace + "." + x.Name == member.ContainingNamespace + "." + member.Type); + } + + var methodName = tree.Count > 0 ? tree.Last() : variableName; + + var method = declaredType.Symbols.FirstOrDefault(x => x.Kind == SymbolKind.Method && x.Name == methodName); + + if (method != null) + { + return new DeclaredMethodSession() + { + Type = declaredType, + Method = method, + }; + } + } + else if (tree.Count == 0) + { + var method = variable; + + if (method != null) + { + return new DeclaredMethodSession() + { + Type = declaredType, + Method = method, + }; + } + } + } + } + } + + return null; + } + + private List<String> GetPreviousWords() + { + var currentLine = GetCurrentLine(); + var currentText = Document.GetText(currentLine.Offset, CaretOffset - currentLine.Offset); + return currentText.Split(' ', ',').ToList(); + } + + #endregion + } +} |
