using System; using System.Collections.Generic; using System.Linq; using System.Text; 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.Text.RegularExpressions; [assembly: CLSCompliant(true)] namespace DBToolControls { public class HighlightingRichTextBox : RichTextBox { private bool textHandlingInProgress; private bool pasting; private string lineStartOffset; private SyntaxProvider syntaxProvider = new SyntaxProvider(); public SyntaxProvider SyntaxProvider { get { return syntaxProvider; } } public string Text { set { Document.Blocks.Clear(); if (value.Length > 2) { Document.Blocks.Add(new Paragraph(new Run(value.Substring(0, value.Length - 2)))); RebuildContent(); SelectAll(); } } get { return new TextRange(Document.ContentStart, Document.ContentEnd).Text; } } public HighlightingRichTextBox() { Document.LineHeight = 1; DataObject.AddPastingHandler(this, (sender, e) => pasting = true); } // keep indentation in new lines protected override void OnPreviewKeyDown(KeyEventArgs e) { if (e.Key == Key.Tab) { CaretPosition.InsertTextInRun("\t"); if(CaretPosition.GetOffsetToPosition(Document.ContentEnd) != 0) CaretPosition = CaretPosition.GetPositionAtOffset(1); e.Handled = true; } else if (e.Key == Key.Enter) { TextRange range = new TextRange(CaretPosition.GetLineStartPosition(0), CaretPosition); string s = range.Text; lineStartOffset = s.Substring(0, s.Length - s.TrimStart().Length); if (new List { ")", ");", "}", "};" }.Contains(s.Trim())) { // unindent if (lineStartOffset.EndsWith("\t")) { lineStartOffset = lineStartOffset.Remove(lineStartOffset.Length - 1); } else if (lineStartOffset.EndsWith(" ")) { lineStartOffset = lineStartOffset.Remove(lineStartOffset.Length - 2); } range.Text = lineStartOffset + range.Text.Trim(); CaretPosition = range.End; } else if (CaretPosition.GetPositionAtOffset(-1) != null) { // indent if line ends with ( or { s = new TextRange(CaretPosition.GetPositionAtOffset(-1), CaretPosition).Text; if (s.Length > 0 && new List { '(', '{' }.Contains(s[0])) lineStartOffset += " "; } } base.OnPreviewKeyDown(e); } protected override void OnKeyUp(KeyEventArgs e) { if (e.Key == Key.Enter) CaretPosition.InsertTextInRun(lineStartOffset); base.OnKeyUp(e); } // Syntax Highlighting protected override void OnTextChanged(TextChangedEventArgs e) { if (pasting) { pasting = false; RebuildContent(); } else if (!textHandlingInProgress) { FormatTextAt(CaretPosition); } base.OnTextChanged(e); } private void FormatTextAt(TextPointer pointer) { textHandlingInProgress = true; TextRange range = WordBreaker.GetWordRange(pointer); TextPointer temp = range.End.GetNextInsertionPosition(LogicalDirection.Backward); if (temp != null) { TextRange lastInput = new TextRange(range.End, temp); if (SyntaxProvider.Delimiters.Contains(lastInput.Text[0])) lastInput.ClearAllProperties(); } ApplyFormatting(range); temp = CaretPosition.GetPositionAtOffset(-3); if (temp != null && !range.Contains(temp)) ApplyFormatting(WordBreaker.GetWordRange(temp)); textHandlingInProgress = false; } private void RebuildContent() { textHandlingInProgress = true; string s = new TextRange(Document.ContentStart, Document.ContentEnd).Text; Paragraph p = new Paragraph(); string[] words = Regex.Split(s, SyntaxProvider.DelimitersAsRegExPattern); if (words.Length < 2) return; foreach (string word in words.Take(words.Length - 2).Where(w => w.Length > 0)) p.Inlines.Add(word.Trim().Length > 0 ? ApplyFormatting(new Run(word)) : new Run(word)); Document.Blocks.Clear(); Document.Blocks.Add(p); CaretPosition = Document.ContentEnd; textHandlingInProgress = false; } private void ApplyFormatting(TextRange range) { Dictionary formatting = SyntaxProvider.GetFormattingFor(range.Text); if (formatting != null) foreach (KeyValuePair pv in formatting) range.ApplyPropertyValue(pv.Key, pv.Value); else range.ClearAllProperties(); } private Run ApplyFormatting(Run r) { Dictionary formatting = SyntaxProvider.GetFormattingFor(r.Text); if (formatting != null) foreach (KeyValuePair pv in formatting) r.SetValue(pv.Key, pv.Value); return r; } } }