1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<Application x:Class="Tango.Scripting.IDE.UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tango.Scripting.IDE.UI"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Styles.xaml" />
<ResourceDictionary Source="pack://application:,,,/Tango.Scripting.IDE;component/Resources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
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
}
}
|