using ICSharpCode.AvalonEdit.CodeCompletion; using ICSharpCode.AvalonEdit.Folding; using ICSharpCode.AvalonEdit.Highlighting; using System; using System.Collections.Generic; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media; namespace FastReport.Design.PageDesigners.Code { internal enum CompletionAction { None, DotEntered, EqualEntered, CtrlSpacePressed } internal class CompletionEventArgs : EventArgs { public IList CompletionData { get; } public string Text { get; } public CompletionAction Action { get; } public CompletionEventArgs(IList completionData, string text, CompletionAction action) { CompletionData = completionData; Text = text; Action = action; } } internal delegate void CompletionEventHandler(object sender, CompletionEventArgs e); internal partial class AvalonSyntaxEditor : SyntaxEditorBase { private ICSharpCode.AvalonEdit.TextEditor editor; private FoldingManager foldingManager; private CompletionWindow completionWindow; private Report report; public override string Text { get => editor.Text; set => editor.Text = value; } public override string SelectedText { get => editor.SelectedText; set => editor.SelectedText = value; } public override bool Modified { get => editor.IsModified; set => editor.IsModified = value; } public override bool ShowLineNumbers { get => editor.ShowLineNumbers; set => editor.ShowLineNumbers = value; } public override bool EnableVirtualSpace { get => editor.Options.EnableVirtualSpace; set => editor.Options.EnableVirtualSpace = value; } public override bool ConvertTabsToSpaces { get => editor.Options.ConvertTabsToSpaces; set => editor.Options.ConvertTabsToSpaces = value; } public override int IndentationSize { get => editor.Options.IndentationSize; set => editor.Options.IndentationSize = value; } private SyntaxType syntaxType; public override SyntaxType SyntaxType { get => syntaxType; set { syntaxType = value; switch (value) { case SyntaxType.None: editor.SyntaxHighlighting = null; break; case SyntaxType.Cs: editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#"); UpdateCSSyntaxColors(); break; case SyntaxType.Vb: editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("VB"); break; case SyntaxType.Xml: editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML"); break; } } } public override bool AllowCodeCompletion { get; set; } private void UpdateCSSyntaxColors() { var hl = editor.SyntaxHighlighting; hl.GetNamedColor("StringInterpolation").Foreground = hl.GetNamedColor("Punctuation").Foreground = hl.GetNamedColor("NumberLiteral").Foreground = new SimpleHighlightingBrush(Colors.Black); hl.GetNamedColor("Comment").Foreground = new SimpleHighlightingBrush(Colors.Green); hl.GetNamedColor("MethodCall").Foreground = new SimpleHighlightingBrush(Color.FromRgb(116, 83, 31)); hl.GetNamedColor("String").Foreground = new SimpleHighlightingBrush(Colors.Brown); hl.GetNamedColor("Char").Foreground = new SimpleHighlightingBrush(Colors.Red); hl.GetNamedColor("Preprocessor").Foreground = new SimpleHighlightingBrush(Colors.DarkGray); hl.GetNamedColor("GetSetAddRemove").Foreground = hl.GetNamedColor("Visibility").Foreground = hl.GetNamedColor("ParameterModifiers").Foreground = hl.GetNamedColor("Modifiers").Foreground = hl.GetNamedColor("TrueFalse").Foreground = hl.GetNamedColor("Keywords").Foreground = hl.GetNamedColor("ValueTypeKeywords").Foreground = hl.GetNamedColor("SemanticKeywords").Foreground = hl.GetNamedColor("NamespaceKeywords").Foreground = hl.GetNamedColor("ReferenceTypeKeywords").Foreground = hl.GetNamedColor("ThisOrBaseReference").Foreground = hl.GetNamedColor("NullOrValueKeywords").Foreground = hl.GetNamedColor("GotoKeywords").Foreground = hl.GetNamedColor("ContextKeywords").Foreground = hl.GetNamedColor("ExceptionKeywords").Foreground = hl.GetNamedColor("CheckedKeyword").Foreground = hl.GetNamedColor("UnsafeKeywords").Foreground = hl.GetNamedColor("OperatorKeywords").Foreground = hl.GetNamedColor("TypeKeywords").Foreground = hl.GetNamedColor("SemanticKeywords").Foreground = new SimpleHighlightingBrush(Colors.Blue); foreach (var color in hl.NamedHighlightingColors) { color.FontWeight = null; } editor.SyntaxHighlighting = null; editor.SyntaxHighlighting = hl; } private string GetTextAtCaret(int caretPosition) { int i = caretPosition - 1; string text = editor.Text; while (i > 0 && (char.IsLetterOrDigit(text[i]) || text[i] == '.')) i--; if (i > 0) i++; return caretPosition - i > 0 ? text.Substring(i, caretPosition - i) : ""; } private char GetCharAtCaret(int caretPosition) { string text = editor.Text; return caretPosition >= 0 && caretPosition < text.Length ? text[caretPosition] : '\0'; } private string GetWordAtCaret(int caretPosition) { int i = caretPosition - 1; string text = editor.Text; while (i > 0 && char.IsLetterOrDigit(text[i])) i--; if (i > 0) i++; return caretPosition - i > 0 ? text.Substring(i, caretPosition - i) : ""; } private void ShowCompletion(string enteredText, bool ctrlSpace) { if (!AllowCodeCompletion) return; string textAtCaret = ""; CompletionAction action = CompletionAction.None; if (enteredText == ".") { action = CompletionAction.DotEntered; textAtCaret = GetTextAtCaret(editor.CaretOffset - 1); } else if (ctrlSpace) { action = CompletionAction.CtrlSpacePressed; textAtCaret = GetTextAtCaret(editor.CaretOffset); } else if (enteredText == " " && GetCharAtCaret(editor.CaretOffset - 2) == '=') { action = CompletionAction.EqualEntered; textAtCaret = GetTextAtCaret(editor.CaretOffset - 3); } if (action != CompletionAction.None) { completionWindow = new CompletionWindow(editor.TextArea); completionWindow.ResizeMode = System.Windows.ResizeMode.NoResize; IList data = completionWindow.CompletionList.CompletionData; DoCodeCompletion(new CompletionEventArgs(data, textAtCaret, action)); if (action == CompletionAction.CtrlSpacePressed) { string lastWord = GetWordAtCaret(editor.CaretOffset); completionWindow.StartOffset -= lastWord.Length; } if (data.Count > 0) { completionWindow.Show(); completionWindow.Closed += (s, args) => completionWindow = null; } } } private void editor_TextArea_TextEntered(object sender, TextCompositionEventArgs e) { ShowCompletion(e.Text, false); } private void OnCtrlSpaceCommand(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs) { ShowCompletion(null, true); } private void editor_TextArea_TextEntering(object sender, TextCompositionEventArgs e) { if (!AllowCodeCompletion) return; 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); } } } public override void Cut() => editor.Cut(); public override void Copy() => editor.Copy(); public override void Paste() => editor.Paste(); public override bool CanUndo => editor.CanUndo; public override void Undo() => editor.Undo(); public override bool CanRedo => editor.CanRedo; public override void Redo() => editor.Redo(); public override void Select(int start, int length) { editor.Select(start, length); } public override void SelectAll() => editor.SelectAll(); public override void Focus() { Application.DoEvents(); editor.Dispatcher.InvokeAsync(() => editor.Focus()); } public override void Locate(int line, int column) { editor.CaretOffset = editor.Document.GetOffset(line, column); } public override void SetCaretFromMousePosition(System.Drawing.Point pos) { var textPos = editor.GetPositionFromPoint(new System.Windows.Point(pos.X / DpiScale, pos.Y / DpiScale)); if (textPos != null) { Locate(textPos.Value.Line, textPos.Value.Column); } } public override void UpdateInternals(Report report) { this.report = report; } public AvalonSyntaxEditor() { editor = new ICSharpCode.AvalonEdit.TextEditor(); SetControl(editor); foldingManager = FoldingManager.Install(editor.TextArea); editor.HorizontalScrollBarVisibility = editor.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto; editor.Padding = new System.Windows.Thickness(4, 2, 0, 0); editor.TextChanged += (s, e) => OnTextChanged(); editor.TextArea.TextEntering += editor_TextArea_TextEntering; editor.TextArea.TextEntered += editor_TextArea_TextEntered; var ctrlSpace = new RoutedCommand(); ctrlSpace.InputGestures.Add(new KeyGesture(Key.Space, System.Windows.Input.ModifierKeys.Control)); editor.CommandBindings.Add(new CommandBinding(ctrlSpace, OnCtrlSpaceCommand)); } } }