using ICSharpCode.AvalonEdit.CodeCompletion; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Folding; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; 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 TeamAAS_VP.Core; using TeamAAS_VP.Models; using TeamAAS_VP.ViewModels.DebugMod; using Prism.Commands; using TeamAAS_VP; using TeamAAS_VP.Resources.Languages; namespace TeamAAS_VP.Controls { /// /// ScriptEditControl.xaml 的交互逻辑 /// public partial class ScriptEditControl : UserControl { /// /// 当前app的所有程序集中的类 /// Dictionary ClassMemberlist = new Dictionary(); #region 属性 private double _fontSize = 14; public new double FontSize { get { return _fontSize; } set { _fontSize = value; this.textEditor.FontSize = value; } } private TextDocument _document = new TextDocument(); public TextDocument Document { get { return _document; } set { _document = value; } } private TextArea _mTextArea; public TextArea mTextArea { get { return _mTextArea; } set { _mTextArea = value; } } private ObservableCollection _CompileMessageList = new ObservableCollection(); public ObservableCollection CompileMessageList { get { return _CompileMessageList; } set { _CompileMessageList = value; this.messagelist.ItemsSource = value; } } private bool _IsCompile = true; public bool IsCompile { get { return _IsCompile; } set { _IsCompile = value; this.Dispatcher.Invoke(new Action(() => { this.btn1.IsEnabled = value; this.btn2.IsEnabled = value; this.btn3.IsEnabled = value; this.btn4.IsEnabled = value; this.btn5.IsEnabled = value; this.btn6.IsEnabled = value; this.btn7.IsEnabled = value; this.btn8.IsEnabled = value; this.btn9.IsEnabled = value; this.btn10.IsEnabled = value; this.btn11.IsEnabled = value; this.btn12.IsEnabled = value; this.btnShowEndOfLine.IsEnabled= value; this.btnShowLineNumbers.IsEnabled= value; this.btnWordWrap.IsEnabled= value; this.textEditor.IsEnabled= value; })); } } [Bindable(true)] [Category("Common")] [DefaultValue("")] public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } // Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string),new PropertyChangedCallback(OnValueChanged))); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is ScriptEditControl control) { if (e.NewValue!=e.OldValue) { if (!control.isWrite) { control.textEditor.Document.Text = (string)e.NewValue; } } } } [Bindable(true)] [Category("Common")] [DefaultValue("")] public string ScriptPath { get { return (string)GetValue(ScriptPathProperty); } set { SetValue(ScriptPathProperty, value); } } // Using a DependencyProperty as the backing store for ScriptPath. This enables animation, styling, binding, etc... public static readonly DependencyProperty ScriptPathProperty = DependencyProperty.Register("ScriptPath", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string), new PropertyChangedCallback(OnPathChanged))); private static void OnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is ScriptEditControl control) { if (e.NewValue != e.OldValue) { } } } [Bindable(true)] [Category("Common")] [DefaultValue("")] public string CompileType { get { return (string)GetValue(CompileTypeProperty); } set { SetValue(CompileTypeProperty, value); } } // Using a DependencyProperty as the backing store for CompileType. This enables animation, styling, binding, etc... public static readonly DependencyProperty CompileTypeProperty = DependencyProperty.Register("CompileType", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string))); #endregion #region 命令 #endregion #region 事件 public static readonly RoutedEvent SaveScriptEvent=EventManager.RegisterRoutedEvent("OnSaveScript", RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ScriptEditControl)); public event RoutedEventHandler OnSaveScript { add { AddHandler(SaveScriptEvent, value);} remove { RemoveHandler(SaveScriptEvent, value);} } #endregion public ScriptEditControl() { InitializeComponent(); this.Loaded += ScriptEditControl_Loaded; this.textEditor.PreviewMouseWheel += TextEditor_PreviewMouseWheel; this.messagelist.SelectionChanged += Messagelist_SelectionChanged; this.textEditor.Document = Document; this.textEditor.Document.TextChanged += Document_TextChanged; this.messagelist.ItemsSource= new ObservableCollection(); } bool isWrite = false; private void Document_TextChanged(object sender, EventArgs e) { isWrite = true; Text = this.textEditor.Document.Text; isWrite = false; } #region 方法 bool isFirst=true; private async void ScriptEditControl_Loaded(object sender, RoutedEventArgs e) { if (isFirst) { textEditor.Options.HighlightCurrentLine = true; mTextArea = textEditor.TextArea; textEditor.TextArea.TextEntering += TextArea_TextEntering; textEditor.TextArea.TextEntered += TextArea_TextEntered; //textEditor.PreviewMouseWheel += vm.TextEditor_PreviewMouseWheel; ICSharpCode.AvalonEdit.Search.SearchPanel.Install(textEditor); foldingManager = FoldingManager.Install(textEditor.TextArea); foldingStrategy = new BraceFoldingStrategy(); DispatcherTimer foldingUpdateTimer = new DispatcherTimer(); foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2); foldingUpdateTimer.Tick += delegate { UpdateFoldings(); }; foldingUpdateTimer.Start(); await Task.Run(() => { try { var cc = GetTypesInNamespace(Assembly.GetExecutingAssembly(), "TeamAAS_VP"); foreach (var item in GetTypesInNamespace(Assembly.GetExecutingAssembly(), "TeamAAS_VP")) { if (!item.IsPublic) { continue; } ClassMemberlist.Add(item.Name, item); } //获取当前应用域中加载的所有程序集数组 Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var item in assemblies) { try { foreach (var item1 in GetTypesInNamespace(Assembly.Load(item.FullName))) { if (!item1.IsPublic) { continue; } ClassMemberlist.Add(item1.Name, item1); } } catch (Exception) { } } } catch (Exception) { } }); isFirst = false; } } /// /// 打开脚本文件 /// /// /// private void Button_Click(object sender, RoutedEventArgs e) { try { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = Lang.打开脚本; dlg.Filter = $"{Lang.脚本文件}|*.cs|{Lang.所有文件}.*|*.*"; dlg.CheckFileExists = true; if (dlg.ShowDialog() ?? false) { this.textEditor.Document.Text = FileHelper.ReadFile(dlg.FileName); } } catch (Exception) { } } /// /// 保存脚本 /// /// /// private void Button_Click_1(object sender, RoutedEventArgs e) { try { if (string.IsNullOrEmpty(ScriptPath)) { return; } FileHelper.WriteFile(this.textEditor.Document.Text, ScriptPath); RoutedEventArgs args = new RoutedEventArgs(ScriptEditControl.SaveScriptEvent, Text); RaiseEvent(args); MessageBox.Show("Succed!"); } catch (Exception ex) { LogHelper.WriteLogError(Lang.保存脚本文件时出错,ex); MessageBox.Show(ex.Message); } } /// /// 另存为 /// /// /// private void Button_Click_2(object sender, RoutedEventArgs e) { try { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = $"{Lang.保存脚本}"; dlg.DefaultExt = ".cs"; dlg.Filter = $"C# {Lang.文件} (*.cs)|*.cs|{Lang.所有文件} (*.txt)|*.txt|All files (*.*)|*.*"; if (dlg.ShowDialog() ?? false) { FileHelper.WriteFile(this.textEditor.Document.Text, dlg.FileName); } } catch (Exception) { } } /// /// 字体放大 /// /// /// private void Button_Click_3(object sender, RoutedEventArgs e) { if (FontSize < 50) { FontSize += 1; } } /// /// 字体缩小 /// /// /// private void Button_Click_4(object sender, RoutedEventArgs e) { if (FontSize > 1) FontSize -= 1; } /// /// 编译 /// /// /// private async void Button_Click_5(object sender, RoutedEventArgs e) { IsCompile = false; CompileMessageList.Clear(); this.messagelist.ItemsSource = CompileMessageList; CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.InformationSlabCircleOutline, FontColol = new SolidColorBrush(Colors.Black), Message = Lang.开始编译}); if (!string.IsNullOrEmpty(CompileType) && CompileType== "VisionScripting") { var rst = await ScriptHelper.CompileVisionScriptingAsync(this.textEditor.Document.Text,null,null,null,null,null,null); if (rst.Item1) { CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CheckCircle, FontColol = new SolidColorBrush(Colors.Green), Message = Lang.编译完成 }); //rst.Item3.Invoke(rst.Item4,null); } else { foreach (var item in rst.Item2) { CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CloseCircle, FontColol = new SolidColorBrush(Colors.Red), Message = item }); } } } else { var rst = await ScriptHelper.CompileAsync(this.textEditor.Document.Text, null, null, null); if (rst.Item1) { CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CheckCircle, FontColol = new SolidColorBrush(Colors.Green), Message = Lang.编译完成 }); //rst.Item3.Invoke(rst.Item4,null); } else { foreach (var item in rst.Item2) { CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CloseCircle, FontColol = new SolidColorBrush(Colors.Red), Message = item }); } } } this.messagelist.ItemsSource = CompileMessageList; IsCompile = true; } CompletionWindow completionWindow; public void TextArea_TextEntered(object sender, System.Windows.Input.TextCompositionEventArgs e) { if (e.Text == ".") { //1.获取.前面的字符串 // 获取当前光标位置 int caretOffset = mTextArea.Caret.Offset; // 获取当前光标位置前面的单词(以空格、标点等作为分隔符) TextDocument document = Document; int startOffset = Math.Max(0, caretOffset - 1); // 从当前位置的前一个字符开始 // 找到单词的起始位置 while (startOffset >= 0 && !char.IsWhiteSpace(document.GetCharAt(startOffset))) { startOffset--; } startOffset++; // 回到单词的起始位置 // 获取单词的文本 int endOffset = caretOffset - 1; // 使用 AvalonEdit 提供的 TextUtilities 获取当前位置前面的单词 string word = document.GetText(startOffset, endOffset - startOffset); //2.检索字符串,并得到对象 var type = word.GetType(); foreach (var item in ClassMemberlist) { if (item.Key.ToLower().Trim(' ') == word.ToLower().Trim(' ')) { type = item.Value; break; } } if (type == null) return; //3.检索对象中所有的属性,字段,事件,方法,并做提示 // open code completion after the user has pressed dot: completionWindow = new CompletionWindow(((ICSharpCode.AvalonEdit.Editing.TextArea)sender)); // provide AvalonEdit with the data: IList data = completionWindow.CompletionList.CompletionData; System.Reflection.MethodInfo[] methodInfos = type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); System.Reflection.PropertyInfo[] propertyInfos = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); // System.Reflection.BindingFlags.Public| System.Reflection.BindingFlags.Static System.Reflection.EventInfo[] eventInfos = type.GetEvents(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); System.Reflection.FieldInfo[] fieldInfos = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); List proposals = new List(); //遍历字段 foreach (var item in fieldInfos) { data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Field)); } //遍历属性 foreach (var item in propertyInfos) { data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Property)); } //遍历方法 foreach (var item in methodInfos) { if (item.IsSpecialName) { continue; } if (item.ToString().Split(' ').Length >= 2) { data.Add(new ScriptCompletionData(item.ToString().Split(" ".ToCharArray(), 2)[1].Replace(" ", ""), CoreMemberType.Void)); } else { data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Void)); } } //遍历事件 foreach (var item in eventInfos) { data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Event)); } completionWindow.Show(); completionWindow.Closed += delegate { completionWindow = null; }; } else if (e.Text == "(") { Document.Insert(mTextArea.Caret.Offset, ")"); mTextArea.Caret.Offset = mTextArea.Caret.Offset - 1; } else if (e.Text == "{") { Document.Insert(mTextArea.Caret.Offset, "}"); mTextArea.Caret.Offset = mTextArea.Caret.Offset - 1; } } public void TextArea_TextEntering(object sender, System.Windows.Input.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); } } // do not set e.Handled=true - we still want to insert the character that was typed } /// /// 获取指定命名空间下的所有类 /// /// /// /// private Type[] GetTypesInNamespace(Assembly assembly, string nameSpace = null) { if (nameSpace == null) { return assembly.GetTypes(); } else { return assembly.GetTypes(); //return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray(); } } public void TextEditor_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) { // 检查是否按下了 Ctrl 键 if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) { // 根据鼠标滚轮方向调整字体大小 if (e.Delta > 0) { Button_Click_3(null,null); } else { Button_Click_4(null,null); } // 禁止事件继续传播,防止影响文本编辑器的滚动 e.Handled = true; } } /// /// 编辑输出信息选中时 /// /// /// private void Messagelist_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListView listView = sender as ListView; ScriptCompileMessage item = listView.SelectedItem as ScriptCompileMessage; if (item == null) { return; } if (item.Kind != MaterialDesignThemes.Wpf.PackIconKind.CloseCircle) { return; } // 正则表达式 string pattern = @"