diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.Stubs/Controls/ScriptEditorControl.xaml.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.Stubs/Controls/ScriptEditorControl.xaml.cs | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.Stubs/Controls/ScriptEditorControl.xaml.cs b/Software/Visual_Studio/Tango.Stubs/Controls/ScriptEditorControl.xaml.cs new file mode 100644 index 000000000..7628e5655 --- /dev/null +++ b/Software/Visual_Studio/Tango.Stubs/Controls/ScriptEditorControl.xaml.cs @@ -0,0 +1,488 @@ +using ICSharpCode.AvalonEdit.CodeCompletion; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Editing; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +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.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; +using Tango.SharedUI; +using Tango.SharedUI.Helpers; + +namespace Tango.Stubs.Controls +{ + /// <summary> + /// Represents a C# script editor control. + /// </summary> + /// <seealso cref="System.Windows.Controls.UserControl" /> + /// <seealso cref="System.Windows.Markup.IComponentConnector" /> + public partial class ScriptEditorControl : UserControl + { + #region Completion + + /// <summary> + /// Represents an auto complete item. + /// </summary> + /// <seealso cref="ICSharpCode.AvalonEdit.CodeCompletion.ICompletionData" /> + internal class CompletionData : ICompletionData + { + private String _description; + + /// <summary> + /// Gets or sets the icon source. + /// </summary> + public BitmapSource Source { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="CompletionData"/> class. + /// </summary> + /// <param name="text">The text.</param> + /// <param name="description">The description.</param> + public CompletionData(string text, String description) + { + this.Text = text; + _description = description; + } + + /// <summary> + /// Gets the image. + /// </summary> + public System.Windows.Media.ImageSource Image + { + get { return Source; } + } + + /// <summary> + /// Gets the text. This property is used to filter the list of visible elements. + /// </summary> + public string Text { get; private set; } + + // Use this property if you want to show a fancy UIElement in the drop down list. + public object Content + { + get { return this.Text; } + } + + /// <summary> + /// Gets the description. + /// </summary> + public object Description + { + get { return _description; } + } + + /// <summary> + /// Gets the priority. This property is used in the selection logic. You can use it to prefer selecting those items + /// which the user is accessing most frequently. + /// </summary> + public double Priority { get { return 0; } } + + /// <summary> + /// Perform the completion. + /// </summary> + /// <param name="textArea">The text area on which completion is performed.</param> + /// <param name="completionSegment">The text segment that was used by the completion window if + /// the user types (segment between CompletionWindow.StartOffset and CompletionWindow.EndOffset).</param> + /// <param name="insertionRequestEventArgs">The EventArgs used for the insertion request. + /// These can be TextCompositionEventArgs, KeyEventArgs, MouseEventArgs, depending on how + /// the insertion was triggered.</param> + public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs) + { + textArea.Document.Replace(completionSegment, this.Text); + } + + /// <summary> + /// Returns a <see cref="System.String" /> that represents this instance. + /// </summary> + /// <returns> + /// A <see cref="System.String" /> that represents this instance. + /// </returns> + public override string ToString() + { + return Text; + } + } + + #endregion + + private CompletionWindow completionWindow; //Holds the auto-complete window instance. + + #region Constructors + + /// <summary> + /// Initializes a new instance of the <see cref="ScriptEditorControl"/> class. + /// </summary> + public ScriptEditorControl() + { + InitializeComponent(); + + textEditor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.CSharp.CSharpIndentationStrategy(); + textEditor.TextArea.TextEntering += textEditor_TextArea_TextEntering; + textEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered; + + this.Loaded += ScriptEditorControl_Loaded; + } + + #endregion + + #region Event Handlers + + /// <summary> + /// Handles the TextEntered event of the textEditor_TextArea control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="TextCompositionEventArgs"/> instance containing the event data.</param> + private void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e) + { + if (e.Text == ".") + { + String keyword = textEditor.TextArea.GetJustCurrentWord(); + + if (keyword != null) + { + completionWindow = new CompletionWindow(textEditor.TextArea); + completionWindow.WindowStyle = WindowStyle.None; + completionWindow.AllowsTransparency = true; + completionWindow.ResizeMode = ResizeMode.NoResize; + + IList<ICompletionData> data = completionWindow.CompletionList.CompletionData; + + bool ok = false; + + List<KeyValuePair<String, Type>> types = new List<KeyValuePair<String, Type>>(); + + types.AddRange(IntellisenseTypes); + + + if (IntellisenseTypes != null) + { + ScriptParser parser = new ScriptParser(); + + try + { + var variables = parser.ParseScript(textEditor.Text); + + foreach (var v in variables) + { + var hT = IntellisenseTypes.SingleOrDefault(x => x.Key == v.Type); + + if (hT.Value != null) + { + types.Add(new KeyValuePair<string, Type>(v.Name, hT.Value)); + } + } + + } + catch { } + } + + KeyValuePair<String, Type> type = types.LastOrDefault(x => keyword == x.Key); + + if (type.Key != null) + { + ok = true; + FillType(type.Value, data); + } + + if (ok) + { + completionWindow.Show(); + completionWindow.Closed += delegate + { + completionWindow = null; + }; + } + } + } + } + + /// <summary> + /// Handles the TextEntering event of the textEditor_TextArea control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="TextCompositionEventArgs"/> instance containing the event data.</param> + private void textEditor_TextArea_TextEntering(object sender, TextCompositionEventArgs e) + { + if (e.Text.Length > 0 && completionWindow != null) + { + if (!char.IsLetterOrDigit(e.Text[0])) + { + // Whenever a non-letter is typed while the completion window is open, + // insert the currently selected element. + completionWindow.CompletionList.RequestInsertion(e); + } + } + } + + /// <summary> + /// Handles the TextChanged event of the textEditor 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 textEditor_TextChanged(object sender, EventArgs e) + { + Text = textEditor.Text; + } + + private void ScriptEditorControl_Loaded(object sender, RoutedEventArgs e) + { + if (HighlightTypes != null) + { + Stream xshd_stream = typeof(ScriptEditorControl).Assembly.GetManifestResourceStream("Tango.Stubs.CSharp-Mode.xshd"); + + String text = String.Empty; + + using (StreamReader reader = new StreamReader(xshd_stream)) + { + text = reader.ReadToEnd(); + } + + String code = String.Empty; + + + + foreach (var name in HighlightTypes.Select(x => x.Key)) + { + code += String.Format("<Word>{0}</Word>", name) + Environment.NewLine; + } + + text = text.Replace("@CUSTOM_TYPES@", code); + + using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(text))) + { + XmlTextReader xshd_reader = new XmlTextReader(ms); + textEditor.SyntaxHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.HighlightingLoader.Load(xshd_reader, ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance); + xshd_reader.Close(); + } + } + } + + #endregion + + #region Private Methods + + /// <summary> + /// Fills the type. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="data">The data.</param> + private void FillType(Type type, IList<ICompletionData> data) + { + List<CompletionData> items = new List<CompletionData>(); + + foreach (var method in type.GetMethods().Where(x => x.IsPublic && !x.IsSpecialName)) + { + String desc = method.ReturnType.Name + " " + method.Name + "(" + String.Join(", ", method.GetParameters().Select(x => x.ParameterType.Name + " " + x.Name).ToArray()) + ")"; + items.Add(new CompletionData(method.Name, desc) { Source = ResourceHelper.GetImageFromResources("Images/pubmethod.gif") }); + } + foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + String desc = property.PropertyType.Name + " " + property.Name; + items.Add(new CompletionData(property.Name, desc) { Source = ResourceHelper.GetImageFromResources("Images/pubproperty.gif") }); + } + foreach (var ev in type.GetEvents(BindingFlags.Instance | BindingFlags.Public)) + { + try + { + String desc = ev.Name + " " + "(" + String.Join(", ", ev.EventHandlerType.GetMethod("Invoke").GetParameters().Select(x => x.ParameterType.Name + " " + x.Name).ToArray()) + ")"; + items.Add(new CompletionData(ev.Name, desc) { Source = ResourceHelper.GetImageFromResources("Images/pubevent.gif") }); + } + catch { } + } + + foreach (var item in items.OrderBy(x => x.Text)) + { + data.Add(item); + } + } + + /// <summary> + /// Fills the assembly. + /// </summary> + /// <param name="asm">The asm.</param> + /// <param name="data">The data.</param> + private void FillAssembly(Assembly asm, IList<ICompletionData> data) + { + var q = from t in asm.GetTypes() + where t.IsClass + select t; + + foreach (var type in q) + { + data.Add(new CompletionData(type.Name, "Class") { Source = ResourceHelper.GetImageFromResources("Images/pubclass.gif") }); + } + } + + #endregion + + #region Properties + + /// <summary> + /// Gets or sets the text. + /// </summary> + public String Text + { + get { return (String)GetValue(TextProperty); } + set { SetValue(TextProperty, value); } + } + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(String), typeof(ScriptEditorControl), new PropertyMetadata(null, (d, e) => (d as ScriptEditorControl).OnTextChanged())); + + /// <summary> + /// Gets or sets the highlight types. + /// </summary> + public ObservableCollection<KeyValuePair<string, Type>> HighlightTypes + { + get { return (ObservableCollection<KeyValuePair<string, Type>>)GetValue(HighlightTypesProperty); } + set { SetValue(HighlightTypesProperty, value); } + } + public static readonly DependencyProperty HighlightTypesProperty = + DependencyProperty.Register("HighlightTypes", typeof(ObservableCollection<KeyValuePair<string, Type>>), typeof(ScriptEditorControl), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the intellisense types. + /// </summary> + public ObservableCollection<KeyValuePair<String,Type>> IntellisenseTypes + { + get { return (ObservableCollection<KeyValuePair<String,Type>>)GetValue(IntellisenseTypesProperty); } + set { SetValue(IntellisenseTypesProperty, value); } + } + public static readonly DependencyProperty IntellisenseTypesProperty = + DependencyProperty.Register("IntellisenseTypes", typeof(ObservableCollection<KeyValuePair<String,Type>>), typeof(ScriptEditorControl), new PropertyMetadata(null)); + + + #endregion + + #region Virtual Methods + + /// <summary> + /// Called when the text has changed. + /// </summary> + protected virtual void OnTextChanged() + { + if (textEditor.Text != Text) + { + textEditor.Text = Text; + } + } + + /// <summary> + /// Called when the insert script command has changed. + /// </summary> + protected virtual void OnInsertScriptCommandChanged() + { + if (InsertSnippetCommand != null) + { + InsertSnippetCommand.Executed += (x, snippet) => + { + textEditor.Document.Insert(textEditor.TextArea.Caret.Offset, snippet); + }; + } + } + + #endregion + + #region Commands + + /// <summary> + /// Gets or sets the run command. + /// </summary> + public RelayCommand RunCommand + { + get { return (RelayCommand)GetValue(RunCommandProperty); } + set { SetValue(RunCommandProperty, value); } + } + public static readonly DependencyProperty RunCommandProperty = + DependencyProperty.Register("RunCommand", typeof(RelayCommand), typeof(ScriptEditorControl), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the stop command. + /// </summary> + public RelayCommand StopCommand + { + get { return (RelayCommand)GetValue(StopCommandProperty); } + set { SetValue(StopCommandProperty, value); } + } + public static readonly DependencyProperty StopCommandProperty = + DependencyProperty.Register("StopCommand", typeof(RelayCommand), typeof(ScriptEditorControl), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the save command. + /// </summary> + public RelayCommand SaveCommand + { + get { return (RelayCommand)GetValue(SaveCommandProperty); } + set { SetValue(SaveCommandProperty, value); } + } + public static readonly DependencyProperty SaveCommandProperty = + DependencyProperty.Register("SaveCommand", typeof(RelayCommand), typeof(ScriptEditorControl), new PropertyMetadata(null)); + + /// <summary> + /// Gets or sets the insert snippet command. + /// </summary> + public RelayCommand<String> InsertSnippetCommand + { + get { return (RelayCommand<String>)GetValue(InsertSnippetCommandProperty); } + set { SetValue(InsertSnippetCommandProperty, value); } + } + public static readonly DependencyProperty InsertSnippetCommandProperty = + DependencyProperty.Register("InsertSnippetCommand", typeof(RelayCommand<String>), typeof(ScriptEditorControl), new PropertyMetadata(null, (d, e) => (d as ScriptEditorControl).OnInsertScriptCommandChanged())); + + #endregion + } + + internal static class DocumentUtils + { + private static Regex _wordRegex = new Regex(@"[^\W\d][\w]*(?<=\w)", RegexOptions.Compiled); + + public static string GetJustCurrentWord(this TextArea textArea) + { + try + { + DocumentLine line = textArea.Document.GetLineByNumber(textArea.Caret.Line); + if (line.Length == 0) + return null; + + int lineCaretPosition = textArea.Caret.Offset - line.Offset; + String l = textArea.Document.GetText(line); + + String trimmed = l.Remove(lineCaretPosition, l.Length - lineCaretPosition); + + return SplitToWords(trimmed).LastOrDefault(x => !String.IsNullOrWhiteSpace(x)); + } + catch + { + return null; + } + } + + public static List<String> SplitToWords(String text) + { + text = text.Replace(".", " "); + text = text.Replace("(", " "); + text = text.Replace(")", " "); + text = text.Replace(",", " "); + var punctuation = text.Where(Char.IsPunctuation).Distinct().ToArray(); + var words = text.Split().Select(x => x.Trim(punctuation)); + return words.ToList(); + } + } +} |
