예제 #1
0
        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
            }
        }
예제 #2
0
        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;
            });
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        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);
                }
            });
        }
예제 #7
0
        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;
                    });
                }
            }
        }
예제 #8
0
        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;
                    }
                });
            }
        }
예제 #9
0
        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);
        }