void IndeterminateProgressThreadProc( ) { try { Thread.Sleep(2222); UITaskHelper.Invoke(this, CancellationToken.None, () => { ucMatches.ShowIndeterminateProgress(true); ucMatches.ShowInfo("The engine is busy, please wait…", true); ucText.SetMatches(RegexMatches.Empty, cbShowCaptures.IsChecked == true, GetEolOption( )); }); } catch (ThreadInterruptedException) { // ignore } catch (ThreadAbortException) { // ignore } catch (Exception exc) { _ = exc; if (Debugger.IsAttached) { Debugger.Break( ); } // ignore } }
void RecolouringThreadProc(ICancellable cnc) { RegexMatches matches; string eol; bool show_captures; lock (this) { matches = LastMatches; eol = LastEol; show_captures = LastShowCaptures; } TextData td = null; Rect clip_rect = Rect.Empty; int top_index = 0; int bottom_index = 0; UITaskHelper.Invoke(rtb, () => { td = null; var start_doc = rtb.Document.ContentStart; var end_doc = rtb.Document.ContentStart; if (!start_doc.HasValidLayout || !end_doc.HasValidLayout) { return; } var td0 = rtb.GetTextData(eol); if (cnc.IsCancellationRequested) { return; } td = td0; clip_rect = new Rect(new Size(rtb.ViewportWidth, rtb.ViewportHeight)); TextPointer top_pointer = rtb.GetPositionFromPoint(new Point(0, 0), snapToText: true).GetLineStartPosition(-1, out int _); if (cnc.IsCancellationRequested) { return; } top_index = td.TextPointers.GetIndex(top_pointer, LogicalDirection.Backward); if (cnc.IsCancellationRequested) { return; } if (top_index < 0) { top_index = 0; } TextPointer bottom_pointer = rtb.GetPositionFromPoint(new Point(0, rtb.ViewportHeight), snapToText: true).GetLineStartPosition(+1, out int lines_skipped); if (cnc.IsCancellationRequested) { return; } if (bottom_pointer == null || lines_skipped == 0) { bottom_index = td.Text.Length; } else { bottom_index = td.TextPointers.GetIndex(bottom_pointer, LogicalDirection.Forward); if (cnc.IsCancellationRequested) { return; } } if (bottom_index > td.Text.Length) { bottom_index = td.Text.Length; } if (bottom_index < top_index) { bottom_index = top_index; // (including 'if bottom_index == 0') } }); if (cnc.IsCancellationRequested) { return; } if (td == null) { return; } if (td.Text.Length == 0) { return; } Debug.Assert(top_index >= 0); Debug.Assert(bottom_index >= top_index); Debug.Assert(bottom_index <= td.Text.Length); // (NOTE. Overlaps are possible in this example: (?=(..)) var segments_and_styles = new List <(Segment segment, StyleInfo styleInfo)>( ); var segments_to_uncolour = new List <Segment>( ); segments_to_uncolour.Add(new Segment(top_index, bottom_index - top_index + 1)); if (matches != null && matches.Count > 0) { int i = -1; foreach (var match in matches.Matches) { ++i; if (cnc.IsCancellationRequested) { break; } Debug.Assert(match.Success); // TODO: consider these conditions for bi-directional text if (match.TextIndex + match.TextLength < top_index) { continue; } if (match.TextIndex > bottom_index) { continue; // (do not break; the order of indices is unspecified) } var highlight_index = unchecked (i % HighlightStyleInfos.Length); Segment.Except(segments_to_uncolour, match.TextIndex, match.TextLength); segments_and_styles.Add((new Segment(match.TextIndex, match.TextLength), HighlightStyleInfos[highlight_index])); } } if (cnc.IsCancellationRequested) { return; } List <(Segment segment, StyleInfo styleInfo)> segments_to_uncolour_with_style = segments_to_uncolour .Select(s => (s, NormalStyleInfo)) .ToList( ); if (cnc.IsCancellationRequested) { return; } int center_index = (top_index + bottom_index) / 2; var all_segments_and_styles = segments_and_styles.Concat(segments_to_uncolour_with_style) .OrderBy(s => Math.Abs(center_index - (s.segment.Index + s.segment.Length / 2))) .ToList( ); if (cnc.IsCancellationRequested) { return; } RtbUtilities.ApplyStyle(cnc, ChangeEventHelper, pbProgress, td, all_segments_and_styles); if (cnc.IsCancellationRequested) { return; } UITaskHelper.BeginInvoke(pbProgress, () => { pbProgress.Visibility = Visibility.Hidden; }); }
bool CollectEof(ICancellable cnc, TextData td, Rect clip_rect, int top_index) { if (cnc.IsCancellationRequested) { return(false); } var rtb = Rtb; double max_x = double.NaN; Rect end_rect = Rect.Empty; UITaskHelper.Invoke(rtb, () => { var end = rtb.Document.ContentEnd; end_rect = end.GetCharacterRect(LogicalDirection.Forward); // (no width) if (end_rect.Bottom < clip_rect.Top || end_rect.Top > clip_rect.Bottom) { return; } max_x = end_rect.Left; // if no RTL, then return a quick answer var begin_line = end.GetLineStartPosition(0); if (begin_line != null) { var r = new TextRange(begin_line, end); var text = r.Text; bool has_RTL = false; for (int k = 0; k < text.Length; ++k) { if (cnc.IsCancellationRequested) { return; } if (UnicodeUtilities.IsRTL(text[k])) { has_RTL = true; break; } } if (!has_RTL) { return; } } // we have RTL segments that need additional navigation to find the rightmost X for (var tp = end; ;) { if (cnc.IsCancellationRequested) { return; } tp = tp.GetNextInsertionPosition(LogicalDirection.Backward); if (tp == null) { break; } // WORKAROUND for lines like "0ראל", when "0" is matched and highlighted tp = tp.GetInsertionPosition(LogicalDirection.Forward); var rect = tp.GetCharacterRect(LogicalDirection.Forward); if (rect.Bottom < end_rect.Bottom) { break; } if (max_x < rect.Left) { max_x = rect.Left; } } }); if (cnc.IsCancellationRequested) { return(false); } lock (this) { if (double.IsNaN(max_x)) { PositionEof = Rect.Empty; } else { PositionEof = new Rect(new Point(max_x, end_rect.Top), end_rect.Size); PositionEof.Offset(rtb.HorizontalOffset, rtb.VerticalOffset); } } DelayedInvalidateVisual( ); return(true); }
bool CollectEols(ICancellable cnc, TextData td, Rect clip_rect, int top_index) { if (cnc.IsCancellationRequested) { return(false); } var rtb = Rtb; List <Rect> positions_eols = new List <Rect>( ); // lines with no right-to-left segments var matches = EolRegex.Matches(td.Text); for (int i = 0; i < matches.Count; ++i) { if (cnc.IsCancellationRequested) { return(false); } int index = matches[i].Index; if (index < top_index) { continue; } int previous_index = i == 0 ? 0 : matches[i - 1].Index; bool has_RTL = false; for (int k = previous_index; k < index; ++k) { if (cnc.IsCancellationRequested) { return(false); } if (UnicodeUtilities.IsRTL(td.Text[k])) { has_RTL = true; break; } } if (has_RTL) { // RTL needs more navigation to find the rightmost X Rect left_rect = Rect.Empty; double max_x = double.NaN; bool should_continue = false; bool should_break = false; UITaskHelper.Invoke(rtb, () => { TextPointer left = td.TextPointers.GetTextPointer(index); left_rect = left.GetCharacterRect(LogicalDirection.Forward); if (left_rect.Bottom < clip_rect.Top) { should_continue = true; return; } if (left_rect.Top > clip_rect.Bottom) { should_break = true; return; } max_x = left_rect.Left; for (var tp = left.GetInsertionPosition(LogicalDirection.Backward); ;) { if (cnc.IsCancellationRequested) { return; } tp = tp.GetNextInsertionPosition(LogicalDirection.Backward); if (tp == null) { break; } // WORKAROUND for lines like "0ראל", when "0" is matched and highlighted tp = tp.GetInsertionPosition(LogicalDirection.Forward); var rect_b = tp.GetCharacterRect(LogicalDirection.Backward); var rect_f = tp.GetCharacterRect(LogicalDirection.Forward); if (cnc.IsCancellationRequested) { return; } if (rect_b.Bottom < left_rect.Top && rect_f.Bottom < left_rect.Top) { break; } if (rect_b.Bottom > left_rect.Top) { if (max_x < rect_b.Left) { max_x = rect_b.Left; } } if (rect_f.Bottom > left_rect.Top) { if (max_x < rect_f.Left) { max_x = rect_f.Left; } } } }); if (cnc.IsCancellationRequested) { return(false); } if (should_continue) { continue; } if (should_break) { break; } Rect eol_rect = new Rect(new Point(max_x, left_rect.Top), left_rect.Size); eol_rect.Offset(rtb.HorizontalOffset, rtb.VerticalOffset); positions_eols.Add(eol_rect); } else { // no RTL; quick answer Rect eol_rect = Rect.Empty; UITaskHelper.Invoke(rtb, () => { TextPointer left = td.TextPointers.GetTextPointer(index); eol_rect = left.GetCharacterRect(LogicalDirection.Forward); }); if (eol_rect.Bottom < clip_rect.Top) { continue; } if (eol_rect.Top > clip_rect.Bottom) { break; } eol_rect.Offset(rtb.HorizontalOffset, rtb.VerticalOffset); positions_eols.Add(eol_rect); } } if (cnc.IsCancellationRequested) { return(false); } lock (this) { PositionsEols = positions_eols; } DelayedInvalidateVisual( ); return(true); }
void ThreadProc(ICancellable cnc) { if (!mShowWhitespaces) { return; } var rtb = Rtb; TextData td = null; Rect clip_rect = Rect.Empty; int top_index = 0; UITaskHelper.Invoke(rtb, () => { td = null; var start_doc = rtb.Document.ContentStart; var end_doc = rtb.Document.ContentStart; if (!start_doc.HasValidLayout || !end_doc.HasValidLayout) { return; } var td0 = rtb.GetTextData(null); if (cnc.IsCancellationRequested) { return; } td = td0; clip_rect = new Rect(new Size(rtb.ViewportWidth, rtb.ViewportHeight)); TextPointer start_pointer = rtb.GetPositionFromPoint(new Point(0, 0), snapToText: true).GetLineStartPosition(-1, out int unused); top_index = td.TextPointers.GetIndex(start_pointer, LogicalDirection.Backward); if (top_index < 0) { top_index = 0; } }); if (cnc.IsCancellationRequested) { return; } if (td != null) { CollectEols(cnc, td, clip_rect, top_index); if (cnc.IsCancellationRequested) { return; } CollectEof(cnc, td, clip_rect, top_index); if (cnc.IsCancellationRequested) { return; } CollectSpaces(cnc, td, clip_rect, top_index); } }
void UpdateWhitespaceWarningThreadProc(ICancellable cnc) { bool has_whitespaces = false; bool show_whitespaces_option = false; string eol = null; BaseTextData td = null; UITaskHelper.Invoke(this, () => { show_whitespaces_option = cbShowWhitespaces.IsChecked == true; eol = GetEolOption( ); td = ucPattern.GetBaseTextData(eol); if (cnc.IsCancellationRequested) { return; } }); if (cnc.IsCancellationRequested) { return; } has_whitespaces = RegexHasWhitespace.IsMatch(td.Text); if (!has_whitespaces) { UITaskHelper.Invoke(this, () => { td = ucText.GetBaseTextData(eol); }); has_whitespaces = RegexHasWhitespace.IsMatch(td.Text); } bool show1 = false; bool show2 = false; if (show_whitespaces_option) { if (has_whitespaces) { show2 = true; } } else { if (has_whitespaces) { show1 = true; } } UITaskHelper.Invoke(this, () => { if (show1 && lblWhitespaceWarning1.Parent == null) { lblWarnings.Inlines.Add(lblWhitespaceWarning1); } if (!show1 && lblWhitespaceWarning1.Parent != null) { lblWarnings.Inlines.Remove(lblWhitespaceWarning1); } if (show2 && lblWhitespaceWarning2.Parent == null) { lblWarnings.Inlines.Add(lblWhitespaceWarning2); } if (!show2 && lblWhitespaceWarning2.Parent != null) { lblWarnings.Inlines.Remove(lblWhitespaceWarning2); } }); }
void FindMatchesThreadProc(ICancellable cnc) { string eol = null; string pattern = null; string text = null; bool first_only = false; IRegexEngine engine = null; UITaskHelper.Invoke(this, () => { eol = GetEolOption( ); pattern = ucPattern.GetBaseTextData(eol).Text; if (cnc.IsCancellationRequested) { return; } text = ucText.GetBaseTextData(eol).Text; if (cnc.IsCancellationRequested) { return; } first_only = cbShowFirstOnly.IsChecked == true; engine = CurrentRegexEngine; }); if (cnc.IsCancellationRequested) { return; } if (string.IsNullOrEmpty(pattern)) { UITaskHelper.BeginInvoke(this, () => { ucText.SetMatches(RegexMatches.Empty, cbShowCaptures.IsChecked == true, GetEolOption( )); ucMatches.ShowNoPattern( ); lblMatches.Text = "Matches"; pnlShowAll.Visibility = Visibility.Collapsed; pnlShowFirst.Visibility = Visibility.Collapsed; }); } else { IMatcher parsed_pattern = null; RegexMatches matches = null; bool is_good = false; try { UITaskHelper.BeginInvoke(this, CancellationToken.None, () => ucMatches.ShowMatchingInProgress(true)); parsed_pattern = engine.ParsePattern(pattern); var indeterminate_progress_thread = new Thread(IndeterminateProgressThreadProc) { IsBackground = true }; try { indeterminate_progress_thread.Start( ); matches = parsed_pattern.Matches(text, cnc); } finally { HideIndeterminateProgress(indeterminate_progress_thread); } is_good = true; } catch (Exception exc) { if (cnc.IsCancellationRequested) { return; } UITaskHelper.BeginInvoke(this, CancellationToken.None, () => { ucText.SetMatches(RegexMatches.Empty, cbShowCaptures.IsChecked == true, GetEolOption( )); ucMatches.ShowError(exc, engine.Capabilities.HasFlag(RegexEngineCapabilityEnum.ScrollErrorsToEnd)); lblMatches.Text = "Error"; pnlShowAll.Visibility = Visibility.Collapsed; pnlShowFirst.Visibility = Visibility.Collapsed; }); Debug.Assert(!is_good); } finally { UITaskHelper.BeginInvoke(this, CancellationToken.None, () => ucMatches.ShowMatchingInProgress(false)); } if (cnc.IsCancellationRequested) { return; } if (is_good) { int count = matches.Count; var matches_to_show = first_only ? new RegexMatches(Math.Min(1, count), matches.Matches.Take(1)) : matches; if (cnc.IsCancellationRequested) { return; } UITaskHelper.BeginInvoke(this, () => { ucText.SetMatches(matches_to_show, cbShowCaptures.IsChecked == true, GetEolOption( )); ucMatches.SetMatches(text, matches_to_show, first_only, cbShowSucceededGroupsOnly.IsChecked == true, cbShowCaptures.IsChecked == true); lblMatches.Text = count == 0 ? "Matches" : count == 1 ? "1 match" : $"{count:#,##0} matches"; pnlShowAll.Visibility = first_only && count > 1 ? Visibility.Visible : Visibility.Collapsed; pnlShowFirst.Visibility = !first_only && count > 1 ? Visibility.Visible : Visibility.Collapsed; }); } } }
void HighlightingThreadProc(ICancellable cnc) { IRegexEngine regex_engine; string eol; lock (this) { regex_engine = mRegexEngine; eol = mEol; } if (regex_engine == null) { return; } TextData td = null; Rect clip_rect = Rect.Empty; int top_index = 0; int bottom_index = 0; bool is_focused = false; UITaskHelper.Invoke(rtb, () => { td = null; var start_doc = rtb.Document.ContentStart; var end_doc = rtb.Document.ContentStart; if (!start_doc.HasValidLayout || !end_doc.HasValidLayout) { return; } var td0 = rtb.GetTextData(eol); if (cnc.IsCancellationRequested) { return; } td = td0; clip_rect = new Rect(new Size(rtb.ViewportWidth, rtb.ViewportHeight)); TextPointer top_pointer = rtb.GetPositionFromPoint(new Point(0, 0), snapToText: true).GetLineStartPosition(-1, out int _); if (cnc.IsCancellationRequested) { return; } top_index = td.TextPointers.GetIndex(top_pointer, LogicalDirection.Backward); if (top_index < 0) { top_index = 0; } TextPointer bottom_pointer = rtb.GetPositionFromPoint(new Point(0, rtb.ViewportHeight), snapToText: true).GetLineStartPosition(+1, out int lines_skipped); if (cnc.IsCancellationRequested) { return; } if (bottom_pointer == null || lines_skipped == 0) { bottom_index = td.Text.Length; } else { bottom_index = td.TextPointers.GetIndex(bottom_pointer, LogicalDirection.Forward); if (cnc.IsCancellationRequested) { return; } } if (bottom_index > td.Text.Length) { bottom_index = td.Text.Length; } if (bottom_index < top_index) { bottom_index = top_index; // (including 'if bottom_index == 0') } is_focused = rtb.IsFocused; }); if (cnc.IsCancellationRequested) { return; } if (td == null) { return; } if (td.Text.Length == 0) { return; } Debug.Assert(top_index >= 0); Debug.Assert(bottom_index >= top_index); Debug.Assert(bottom_index <= td.Text.Length); Highlights highlights = null; if (is_focused) { var visible_segment = new Segment(top_index, bottom_index - top_index + 1); highlights = new Highlights( ); regex_engine.HighlightPattern(cnc, highlights, td.Text, td.SelectionStart, td.SelectionEnd, visible_segment); } if (cnc.IsCancellationRequested) { return; } lock ( Locker ) { ChangeEventHelper.Invoke(CancellationToken.None, () => { TryHighlight(ref LeftHighlightedParenthesis, highlights?.LeftPar ?? Segment.Empty, td, PatternParaHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } TryHighlight(ref RightHighlightedParenthesis, highlights?.RightPar ?? Segment.Empty, td, PatternParaHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } TryHighlight(ref LeftHighlightedBracket, highlights?.LeftBracket ?? Segment.Empty, td, PatternCharGroupBracketHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } TryHighlight(ref RightHighlightedBracket, highlights?.RightBracket ?? Segment.Empty, td, PatternCharGroupBracketHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } TryHighlight(ref LeftHighlightedCurlyBrace, highlights?.LeftCurlyBrace ?? Segment.Empty, td, PatternRangeCurlyBraceHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } TryHighlight(ref RightHighlightedCurlyBrace, highlights?.RightCurlyBrace ?? Segment.Empty, td, PatternRangeCurlyBraceHighlightStyleInfo); if (cnc.IsCancellationRequested) { return; } }); } }
void RecolouringThreadProc(ICancellable cnc) { IRegexEngine regex_engine; string eol; lock (this) { regex_engine = mRegexEngine; eol = mEol; } if (regex_engine == null) { return; } TextData td = null; Rect clip_rect = Rect.Empty; int top_index = 0; int bottom_index = 0; UITaskHelper.Invoke(rtb, () => { td = null; var start_doc = rtb.Document.ContentStart; var end_doc = rtb.Document.ContentStart; if (!start_doc.HasValidLayout || !end_doc.HasValidLayout) { return; } var td0 = rtb.GetTextData(eol); if (cnc.IsCancellationRequested) { return; } td = td0; clip_rect = new Rect(new Size(rtb.ViewportWidth, rtb.ViewportHeight)); TextPointer top_pointer = rtb.GetPositionFromPoint(new Point(0, 0), snapToText: true).GetLineStartPosition(-1, out int _); if (cnc.IsCancellationRequested) { return; } top_index = td.TextPointers.GetIndex(top_pointer, LogicalDirection.Backward); if (cnc.IsCancellationRequested) { return; } if (top_index < 0) { top_index = 0; } TextPointer bottom_pointer = rtb.GetPositionFromPoint(new Point(0, rtb.ViewportHeight), snapToText: true).GetLineStartPosition(+1, out int lines_skipped); if (cnc.IsCancellationRequested) { return; } if (bottom_pointer == null || lines_skipped == 0) { bottom_index = td.Text.Length; } else { bottom_index = td.TextPointers.GetIndex(bottom_pointer, LogicalDirection.Forward); if (cnc.IsCancellationRequested) { return; } } if (bottom_index > td.Text.Length) { bottom_index = td.Text.Length; } if (bottom_index < top_index) { bottom_index = top_index; // (including 'if bottom_index == 0') } }); if (cnc.IsCancellationRequested) { return; } if (td == null) { return; } if (td.Text.Length == 0) { return; } Debug.Assert(top_index >= 0); Debug.Assert(bottom_index >= top_index); //Debug.Assert( bottom_index < td.OldPointers.Count ); Debug.Assert(bottom_index <= td.Text.Length); var visible_segment = new Segment(top_index, bottom_index - top_index + 1); var segments_to_colourise = new ColouredSegments( ); regex_engine.ColourisePattern(cnc, segments_to_colourise, td.Text, visible_segment); if (cnc.IsCancellationRequested) { return; } int center_index = (top_index + bottom_index) / 2; var arranged_escapes = segments_to_colourise.Escapes .OrderBy(s => Math.Abs(center_index - (s.Index + s.Length / 2))) .ToList( ); RtbUtilities.ApplyStyle(cnc, ChangeEventHelper, null, td, segments_to_colourise.Comments, PatternCommentStyleInfo); RtbUtilities.ApplyStyle(cnc, ChangeEventHelper, null, td, arranged_escapes, PatternEscapeStyleInfo); RtbUtilities.ApplyStyle(cnc, ChangeEventHelper, null, td, segments_to_colourise.GroupNames, PatternGroupNameStyleInfo); var uncovered_segments = new List <Segment> { new Segment(0, td.Text.Length) }; foreach (var s in segments_to_colourise.All.SelectMany(s => s)) { if (cnc.IsCancellationRequested) { return; } Segment.Except(uncovered_segments, s); } Segment.Except(uncovered_segments, LeftHighlightedParenthesis); Segment.Except(uncovered_segments, RightHighlightedParenthesis); Segment.Except(uncovered_segments, LeftHighlightedBracket); Segment.Except(uncovered_segments, RightHighlightedBracket); Segment.Except(uncovered_segments, LeftHighlightedCurlyBrace); Segment.Except(uncovered_segments, RightHighlightedCurlyBrace); var segments_to_uncolour = uncovered_segments .Select(s => Segment.Intersection(s, visible_segment)) .Where(s => !s.IsEmpty) .OrderBy(s => Math.Abs(center_index - (s.Index + s.Length / 2))) .ToList( ); if (cnc.IsCancellationRequested) { return; } RtbUtilities.ApplyStyle(cnc, ChangeEventHelper, null, td, segments_to_uncolour, PatternNormalStyleInfo); }