AvalonSyntaxEditor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using ICSharpCode.AvalonEdit.CodeCompletion;
  2. using ICSharpCode.AvalonEdit.Folding;
  3. using ICSharpCode.AvalonEdit.Highlighting;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Windows.Forms;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. namespace FastReport.Design.PageDesigners.Code
  10. {
  11. internal enum CompletionAction
  12. {
  13. None,
  14. DotEntered,
  15. EqualEntered,
  16. CtrlSpacePressed
  17. }
  18. internal class CompletionEventArgs : EventArgs
  19. {
  20. public IList<ICompletionData> CompletionData { get; }
  21. public string Text { get; }
  22. public CompletionAction Action { get; }
  23. public CompletionEventArgs(IList<ICompletionData> completionData, string text, CompletionAction action)
  24. {
  25. CompletionData = completionData;
  26. Text = text;
  27. Action = action;
  28. }
  29. }
  30. internal delegate void CompletionEventHandler(object sender, CompletionEventArgs e);
  31. internal partial class AvalonSyntaxEditor : SyntaxEditorBase
  32. {
  33. private ICSharpCode.AvalonEdit.TextEditor editor;
  34. private FoldingManager foldingManager;
  35. private CompletionWindow completionWindow;
  36. private Report report;
  37. public override string Text
  38. {
  39. get => editor.Text;
  40. set => editor.Text = value;
  41. }
  42. public override string SelectedText
  43. {
  44. get => editor.SelectedText;
  45. set => editor.SelectedText = value;
  46. }
  47. public override bool Modified
  48. {
  49. get => editor.IsModified;
  50. set => editor.IsModified = value;
  51. }
  52. public override bool ShowLineNumbers
  53. {
  54. get => editor.ShowLineNumbers;
  55. set => editor.ShowLineNumbers = value;
  56. }
  57. public override bool EnableVirtualSpace
  58. {
  59. get => editor.Options.EnableVirtualSpace;
  60. set => editor.Options.EnableVirtualSpace = value;
  61. }
  62. public override bool ConvertTabsToSpaces
  63. {
  64. get => editor.Options.ConvertTabsToSpaces;
  65. set => editor.Options.ConvertTabsToSpaces = value;
  66. }
  67. public override int IndentationSize
  68. {
  69. get => editor.Options.IndentationSize;
  70. set => editor.Options.IndentationSize = value;
  71. }
  72. private SyntaxType syntaxType;
  73. public override SyntaxType SyntaxType
  74. {
  75. get => syntaxType;
  76. set
  77. {
  78. syntaxType = value;
  79. switch (value)
  80. {
  81. case SyntaxType.None:
  82. editor.SyntaxHighlighting = null;
  83. break;
  84. case SyntaxType.Cs:
  85. editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
  86. UpdateCSSyntaxColors();
  87. break;
  88. case SyntaxType.Vb:
  89. editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("VB");
  90. break;
  91. case SyntaxType.Xml:
  92. editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML");
  93. break;
  94. }
  95. }
  96. }
  97. public override bool AllowCodeCompletion { get; set; }
  98. private void UpdateCSSyntaxColors()
  99. {
  100. var hl = editor.SyntaxHighlighting;
  101. hl.GetNamedColor("StringInterpolation").Foreground =
  102. hl.GetNamedColor("Punctuation").Foreground =
  103. hl.GetNamedColor("NumberLiteral").Foreground = new SimpleHighlightingBrush(Colors.Black);
  104. hl.GetNamedColor("Comment").Foreground = new SimpleHighlightingBrush(Colors.Green);
  105. hl.GetNamedColor("MethodCall").Foreground = new SimpleHighlightingBrush(Color.FromRgb(116, 83, 31));
  106. hl.GetNamedColor("String").Foreground = new SimpleHighlightingBrush(Colors.Brown);
  107. hl.GetNamedColor("Char").Foreground = new SimpleHighlightingBrush(Colors.Red);
  108. hl.GetNamedColor("Preprocessor").Foreground = new SimpleHighlightingBrush(Colors.DarkGray);
  109. hl.GetNamedColor("GetSetAddRemove").Foreground =
  110. hl.GetNamedColor("Visibility").Foreground =
  111. hl.GetNamedColor("ParameterModifiers").Foreground =
  112. hl.GetNamedColor("Modifiers").Foreground =
  113. hl.GetNamedColor("TrueFalse").Foreground =
  114. hl.GetNamedColor("Keywords").Foreground =
  115. hl.GetNamedColor("ValueTypeKeywords").Foreground =
  116. hl.GetNamedColor("SemanticKeywords").Foreground =
  117. hl.GetNamedColor("NamespaceKeywords").Foreground =
  118. hl.GetNamedColor("ReferenceTypeKeywords").Foreground =
  119. hl.GetNamedColor("ThisOrBaseReference").Foreground =
  120. hl.GetNamedColor("NullOrValueKeywords").Foreground =
  121. hl.GetNamedColor("GotoKeywords").Foreground =
  122. hl.GetNamedColor("ContextKeywords").Foreground =
  123. hl.GetNamedColor("ExceptionKeywords").Foreground =
  124. hl.GetNamedColor("CheckedKeyword").Foreground =
  125. hl.GetNamedColor("UnsafeKeywords").Foreground =
  126. hl.GetNamedColor("OperatorKeywords").Foreground =
  127. hl.GetNamedColor("TypeKeywords").Foreground =
  128. hl.GetNamedColor("SemanticKeywords").Foreground = new SimpleHighlightingBrush(Colors.Blue);
  129. foreach (var color in hl.NamedHighlightingColors)
  130. {
  131. color.FontWeight = null;
  132. }
  133. editor.SyntaxHighlighting = null;
  134. editor.SyntaxHighlighting = hl;
  135. }
  136. private string GetTextAtCaret(int caretPosition)
  137. {
  138. int i = caretPosition - 1;
  139. string text = editor.Text;
  140. while (i > 0 && (char.IsLetterOrDigit(text[i]) || text[i] == '.'))
  141. i--;
  142. if (i > 0)
  143. i++;
  144. return caretPosition - i > 0 ? text.Substring(i, caretPosition - i) : "";
  145. }
  146. private char GetCharAtCaret(int caretPosition)
  147. {
  148. string text = editor.Text;
  149. return caretPosition >= 0 && caretPosition < text.Length ? text[caretPosition] : '\0';
  150. }
  151. private string GetWordAtCaret(int caretPosition)
  152. {
  153. int i = caretPosition - 1;
  154. string text = editor.Text;
  155. while (i > 0 && char.IsLetterOrDigit(text[i]))
  156. i--;
  157. if (i > 0)
  158. i++;
  159. return caretPosition - i > 0 ? text.Substring(i, caretPosition - i) : "";
  160. }
  161. private void ShowCompletion(string enteredText, bool ctrlSpace)
  162. {
  163. if (!AllowCodeCompletion)
  164. return;
  165. string textAtCaret = "";
  166. CompletionAction action = CompletionAction.None;
  167. if (enteredText == ".")
  168. {
  169. action = CompletionAction.DotEntered;
  170. textAtCaret = GetTextAtCaret(editor.CaretOffset - 1);
  171. }
  172. else if (ctrlSpace)
  173. {
  174. action = CompletionAction.CtrlSpacePressed;
  175. textAtCaret = GetTextAtCaret(editor.CaretOffset);
  176. }
  177. else if (enteredText == " " && GetCharAtCaret(editor.CaretOffset - 2) == '=')
  178. {
  179. action = CompletionAction.EqualEntered;
  180. textAtCaret = GetTextAtCaret(editor.CaretOffset - 3);
  181. }
  182. if (action != CompletionAction.None)
  183. {
  184. completionWindow = new CompletionWindow(editor.TextArea);
  185. completionWindow.ResizeMode = System.Windows.ResizeMode.NoResize;
  186. IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
  187. DoCodeCompletion(new CompletionEventArgs(data, textAtCaret, action));
  188. if (action == CompletionAction.CtrlSpacePressed)
  189. {
  190. string lastWord = GetWordAtCaret(editor.CaretOffset);
  191. completionWindow.StartOffset -= lastWord.Length;
  192. }
  193. if (data.Count > 0)
  194. {
  195. completionWindow.Show();
  196. completionWindow.Closed += (s, args) => completionWindow = null;
  197. }
  198. }
  199. }
  200. private void editor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
  201. {
  202. ShowCompletion(e.Text, false);
  203. }
  204. private void OnCtrlSpaceCommand(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
  205. {
  206. ShowCompletion(null, true);
  207. }
  208. private void editor_TextArea_TextEntering(object sender, TextCompositionEventArgs e)
  209. {
  210. if (!AllowCodeCompletion)
  211. return;
  212. if (e.Text.Length > 0 && completionWindow != null)
  213. {
  214. if (!char.IsLetterOrDigit(e.Text[0]))
  215. {
  216. // Whenever a non-letter is typed while the completion window is open,
  217. // insert the currently selected element.
  218. completionWindow.CompletionList.RequestInsertion(e);
  219. }
  220. }
  221. }
  222. public override void Cut() => editor.Cut();
  223. public override void Copy() => editor.Copy();
  224. public override void Paste() => editor.Paste();
  225. public override bool CanUndo => editor.CanUndo;
  226. public override void Undo() => editor.Undo();
  227. public override bool CanRedo => editor.CanRedo;
  228. public override void Redo() => editor.Redo();
  229. public override void Select(int start, int length)
  230. {
  231. editor.Select(start, length);
  232. }
  233. public override void SelectAll() => editor.SelectAll();
  234. public override void Focus()
  235. {
  236. Application.DoEvents();
  237. editor.Dispatcher.InvokeAsync(() => editor.Focus());
  238. }
  239. public override void Locate(int line, int column)
  240. {
  241. editor.CaretOffset = editor.Document.GetOffset(line, column);
  242. }
  243. public override void SetCaretFromMousePosition(System.Drawing.Point pos)
  244. {
  245. var textPos = editor.GetPositionFromPoint(new System.Windows.Point(pos.X / DpiScale, pos.Y / DpiScale));
  246. if (textPos != null)
  247. {
  248. Locate(textPos.Value.Line, textPos.Value.Column);
  249. }
  250. }
  251. public override void UpdateInternals(Report report)
  252. {
  253. this.report = report;
  254. }
  255. public AvalonSyntaxEditor()
  256. {
  257. editor = new ICSharpCode.AvalonEdit.TextEditor();
  258. SetControl(editor);
  259. foldingManager = FoldingManager.Install(editor.TextArea);
  260. editor.HorizontalScrollBarVisibility = editor.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto;
  261. editor.Padding = new System.Windows.Thickness(4, 2, 0, 0);
  262. editor.TextChanged += (s, e) => OnTextChanged();
  263. editor.TextArea.TextEntering += editor_TextArea_TextEntering;
  264. editor.TextArea.TextEntered += editor_TextArea_TextEntered;
  265. var ctrlSpace = new RoutedCommand();
  266. ctrlSpace.InputGestures.Add(new KeyGesture(Key.Space, System.Windows.Input.ModifierKeys.Control));
  267. editor.CommandBindings.Add(new CommandBinding(ctrlSpace, OnCtrlSpaceCommand));
  268. }
  269. }
  270. }